Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
d8080c01
Commit
d8080c01
authored
Jan 26, 2019
by
Raymond Hettinger
Committed by
GitHub
Jan 26, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-35780: Fix errors in lru_cache() C code (GH-11623)
parent
adad9e68
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
234 additions
and
89 deletions
+234
-89
Lib/functools.py
Lib/functools.py
+7
-4
Lib/test/test_functools.py
Lib/test/test_functools.py
+28
-1
Misc/NEWS.d/next/Library/2019-01-19-17-01-43.bpo-35780.CLf7fT.rst
...S.d/next/Library/2019-01-19-17-01-43.bpo-35780.CLf7fT.rst
+11
-0
Modules/_functoolsmodule.c
Modules/_functoolsmodule.c
+188
-84
No files found.
Lib/functools.py
View file @
d8080c01
...
...
@@ -454,7 +454,7 @@ class _HashedSeq(list):
def
_make_key
(
args
,
kwds
,
typed
,
kwd_mark
=
(
object
(),),
fasttypes
=
{
int
,
str
,
frozenset
,
type
(
None
)
},
fasttypes
=
{
int
,
str
},
tuple
=
tuple
,
type
=
type
,
len
=
len
):
"""Make a cache key from optionally typed positional and keyword arguments
...
...
@@ -510,8 +510,11 @@ def lru_cache(maxsize=128, typed=False):
# Early detection of an erroneous call to @lru_cache without any arguments
# resulting in the inner function being passed to maxsize instead of an
# integer or None.
if
maxsize
is
not
None
and
not
isinstance
(
maxsize
,
int
):
# integer or None. Negative maxsize is treated as 0.
if
isinstance
(
maxsize
,
int
):
if
maxsize
<
0
:
maxsize
=
0
elif
maxsize
is
not
None
:
raise
TypeError
(
'Expected maxsize to be an integer or None'
)
def
decorating_function
(
user_function
):
...
...
@@ -578,6 +581,7 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
link
[
NEXT
]
=
root
hits
+=
1
return
result
misses
+=
1
result
=
user_function
(
*
args
,
**
kwds
)
with
lock
:
if
key
in
cache
:
...
...
@@ -615,7 +619,6 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
# Use the cache_len bound method instead of the len() function
# which could potentially be wrapped in an lru_cache itself.
full
=
(
cache_len
()
>=
maxsize
)
misses
+=
1
return
result
def
cache_info
():
...
...
Lib/test/test_functools.py
View file @
d8080c01
...
...
@@ -1233,6 +1233,33 @@ class TestLRU:
self
.
assertEqual
(
misses
,
4
)
self
.
assertEqual
(
currsize
,
2
)
def
test_lru_bug_35780
(
self
):
# C version of the lru_cache was not checking to see if
# the user function call has already modified the cache
# (this arises in recursive calls and in multi-threading).
# This cause the cache to have orphan links not referenced
# by the cache dictionary.
once
=
True
# Modified by f(x) below
@
self
.
module
.
lru_cache
(
maxsize
=
10
)
def
f
(
x
):
nonlocal
once
rv
=
f'.
{
x
}
.'
if
x
==
20
and
once
:
once
=
False
rv
=
f
(
x
)
return
rv
# Fill the cache
for
x
in
range
(
15
):
self
.
assertEqual
(
f
(
x
),
f'.
{
x
}
.'
)
self
.
assertEqual
(
f
.
cache_info
().
currsize
,
10
)
# Make a recursive call and make sure the cache remains full
self
.
assertEqual
(
f
(
20
),
'.20.'
)
self
.
assertEqual
(
f
.
cache_info
().
currsize
,
10
)
def
test_lru_hash_only_once
(
self
):
# To protect against weird reentrancy bugs and to improve
# efficiency when faced with slow __hash__ methods, the
...
...
@@ -1329,7 +1356,7 @@ class TestLRU:
for
i
in
(
0
,
1
):
self
.
assertEqual
([
eq
(
n
)
for
n
in
range
(
150
)],
list
(
range
(
150
)))
self
.
assertEqual
(
eq
.
cache_info
(),
self
.
module
.
_CacheInfo
(
hits
=
0
,
misses
=
300
,
maxsize
=
-
10
,
currsize
=
1
))
self
.
module
.
_CacheInfo
(
hits
=
0
,
misses
=
300
,
maxsize
=
0
,
currsize
=
0
))
def
test_lru_with_exceptions
(
self
):
# Verify that user_function exceptions get passed through without
...
...
Misc/NEWS.d/next/Library/2019-01-19-17-01-43.bpo-35780.CLf7fT.rst
0 → 100644
View file @
d8080c01
Fix lru_cache() errors arising in recursive, reentrant, or
multi-threaded code. These errors could result in orphan links and in
the cache being trapped in a state with fewer than the specified maximum
number of links. Fix handling of negative maxsize which should have
been treated as zero. Fix errors in toggling the "full" status flag.
Fix misordering of links when errors are encountered. Sync-up the C
code and pure Python code for the space saving path in functions with a
single positional argument. In this common case, the space overhead of
an lru cache entry is reduced by almost half. Fix counting of cache
misses. In error cases, the miss count was out of sync with the actual
number of times the underlying user function was called.
Modules/_functoolsmodule.c
View file @
d8080c01
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment