summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2010-08-17 06:17:18 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2010-08-17 06:17:18 (GMT)
commit9887683f7407b2d4ec0e0ff1ff9b9a26137d0724 (patch)
tree07c1120d84c6a4f83ede41bccd2db5d08acb7ee4 /Lib
parent632a0c1476bce66a2226ce9b103d0ef96e739a2c (diff)
downloadcpython-9887683f7407b2d4ec0e0ff1ff9b9a26137d0724.zip
cpython-9887683f7407b2d4ec0e0ff1ff9b9a26137d0724.tar.gz
cpython-9887683f7407b2d4ec0e0ff1ff9b9a26137d0724.tar.bz2
Document and test the resolution of issue 3445 (tolerate missing attributes in functools.update_wrapper, previously implemented as a side effect of the __annotations__ copying patch) and implement issue 9567 (add a __wrapped__ attribute when using update_wrapper)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/functools.py9
-rw-r--r--Lib/test/test_functools.py29
2 files changed, 36 insertions, 2 deletions
diff --git a/Lib/functools.py b/Lib/functools.py
index c2c2282..bd1334b 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -38,9 +38,14 @@ 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:
- if hasattr(wrapped, attr):
- setattr(wrapper, attr, getattr(wrapped, attr))
+ try:
+ value = getattr(wrapped, attr)
+ except AttributeError:
+ pass
+ else:
+ setattr(wrapper, attr, value)
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 2b6da64..211ef18 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -194,6 +194,7 @@ class TestUpdateWrapper(unittest.TestCase):
def test_default_update(self):
wrapper, f = self._default_update()
self.check_wrapper(wrapper, f)
+ self.assertIs(wrapper.__wrapped__, 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')
@@ -236,6 +237,28 @@ class TestUpdateWrapper(unittest.TestCase):
self.assertEqual(wrapper.attr, 'This is a different test')
self.assertEqual(wrapper.dict_attr, f.dict_attr)
+ def test_missing_attributes(self):
+ def f():
+ pass
+ def wrapper():
+ pass
+ wrapper.dict_attr = {}
+ assign = ('attr',)
+ update = ('dict_attr',)
+ # Missing attributes on wrapped object are ignored
+ functools.update_wrapper(wrapper, f, assign, update)
+ self.assertNotIn('attr', wrapper.__dict__)
+ self.assertEqual(wrapper.dict_attr, {})
+ # Wrapper must have expected attributes for updating
+ del wrapper.dict_attr
+ with self.assertRaises(AttributeError):
+ functools.update_wrapper(wrapper, f, assign, update)
+ wrapper.dict_attr = 1
+ with self.assertRaises(AttributeError):
+ functools.update_wrapper(wrapper, f, assign, update)
+
+ @unittest.skipIf(sys.flags.optimize >= 2,
+ "Docstrings are omitted with -O2 and above")
def test_builtin_update(self):
# Test for bug #1576241
def wrapper():
@@ -495,6 +518,12 @@ class TestLRU(unittest.TestCase):
self.assertEqual(f.hits, 0)
self.assertEqual(f.misses, 1)
+ # Test bypassing the cache
+ self.assertIs(f.__wrapped__, orig)
+ f.__wrapped__(x, y)
+ self.assertEqual(f.hits, 0)
+ self.assertEqual(f.misses, 1)
+
# test size zero (which means "never-cache")
@functools.lru_cache(0)
def f():