mirror of
https://github.com/jazzband/contextlib2.git
synced 2026-05-16 11:03:16 +00:00
Issue #5: support old-style classes in ExitStack
This commit is contained in:
parent
c8efb9adcc
commit
911b459522
4 changed files with 52 additions and 7 deletions
4
NEWS.rst
4
NEWS.rst
|
|
@ -4,7 +4,9 @@ Release History
|
||||||
0.5.3 (not yet released)
|
0.5.3 (not yet released)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
* TBD
|
* ``ExitStack`` now correctly handles context managers implemented as old-style
|
||||||
|
classes in Python 2.x (such as ``codecs.StreamReader`` and
|
||||||
|
``codecs.StreamWriter``)
|
||||||
|
|
||||||
0.5.2 (2016-05-02)
|
0.5.2 (2016-05-02)
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,20 @@ else:
|
||||||
exc_type, exc_value, exc_tb = exc_details
|
exc_type, exc_value, exc_tb = exc_details
|
||||||
exec ("raise exc_type, exc_value, exc_tb")
|
exec ("raise exc_type, exc_value, exc_tb")
|
||||||
|
|
||||||
|
# Handle old-style classes if they exist
|
||||||
|
try:
|
||||||
|
from types import InstanceType
|
||||||
|
except ImportError:
|
||||||
|
# Python 3 doesn't have old-style classes
|
||||||
|
_get_type = type
|
||||||
|
else:
|
||||||
|
# Need to handle old-style context managers on Python 2
|
||||||
|
def _get_type(obj):
|
||||||
|
obj_type = type(obj)
|
||||||
|
if obj_type is InstanceType:
|
||||||
|
return obj.__class__ # Old-style class
|
||||||
|
return obj_type # New-style class
|
||||||
|
|
||||||
# Inspired by discussions on http://bugs.python.org/issue13585
|
# Inspired by discussions on http://bugs.python.org/issue13585
|
||||||
class ExitStack(object):
|
class ExitStack(object):
|
||||||
"""Context manager for dynamic management of a stack of exit callbacks
|
"""Context manager for dynamic management of a stack of exit callbacks
|
||||||
|
|
@ -328,7 +342,7 @@ class ExitStack(object):
|
||||||
"""
|
"""
|
||||||
# We use an unbound method rather than a bound method to follow
|
# We use an unbound method rather than a bound method to follow
|
||||||
# the standard lookup behaviour for special methods
|
# the standard lookup behaviour for special methods
|
||||||
_cb_type = type(exit)
|
_cb_type = _get_type(exit)
|
||||||
try:
|
try:
|
||||||
exit_method = _cb_type.__exit__
|
exit_method = _cb_type.__exit__
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -358,7 +372,7 @@ class ExitStack(object):
|
||||||
returns the result of the __enter__ method.
|
returns the result of the __enter__ method.
|
||||||
"""
|
"""
|
||||||
# We look up the special methods on the type to match the with statement
|
# We look up the special methods on the type to match the with statement
|
||||||
_cm_type = type(cm)
|
_cm_type = _get_type(cm)
|
||||||
_exit = _cm_type.__exit__
|
_exit = _cm_type.__exit__
|
||||||
result = _cm_type.__enter__(cm)
|
result = _cm_type.__enter__(cm)
|
||||||
self._push_cm_exit(cm, _exit)
|
self._push_cm_exit(cm, _exit)
|
||||||
|
|
|
||||||
|
|
@ -310,10 +310,6 @@ Functions and classes provided:
|
||||||
foundation for higher level context managers that manipulate the exit
|
foundation for higher level context managers that manipulate the exit
|
||||||
stack in application specific ways.
|
stack in application specific ways.
|
||||||
|
|
||||||
Context managers used with :class:`ExitStack` must be new-style classes -
|
|
||||||
this is the default on Python 3, but requires explicitly inheriting from
|
|
||||||
:class:`object` or another new-style class in Python 2.
|
|
||||||
|
|
||||||
.. versionadded:: 0.4
|
.. versionadded:: 0.4
|
||||||
Part of the standard library in Python 3.3 and later
|
Part of the standard library in Python 3.3 and later
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -706,6 +706,39 @@ class TestExitStack(unittest.TestCase):
|
||||||
stack.push(cm)
|
stack.push(cm)
|
||||||
self.assertIs(stack._exit_callbacks[-1], cm)
|
self.assertIs(stack._exit_callbacks[-1], cm)
|
||||||
|
|
||||||
|
def test_default_class_semantics(self):
|
||||||
|
# For Python 2.x, this ensures compatibility with old-style classes
|
||||||
|
# For Python 3.x, it just reruns some of the other tests
|
||||||
|
class DefaultCM:
|
||||||
|
def __enter__(self):
|
||||||
|
result.append("Enter")
|
||||||
|
def __exit__(self, *exc_details):
|
||||||
|
result.append("Exit")
|
||||||
|
class DefaultCallable:
|
||||||
|
def __call__(self, *exc_details):
|
||||||
|
result.append("Callback")
|
||||||
|
|
||||||
|
result = []
|
||||||
|
cm = DefaultCM()
|
||||||
|
cb = DefaultCallable()
|
||||||
|
with ExitStack() as stack:
|
||||||
|
stack.enter_context(cm)
|
||||||
|
self.assertIs(stack._exit_callbacks[-1].__self__, cm)
|
||||||
|
stack.push(cb)
|
||||||
|
stack.push(cm)
|
||||||
|
self.assertIs(stack._exit_callbacks[-1].__self__, cm)
|
||||||
|
result.append("Running")
|
||||||
|
stack.callback(cb)
|
||||||
|
self.assertIs(stack._exit_callbacks[-1].__wrapped__, cb)
|
||||||
|
self.assertEqual(result, ["Enter", "Running",
|
||||||
|
"Callback", "Exit",
|
||||||
|
"Callback", "Exit",
|
||||||
|
])
|
||||||
|
|
||||||
|
with ExitStack():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestRedirectStream:
|
class TestRedirectStream:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue