Skip to content

Commit 14adbd4

Browse files
authored
bpo-36650: Fix handling of empty keyword args in C version of lru_cache. (GH-12881)
1 parent 9d062d6 commit 14adbd4

File tree

3 files changed

+21
-4
lines changed

3 files changed

+21
-4
lines changed

Lib/test/test_functools.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,20 @@ def f(x):
12711271
self.assertEqual(f(20), '.20.')
12721272
self.assertEqual(f.cache_info().currsize, 10)
12731273

1274+
def test_lru_bug_36650(self):
1275+
# C version of lru_cache was treating a call with an empty **kwargs
1276+
# dictionary as being distinct from a call with no keywords at all.
1277+
# This did not result in an incorrect answer, but it did trigger
1278+
# an unexpected cache miss.
1279+
1280+
@self.module.lru_cache()
1281+
def f(x):
1282+
pass
1283+
1284+
f(0)
1285+
f(0, **{})
1286+
self.assertEqual(f.cache_info().hits, 1)
1287+
12741288
def test_lru_hash_only_once(self):
12751289
# To protect against weird reentrancy bugs and to improve
12761290
# efficiency when faced with slow __hash__ methods, the
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The C version of functools.lru_cache() was treating calls with an empty
2+
``**kwargs`` dictionary as being distinct from calls with no keywords at all.
3+
This did not result in an incorrect answer, but it did trigger an unexpected
4+
cache miss.

Modules/_functoolsmodule.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,10 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
750750
PyObject *key, *keyword, *value;
751751
Py_ssize_t key_size, pos, key_pos, kwds_size;
752752

753+
kwds_size = kwds ? PyDict_GET_SIZE(kwds) : 0;
754+
753755
/* short path, key will match args anyway, which is a tuple */
754-
if (!typed && !kwds) {
756+
if (!typed && !kwds_size) {
755757
if (PyTuple_GET_SIZE(args) == 1) {
756758
key = PyTuple_GET_ITEM(args, 0);
757759
if (PyUnicode_CheckExact(key) || PyLong_CheckExact(key)) {
@@ -765,9 +767,6 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
765767
return args;
766768
}
767769

768-
kwds_size = kwds ? PyDict_GET_SIZE(kwds) : 0;
769-
assert(kwds_size >= 0);
770-
771770
key_size = PyTuple_GET_SIZE(args);
772771
if (kwds_size)
773772
key_size += kwds_size * 2 + 1;

0 commit comments

Comments
 (0)