Index: Doc/library/functools.rst =================================================================== --- Doc/library/functools.rst (revision 82038) +++ Doc/library/functools.rst (working copy) @@ -111,9 +111,9 @@ 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 Index: Lib/test/test_functools.py =================================================================== --- Lib/test/test_functools.py (revision 82038) +++ Lib/test/test_functools.py (working copy) @@ -181,11 +181,11 @@ 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 @@ -195,6 +195,8 @@ 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") @@ -213,6 +215,7 @@ 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): @@ -239,6 +242,7 @@ functools.update_wrapper(wrapper, max) self.assertEqual(wrapper.__name__, 'max') self.assertTrue(wrapper.__doc__.startswith('max(')) + self.assertEqual(wrapper.__annotations__, {}) class TestWraps(TestUpdateWrapper): Index: Lib/functools.py =================================================================== --- Lib/functools.py (revision 82038) +++ Lib/functools.py (working copy) @@ -12,7 +12,7 @@ # 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 @@ 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()