summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2010-08-08 00:56:52 (GMT)
committerRaymond Hettinger <python@rcn.com>2010-08-08 00:56:52 (GMT)
commitc6d80c1bef62c996d1a7d3b544a108fef17fbf65 (patch)
tree69bfd207498ceb86fcec365e074c50c0f752e5fc
parentf56c9cd30d7b7113e32a2562f66cc78a3ec441a3 (diff)
downloadcpython-c6d80c1bef62c996d1a7d3b544a108fef17fbf65.zip
cpython-c6d80c1bef62c996d1a7d3b544a108fef17fbf65.tar.gz
cpython-c6d80c1bef62c996d1a7d3b544a108fef17fbf65.tar.bz2
Issue 8814: functools.wraps() did not copy __annotations__.
-rw-r--r--Doc/library/functools.rst6
-rw-r--r--Lib/functools.py5
-rw-r--r--Lib/test/test_functools.py8
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS4
5 files changed, 17 insertions, 7 deletions
diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst
index 94be636..570f4d2 100644
--- a/Doc/library/functools.rst
+++ b/Doc/library/functools.rst
@@ -66,9 +66,9 @@ The :mod:`functools` module defines the following functions:
attributes of the wrapper function are updated with the corresponding attributes
from the original function. The default values for these arguments are the
module level constants *WRAPPER_ASSIGNMENTS* (which assigns to the wrapper
- function's *__name__*, *__module__* and *__doc__*, the documentation string) and
- *WRAPPER_UPDATES* (which updates the wrapper function's *__dict__*, i.e. the
- instance dictionary).
+ function's *__name__*, *__module__*, *__annotations__* and *__doc__*, the
+ documentation string) and *WRAPPER_UPDATES* (which updates the wrapper
+ function's *__dict__*, i.e. the instance dictionary).
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
diff --git a/Lib/functools.py b/Lib/functools.py
index a54f030..103dd42 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -12,7 +12,7 @@ from _functools import partial, reduce
# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection
-WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
+WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__', '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
@@ -30,7 +30,8 @@ def update_wrapper(wrapper,
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
- setattr(wrapper, attr, getattr(wrapped, attr))
+ if hasattr(wrapped, attr):
+ setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
# Return the wrapper so this can be used as a decorator via partial()
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index eff31e0..7b79f28 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -181,17 +181,19 @@ class TestUpdateWrapper(unittest.TestCase):
self.assertTrue(wrapped_attr[key] is wrapper_attr[key])
def test_default_update(self):
- def f():
+ def f(a:'This is a new annotation'):
"""This is a test"""
pass
f.attr = 'This is also a test'
- def wrapper():
+ def wrapper(b:'This is the prior annotation'):
pass
functools.update_wrapper(wrapper, f)
self.check_wrapper(wrapper, f)
self.assertEqual(wrapper.__name__, 'f')
self.assertEqual(wrapper.__doc__, 'This is a test')
self.assertEqual(wrapper.attr, 'This is also a test')
+ self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
+ self.assertNotIn('b', wrapper.__annotations__)
def test_no_update(self):
def f():
@@ -204,6 +206,7 @@ class TestUpdateWrapper(unittest.TestCase):
self.check_wrapper(wrapper, f, (), ())
self.assertEqual(wrapper.__name__, 'wrapper')
self.assertEqual(wrapper.__doc__, None)
+ self.assertEqual(wrapper.__annotations__, {})
self.assertFalse(hasattr(wrapper, 'attr'))
def test_selective_update(self):
@@ -230,6 +233,7 @@ class TestUpdateWrapper(unittest.TestCase):
functools.update_wrapper(wrapper, max)
self.assertEqual(wrapper.__name__, 'max')
self.assertTrue(wrapper.__doc__.startswith('max('))
+ self.assertEqual(wrapper.__annotations__, {})
class TestWraps(TestUpdateWrapper):
diff --git a/Misc/ACKS b/Misc/ACKS
index 6edb710..67684eb 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -144,6 +144,7 @@ Steve Clift
Nick Coghlan
Josh Cogliati
Dave Cole
+Terrence Cole
Benjamin Collar
Jeffery Collins
Paul Colomiets
diff --git a/Misc/NEWS b/Misc/NEWS
index aceb158..997b883 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,10 @@ What's New in Python 3.1.3?
Core and Builtins
-----------------
+- Issue #8814: function annotations (the ``__annotations__`` attribute)
+ are now included in the set of attributes copied by default by
+ functools.wraps and functools.update_wrapper. Patch by Terrence Cole.
+
- Issue #83755: Implicit set-to-frozenset conversion was not thread-safe.
- Issue #9416: Fix some issues with complex formatting where the