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 Wed Mar 30 21:15:51 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) @@ -157,6 +161,10 @@ .. versionadded:: 3.2 + .. note:: + If you want the decorated function to be reusable, the context manager + itself should be reusable, too. + .. seealso:: diff -r 4fde67eddfae Lib/contextlib.py --- a/Lib/contextlib.py Mon Mar 28 17:08:36 2011 +0800 +++ b/Lib/contextlib.py Wed Mar 30 21:15:51 2011 +0800 @@ -9,10 +9,18 @@ class ContextDecorator(object): "A base class or mixin that enables context managers to work as decorators." + + def _recreate(self): + """ + Return a recreated instance of self used in the decorated function, in + order to make the decorated function reusable. + """ + return self + def __call__(self, func): @wraps(func) def inner(*args, **kwds): - with self: + with self._recreate(): return func(*args, **kwds) return inner @@ -20,8 +28,12 @@ class _GeneratorContextManager(ContextDecorator): """Helper for @contextmanager decorator.""" - def __init__(self, gen): - self.gen = gen + def __init__(self, func, *args, **kwds): + self.gen = func(*args, **kwds) + self.func, self.args, self.kwds = func, args, kwds + + def _recreate(self): + return self.__class__(self.func, *self.args, **self.kwds) def __enter__(self): try: @@ -92,7 +104,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 Wed Mar 30 21:15:51 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 Wed Mar 30 21:15:51 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