Issue #29203: functools.lru_cache() now respects PEP 468
This commit is contained in:
parent
04316c4cc8
commit
4ee39141e8
|
@ -421,7 +421,7 @@ class _HashedSeq(list):
|
||||||
def _make_key(args, kwds, typed,
|
def _make_key(args, kwds, typed,
|
||||||
kwd_mark = (object(),),
|
kwd_mark = (object(),),
|
||||||
fasttypes = {int, str, frozenset, type(None)},
|
fasttypes = {int, str, frozenset, type(None)},
|
||||||
sorted=sorted, tuple=tuple, type=type, len=len):
|
tuple=tuple, type=type, len=len):
|
||||||
"""Make a cache key from optionally typed positional and keyword arguments
|
"""Make a cache key from optionally typed positional and keyword arguments
|
||||||
|
|
||||||
The key is constructed in a way that is flat as possible rather than
|
The key is constructed in a way that is flat as possible rather than
|
||||||
|
@ -434,14 +434,13 @@ def _make_key(args, kwds, typed,
|
||||||
"""
|
"""
|
||||||
key = args
|
key = args
|
||||||
if kwds:
|
if kwds:
|
||||||
sorted_items = sorted(kwds.items())
|
|
||||||
key += kwd_mark
|
key += kwd_mark
|
||||||
for item in sorted_items:
|
for item in kwds.items():
|
||||||
key += item
|
key += item
|
||||||
if typed:
|
if typed:
|
||||||
key += tuple(type(v) for v in args)
|
key += tuple(type(v) for v in args)
|
||||||
if kwds:
|
if kwds:
|
||||||
key += tuple(type(v) for k, v in sorted_items)
|
key += tuple(type(v) for v in kwds.values())
|
||||||
elif len(key) == 1 and type(key[0]) in fasttypes:
|
elif len(key) == 1 and type(key[0]) in fasttypes:
|
||||||
return key[0]
|
return key[0]
|
||||||
return _HashedSeq(key)
|
return _HashedSeq(key)
|
||||||
|
|
|
@ -1306,6 +1306,16 @@ class TestLRU:
|
||||||
self.assertEqual(fib.cache_info(),
|
self.assertEqual(fib.cache_info(),
|
||||||
self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
|
self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
|
||||||
|
|
||||||
|
def test_kwargs_order(self):
|
||||||
|
# PEP 468: Preserving Keyword Argument Order
|
||||||
|
@self.module.lru_cache(maxsize=10)
|
||||||
|
def f(**kwargs):
|
||||||
|
return list(kwargs.items())
|
||||||
|
self.assertEqual(f(a=1, b=2), [('a', 1), ('b', 2)])
|
||||||
|
self.assertEqual(f(b=2, a=1), [('b', 2), ('a', 1)])
|
||||||
|
self.assertEqual(f.cache_info(),
|
||||||
|
self.module._CacheInfo(hits=0, misses=2, maxsize=10, currsize=2))
|
||||||
|
|
||||||
def test_lru_cache_decoration(self):
|
def test_lru_cache_decoration(self):
|
||||||
def f(zomg: 'zomg_annotation'):
|
def f(zomg: 'zomg_annotation'):
|
||||||
"""f doc string"""
|
"""f doc string"""
|
||||||
|
|
|
@ -47,6 +47,10 @@ Library
|
||||||
- Issue #28961: Fix unittest.mock._Call helper: don't ignore the name parameter
|
- Issue #28961: Fix unittest.mock._Call helper: don't ignore the name parameter
|
||||||
anymore. Patch written by Jiajun Huang.
|
anymore. Patch written by Jiajun Huang.
|
||||||
|
|
||||||
|
- Issue #29203: functools.lru_cache() now respects PEP 468 and preserves
|
||||||
|
the order of keyword arguments. f(a=1, b=2) is now cached separately
|
||||||
|
from f(b=2, a=1) since both calls could potentially give different results.
|
||||||
|
|
||||||
- Issue #15812: inspect.getframeinfo() now correctly shows the first line of
|
- Issue #15812: inspect.getframeinfo() now correctly shows the first line of
|
||||||
a context. Patch by Sam Breese.
|
a context. Patch by Sam Breese.
|
||||||
|
|
||||||
|
|
|
@ -704,8 +704,8 @@ static PyTypeObject lru_cache_type;
|
||||||
static PyObject *
|
static PyObject *
|
||||||
lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
||||||
{
|
{
|
||||||
PyObject *key, *sorted_items;
|
PyObject *key, *keyword, *value;
|
||||||
Py_ssize_t key_size, pos, key_pos;
|
Py_ssize_t key_size, pos, key_pos, kwds_size;
|
||||||
|
|
||||||
/* 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) {
|
||||||
|
@ -713,28 +713,18 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kwds && PyDict_Size(kwds) > 0) {
|
kwds_size = kwds ? PyDict_Size(kwds) : 0;
|
||||||
sorted_items = PyDict_Items(kwds);
|
assert(kwds_size >= 0);
|
||||||
if (!sorted_items)
|
|
||||||
return NULL;
|
|
||||||
if (PyList_Sort(sorted_items) < 0) {
|
|
||||||
Py_DECREF(sorted_items);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
sorted_items = NULL;
|
|
||||||
|
|
||||||
key_size = PyTuple_GET_SIZE(args);
|
key_size = PyTuple_GET_SIZE(args);
|
||||||
if (sorted_items)
|
if (kwds_size)
|
||||||
key_size += PyList_GET_SIZE(sorted_items);
|
key_size += kwds_size * 2 + 1;
|
||||||
if (typed)
|
if (typed)
|
||||||
key_size *= 2;
|
key_size += PyTuple_GET_SIZE(args) + kwds_size;
|
||||||
if (sorted_items)
|
|
||||||
key_size++;
|
|
||||||
|
|
||||||
key = PyTuple_New(key_size);
|
key = PyTuple_New(key_size);
|
||||||
if (key == NULL)
|
if (key == NULL)
|
||||||
goto done;
|
return NULL;
|
||||||
|
|
||||||
key_pos = 0;
|
key_pos = 0;
|
||||||
for (pos = 0; pos < PyTuple_GET_SIZE(args); ++pos) {
|
for (pos = 0; pos < PyTuple_GET_SIZE(args); ++pos) {
|
||||||
|
@ -742,14 +732,16 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
||||||
Py_INCREF(item);
|
Py_INCREF(item);
|
||||||
PyTuple_SET_ITEM(key, key_pos++, item);
|
PyTuple_SET_ITEM(key, key_pos++, item);
|
||||||
}
|
}
|
||||||
if (sorted_items) {
|
if (kwds_size) {
|
||||||
Py_INCREF(kwd_mark);
|
Py_INCREF(kwd_mark);
|
||||||
PyTuple_SET_ITEM(key, key_pos++, kwd_mark);
|
PyTuple_SET_ITEM(key, key_pos++, kwd_mark);
|
||||||
for (pos = 0; pos < PyList_GET_SIZE(sorted_items); ++pos) {
|
for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) {
|
||||||
PyObject *item = PyList_GET_ITEM(sorted_items, pos);
|
Py_INCREF(keyword);
|
||||||
Py_INCREF(item);
|
PyTuple_SET_ITEM(key, key_pos++, keyword);
|
||||||
PyTuple_SET_ITEM(key, key_pos++, item);
|
Py_INCREF(value);
|
||||||
|
PyTuple_SET_ITEM(key, key_pos++, value);
|
||||||
}
|
}
|
||||||
|
assert(key_pos == PyTuple_GET_SIZE(args) + kwds_size * 2 + 1);
|
||||||
}
|
}
|
||||||
if (typed) {
|
if (typed) {
|
||||||
for (pos = 0; pos < PyTuple_GET_SIZE(args); ++pos) {
|
for (pos = 0; pos < PyTuple_GET_SIZE(args); ++pos) {
|
||||||
|
@ -757,20 +749,15 @@ lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
|
||||||
Py_INCREF(item);
|
Py_INCREF(item);
|
||||||
PyTuple_SET_ITEM(key, key_pos++, item);
|
PyTuple_SET_ITEM(key, key_pos++, item);
|
||||||
}
|
}
|
||||||
if (sorted_items) {
|
if (kwds_size) {
|
||||||
for (pos = 0; pos < PyList_GET_SIZE(sorted_items); ++pos) {
|
for (pos = 0; PyDict_Next(kwds, &pos, &keyword, &value);) {
|
||||||
PyObject *tp_items = PyList_GET_ITEM(sorted_items, pos);
|
PyObject *item = (PyObject *)Py_TYPE(value);
|
||||||
PyObject *item = (PyObject *)Py_TYPE(PyTuple_GET_ITEM(tp_items, 1));
|
|
||||||
Py_INCREF(item);
|
Py_INCREF(item);
|
||||||
PyTuple_SET_ITEM(key, key_pos++, item);
|
PyTuple_SET_ITEM(key, key_pos++, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(key_pos == key_size);
|
assert(key_pos == key_size);
|
||||||
|
|
||||||
done:
|
|
||||||
if (sorted_items)
|
|
||||||
Py_DECREF(sorted_items);
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue