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
24c05bc1
Commit
24c05bc1
authored
Jul 15, 2013
by
Nick Coghlan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Close issue 17482: don't overwrite __wrapped__
parent
6180a2f4
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
34 additions
and
6 deletions
+34
-6
Doc/library/functools.rst
Doc/library/functools.rst
+7
-2
Doc/whatsnew/3.4.rst
Doc/whatsnew/3.4.rst
+9
-0
Lib/functools.py
Lib/functools.py
+3
-1
Lib/test/test_functools.py
Lib/test/test_functools.py
+11
-3
Misc/NEWS
Misc/NEWS
+4
-0
No files found.
Doc/library/functools.rst
View file @
24c05bc1
...
...
@@ -306,8 +306,8 @@ The :mod:`functools` module defines the following functions:
To allow access to the original function for introspection and other purposes
(e.g. bypassing a caching decorator such as :func:`lru_cache`), this function
automatically adds a
__wrapped__
attribute to the wrapper that refers to
the
original function
.
automatically adds a
``__wrapped__``
attribute to the wrapper that refers to
the
function being wrapped
.
The main intended use for this function is in :term:`decorator` functions which
wrap the decorated function and return the wrapper. If the wrapper function is
...
...
@@ -330,6 +330,11 @@ The :mod:`functools` module defines the following functions:
.. versionchanged:: 3.2
Missing attributes no longer trigger an :exc:`AttributeError`.
.. versionchanged:: 3.4
The ``__wrapped__`` attribute now always refers to the wrapped
function, even if that function defined a ``__wrapped__`` attribute.
(see :issue:`17482`)
.. decorator:: wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
...
...
Doc/whatsnew/3.4.rst
View file @
24c05bc1
...
...
@@ -315,3 +315,12 @@ that may require changes to your code.
found but improperly structured. If you were catching ImportError before and
wish to continue to ignore syntax or decoding issues, catch all three
exceptions now.
* :func:`functools.update_wrapper` and :func:`functools.wraps` now correctly
set the ``__wrapped__`` attribute even if the wrapped function had a
wrapped attribute set. This means ``__wrapped__`` attributes now correctly
link a stack of decorated functions rather than every ``__wrapped__``
attribute in the chain referring to the innermost function. Introspection
libraries that assumed the previous behaviour was intentional will need to
be updated to walk the chain of ``__wrapped__`` attributes to find the
innermost function.
Lib/functools.py
View file @
24c05bc1
...
...
@@ -55,7 +55,6 @@ def update_wrapper(wrapper,
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
wrapper
.
__wrapped__
=
wrapped
for
attr
in
assigned
:
try
:
value
=
getattr
(
wrapped
,
attr
)
...
...
@@ -65,6 +64,9 @@ def update_wrapper(wrapper,
setattr
(
wrapper
,
attr
,
value
)
for
attr
in
updated
:
getattr
(
wrapper
,
attr
).
update
(
getattr
(
wrapped
,
attr
,
{}))
# Issue #17482: set __wrapped__ last so we don't inadvertently copy it
# from the wrapped function when updating __dict__
wrapper
.
__wrapped__
=
wrapped
# Return the wrapper so this can be used as a decorator via partial()
return
wrapper
...
...
Lib/test/test_functools.py
View file @
24c05bc1
...
...
@@ -224,19 +224,26 @@ class TestUpdateWrapper(unittest.TestCase):
updated
=
functools
.
WRAPPER_UPDATES
):
# Check attributes were assigned
for
name
in
assigned
:
self
.
assert
True
(
getattr
(
wrapper
,
name
)
is
getattr
(
wrapped
,
name
))
self
.
assert
Is
(
getattr
(
wrapper
,
name
),
getattr
(
wrapped
,
name
))
# Check attributes were updated
for
name
in
updated
:
wrapper_attr
=
getattr
(
wrapper
,
name
)
wrapped_attr
=
getattr
(
wrapped
,
name
)
for
key
in
wrapped_attr
:
self
.
assertTrue
(
wrapped_attr
[
key
]
is
wrapper_attr
[
key
])
if
name
==
"__dict__"
and
key
==
"__wrapped__"
:
# __wrapped__ is overwritten by the update code
continue
self
.
assertIs
(
wrapped_attr
[
key
],
wrapper_attr
[
key
])
# Check __wrapped__
self
.
assertIs
(
wrapper
.
__wrapped__
,
wrapped
)
def
_default_update
(
self
):
def
f
(
a
:
'This is a new annotation'
):
"""This is a test"""
pass
f
.
attr
=
'This is also a test'
f
.
__wrapped__
=
"This is a bald faced lie"
def
wrapper
(
b
:
'This is the prior annotation'
):
pass
functools
.
update_wrapper
(
wrapper
,
f
)
...
...
@@ -331,14 +338,15 @@ class TestWraps(TestUpdateWrapper):
"""This is a test"""
pass
f
.
attr
=
'This is also a test'
f
.
__wrapped__
=
"This is still a bald faced lie"
@
functools
.
wraps
(
f
)
def
wrapper
():
pass
self
.
check_wrapper
(
wrapper
,
f
)
return
wrapper
,
f
def
test_default_update
(
self
):
wrapper
,
f
=
self
.
_default_update
()
self
.
check_wrapper
(
wrapper
,
f
)
self
.
assertEqual
(
wrapper
.
__name__
,
'f'
)
self
.
assertEqual
(
wrapper
.
__qualname__
,
f
.
__qualname__
)
self
.
assertEqual
(
wrapper
.
attr
,
'This is also a test'
)
...
...
Misc/NEWS
View file @
24c05bc1
...
...
@@ -154,6 +154,10 @@ Core and Builtins
Library
-------
-
Issue
#
17482
:
functools
.
update_wrapper
(
and
functools
.
wraps
)
now
set
the
__wrapped__
attribute
correctly
even
if
the
underlying
function
has
a
__wrapped__
attribute
set
.
-
Issue
#
18431
:
The
new
email
header
parser
now
decodes
RFC2047
encoded
words
in
structured
headers
.
...
...
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