mirror of
https://github.com/python/cpython.git
synced 2025-11-11 14:44:57 +00:00
bpo-36650: Fix handling of empty keyword args in C version of lru_cache. (GH-12881)
This commit is contained in:
parent
9d062d690b
commit
14adbd4598
3 changed files with 21 additions and 4 deletions
|
|
@ -1271,6 +1271,20 @@ class TestLRU:
|
||||||
self.assertEqual(f(20), '.20.')
|
self.assertEqual(f(20), '.20.')
|
||||||
self.assertEqual(f.cache_info().currsize, 10)
|
self.assertEqual(f.cache_info().currsize, 10)
|
||||||
|
|
||||||
|
def test_lru_bug_36650(self):
|
||||||
|
# C version of lru_cache was treating a call with an empty **kwargs
|
||||||
|
# dictionary as being distinct from a call with no keywords at all.
|
||||||
|
# This did not result in an incorrect answer, but it did trigger
|
||||||
|
# an unexpected cache miss.
|
||||||
|
|
||||||
|
@self.module.lru_cache()
|
||||||
|
def f(x):
|
||||||
|
pass
|
||||||
|
|
||||||
|
f(0)
|
||||||
|
f(0, **{})
|
||||||
|
self.assertEqual(f.cache_info().hits, 1)
|
||||||
|
|
||||||
def test_lru_hash_only_once(self):
|
def test_lru_hash_only_once(self):
|
||||||
# To protect against weird reentrancy bugs and to improve
|
# To protect against weird reentrancy bugs and to improve
|
||||||
# efficiency when faced with slow __hash__ methods, the
|
# efficiency when faced with slow __hash__ methods, the
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
The C version of functools.lru_cache() was treating calls with an empty
|
||||||
|
``**kwargs`` dictionary as being distinct from calls with no keywords at all.
|
||||||
|
This did not result in an incorrect answer, but it did trigger an unexpected
|
||||||
|
cache miss.
|
||||||
|
|
@ -750,8 +750,10 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
||||||
PyObject *key, *keyword, *value;
|
PyObject *key, *keyword, *value;
|
||||||
Py_ssize_t key_size, pos, key_pos, kwds_size;
|
Py_ssize_t key_size, pos, key_pos, kwds_size;
|
||||||
|
|
||||||
|
kwds_size = kwds ? PyDict_GET_SIZE(kwds) : 0;
|
||||||
|
|
||||||
/* short path, key will match args anyway, which is a tuple */
|
/* short path, key will match args anyway, which is a tuple */
|
||||||
if (!typed && !kwds) {
|
if (!typed && !kwds_size) {
|
||||||
if (PyTuple_GET_SIZE(args) == 1) {
|
if (PyTuple_GET_SIZE(args) == 1) {
|
||||||
key = PyTuple_GET_ITEM(args, 0);
|
key = PyTuple_GET_ITEM(args, 0);
|
||||||
if (PyUnicode_CheckExact(key) || PyLong_CheckExact(key)) {
|
if (PyUnicode_CheckExact(key) || PyLong_CheckExact(key)) {
|
||||||
|
|
@ -765,9 +767,6 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
kwds_size = kwds ? PyDict_GET_SIZE(kwds) : 0;
|
|
||||||
assert(kwds_size >= 0);
|
|
||||||
|
|
||||||
key_size = PyTuple_GET_SIZE(args);
|
key_size = PyTuple_GET_SIZE(args);
|
||||||
if (kwds_size)
|
if (kwds_size)
|
||||||
key_size += kwds_size * 2 + 1;
|
key_size += kwds_size * 2 + 1;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue