diff -r 4fde67eddfae Doc/library/contextlib.rst --- a/Doc/library/contextlib.rst Mon Mar 28 17:08:36 2011 +0800 +++ b/Doc/library/contextlib.rst Tue Mar 29 16:30:57 2011 +0800 @@ -60,6 +60,10 @@ .. versionchanged:: 3.2 Use of :class:`ContextDecorator`. + .. versionchanged:: 3.3 + The function decorated by context manager which is created by contextmanager + is reusable. + .. function:: closing(thing) diff -r 4fde67eddfae Lib/contextlib.py --- a/Lib/contextlib.py Mon Mar 28 17:08:36 2011 +0800 +++ b/Lib/contextlib.py Tue Mar 29 16:30:57 2011 +0800 @@ -3,6 +3,7 @@ import sys from functools import wraps from warnings import warn +import copy __all__ = ["contextmanager", "closing", "ContextDecorator"] @@ -20,10 +21,13 @@ class _GeneratorContextManager(ContextDecorator): """Helper for @contextmanager decorator.""" - def __init__(self, gen): - self.gen = gen + def __init__(self, func, *args, **kwds): + self.gen = None + self.func, self.args, self.kwds = func, args, kwds def __enter__(self): + if self.gen is None: + self.gen = self.func(*self.args, **self.kwds) try: return next(self.gen) except StopIteration: @@ -61,6 +65,13 @@ if sys.exc_info()[1] is not value: raise + def __call__(self, func): + @wraps(func) + def inner(*args, **kwds): + with copy.copy(self): + return func(*args, **kwds) + return inner + def contextmanager(func): """@contextmanager decorator. @@ -92,7 +103,7 @@ """ @wraps(func) def helper(*args, **kwds): - return _GeneratorContextManager(func(*args, **kwds)) + return _GeneratorContextManager(func, *args, **kwds) return helper diff -r 4fde67eddfae Lib/test/test_contextlib.py --- a/Lib/test/test_contextlib.py Mon Mar 28 17:08:36 2011 +0800 +++ b/Lib/test/test_contextlib.py Tue Mar 29 16:30:57 2011 +0800 @@ -357,13 +357,20 @@ yield state.append(999) + asserted_state = [1] @woohoo(1) def test(x): - self.assertEqual(state, [1]) + self.assertEqual(state, asserted_state) state.append(x) test('something') self.assertEqual(state, [1, 'something', 999]) + # Test if the decorated function is 'reusable' + asserted_state = [1, 'something', 999, 1] + test('something else') + self.assertEqual(state, [1, 'something', 999, 1, 'something else', + 999]) + # This is needed to make the test actually run under regrtest.py! def test_main(): diff -r 4fde67eddfae Lib/test/test_with.py --- a/Lib/test/test_with.py Mon Mar 28 17:08:36 2011 +0800 +++ b/Lib/test/test_with.py Tue Mar 29 16:30:57 2011 +0800 @@ -14,8 +14,8 @@ class MockContextManager(_GeneratorContextManager): - def __init__(self, gen): - _GeneratorContextManager.__init__(self, gen) + def __init__(self, func, *args, **kwds): + super().__init__(func, *args, **kwds) self.enter_called = False self.exit_called = False self.exit_args = None @@ -33,7 +33,7 @@ def mock_contextmanager(func): def helper(*args, **kwds): - return MockContextManager(func(*args, **kwds)) + return MockContextManager(func, *args, **kwds) return helper