summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2010-08-04 18:28:02 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2010-08-04 18:28:02 (GMT)
commit560f7647ce5991140e215a7ac775130798c92dc8 (patch)
tree7b19296ddf3d8a0d111766226cff9367f77bd619 /Lib
parent5626eec0c2f62999967eff0ab6aa8ad329e8571b (diff)
downloadcpython-560f7647ce5991140e215a7ac775130798c92dc8.zip
cpython-560f7647ce5991140e215a7ac775130798c92dc8.tar.gz
cpython-560f7647ce5991140e215a7ac775130798c92dc8.tar.bz2
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.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/functools.py5
-rw-r--r--Lib/test/test_functools.py8
2 files changed, 9 insertions, 4 deletions
diff --git a/Lib/functools.py b/Lib/functools.py
index 863706d..e244070 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -19,7 +19,7 @@ from operator import itemgetter
# 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,
@@ -37,7 +37,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 a02d37c..70a8ad6 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -182,11 +182,11 @@ class TestUpdateWrapper(unittest.TestCase):
self.assertTrue(wrapped_attr[key] is wrapper_attr[key])
def _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)
return wrapper, f
@@ -196,6 +196,8 @@ class TestUpdateWrapper(unittest.TestCase):
self.check_wrapper(wrapper, f)
self.assertEqual(wrapper.__name__, 'f')
self.assertEqual(wrapper.attr, 'This is also a test')
+ self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation')
+ self.assertNotIn('b', wrapper.__annotations__)
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
@@ -214,6 +216,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):
@@ -240,6 +243,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):