Finish bringing SVN into line with latest version of PEP 343 by getting rid of all remaining references to context objects that I could find. Without a __context__() method context objects no longer exist. Also get test_with working again, and adopt a suggestion from Neal for decimal.Context.get_manager()
This commit is contained in:
parent
1b06a1d4e3
commit
afd5e63e24
|
@ -11,19 +11,20 @@ This module provides utilities for common tasks involving the
|
|||
|
||||
Functions provided:
|
||||
|
||||
\begin{funcdesc}{context}{func}
|
||||
\begin{funcdesc}{contextmanager}{func}
|
||||
This function is a decorator that can be used to define a factory
|
||||
function for \keyword{with} statement context objects, without
|
||||
needing to create a class or separate \method{__enter__()} and
|
||||
\method{__exit__()} methods.
|
||||
|
||||
A simple example:
|
||||
A simple example (this is not recommended as a real way of
|
||||
generating HTML!):
|
||||
|
||||
\begin{verbatim}
|
||||
from __future__ import with_statement
|
||||
from contextlib import contextfactory
|
||||
from contextlib import contextmanager
|
||||
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def tag(name):
|
||||
print "<%s>" % name
|
||||
yield
|
||||
|
@ -56,7 +57,7 @@ treat the exception as having been handled, and resume execution with
|
|||
the statement immediately following the \keyword{with} statement.
|
||||
\end{funcdesc}
|
||||
|
||||
\begin{funcdesc}{nested}{ctx1\optional{, ctx2\optional{, ...}}}
|
||||
\begin{funcdesc}{nested}{mgr1\optional{, mgr2\optional{, ...}}}
|
||||
Combine multiple context managers into a single nested context manager.
|
||||
|
||||
Code like this:
|
||||
|
@ -78,12 +79,12 @@ with A as X:
|
|||
\end{verbatim}
|
||||
|
||||
Note that if the \method{__exit__()} method of one of the nested
|
||||
context objects indicates an exception should be suppressed, no
|
||||
context managers indicates an exception should be suppressed, no
|
||||
exception information will be passed to any remaining outer context
|
||||
objects. Similarly, if the \method{__exit__()} method of one of the
|
||||
nested context objects raises an exception, any previous exception
|
||||
nested context managers raises an exception, any previous exception
|
||||
state will be lost; the new exception will be passed to the
|
||||
\method{__exit__()} methods of any remaining outer context objects.
|
||||
\method{__exit__()} methods of any remaining outer context managers.
|
||||
In general, \method{__exit__()} methods should avoid raising
|
||||
exceptions, and in particular they should not re-raise a
|
||||
passed-in exception.
|
||||
|
@ -91,13 +92,13 @@ passed-in exception.
|
|||
|
||||
\label{context-closing}
|
||||
\begin{funcdesc}{closing}{thing}
|
||||
Return a context that closes \var{thing} upon completion of the
|
||||
block. This is basically equivalent to:
|
||||
Return a context manager that closes \var{thing} upon completion of
|
||||
the block. This is basically equivalent to:
|
||||
|
||||
\begin{verbatim}
|
||||
from contextlib import contextfactory
|
||||
from contextlib import contextmanager
|
||||
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def closing(thing):
|
||||
try:
|
||||
yield thing
|
||||
|
|
|
@ -1753,67 +1753,50 @@ implemented in C will have to provide a writable
|
|||
\end{memberdesc}
|
||||
|
||||
|
||||
\subsection{Context Types \label{typecontext}}
|
||||
\subsection{Context Manager Types \label{typecontextmanager}}
|
||||
|
||||
\versionadded{2.5}
|
||||
\index{with statement context protocol}
|
||||
\index{context manager}
|
||||
\index{context management protocol}
|
||||
\index{protocol!with statement context}
|
||||
\index{protocol!context management}
|
||||
|
||||
Python's \keyword{with} statement supports the concept of a runtime
|
||||
context defined by a context manager. This is implemented using
|
||||
three distinct methods; these are used to allow user-defined
|
||||
classes to define a runtime context.
|
||||
two separate methods that allow user-defined classes to define
|
||||
a runtime context that is entered before the statement body is
|
||||
executed and exited when the statement ends.
|
||||
|
||||
The \dfn{context management protocol} consists of a single
|
||||
method that needs to be provided for a context manager object to
|
||||
The \dfn{context management protocol} consists of a pair of
|
||||
methods that need to be provided for a context manager object to
|
||||
define a runtime context:
|
||||
|
||||
\begin{methoddesc}[context manager]{__context__}{}
|
||||
Return a with statement context object. The object is required to
|
||||
support the with statement context protocol described below. If an
|
||||
object supports different kinds of runtime context, additional
|
||||
methods can be provided to specifically request context objects for
|
||||
those kinds of runtime context. (An example of an object supporting
|
||||
multiple kinds of context would be a synchronisation object which
|
||||
supported both a locked context for normal thread synchronisation
|
||||
and an unlocked context to temporarily release a held lock while
|
||||
performing a potentially long running operation)
|
||||
\end{methoddesc}
|
||||
\begin{methoddesc}[context manager]{__enter__}{}
|
||||
Enter the runtime context and return either this object or another
|
||||
object related to the runtime context. The value returned by this
|
||||
method is bound to the identifier in the \keyword{as} clause of
|
||||
\keyword{with} statements using this context manager.
|
||||
|
||||
The with statement context objects themselves are required to support the
|
||||
following three methods, which together form the
|
||||
\dfn{with statement context protocol}:
|
||||
|
||||
\begin{methoddesc}[with statement context]{__context__}{}
|
||||
Return the context object itself. This is required to allow both
|
||||
context objects and context managers to be used in a \keyword{with}
|
||||
An example of a context manager that returns itself is a file object.
|
||||
File objects return themselves from __enter__() to allow
|
||||
\function{open()} to be used as the context expression in a with
|
||||
statement.
|
||||
|
||||
An example of a context manager that returns a related
|
||||
object is the one returned by \code{decimal.Context.get_manager()}.
|
||||
These managers set the active decimal context to a copy of the
|
||||
original decimal context and then return the copy. This allows
|
||||
changes to be made to the current decimal context in the body of
|
||||
the \keyword{with} statement without affecting code outside
|
||||
the \keyword{with} statement.
|
||||
\end{methoddesc}
|
||||
|
||||
\begin{methoddesc}[with statement context]{__enter__}{}
|
||||
Enter the runtime context and return either the defining context
|
||||
manager or another object related to the runtime context. The value
|
||||
returned by this method is bound to the identifier in the
|
||||
\keyword{as} clause of \keyword{with} statements using this context.
|
||||
(An example of a context object that returns the original context
|
||||
manager is file objects, which are returned from __enter__() to
|
||||
allow \function{open()} to be used directly in a with
|
||||
statement. An example of a context object that returns a related
|
||||
object is \code{decimal.Context} which sets the active decimal
|
||||
context to a copy of the context manager and then returns the copy.
|
||||
This allows changes to be made to the current decimal context in the
|
||||
body of the \keyword{with} statement without affecting code outside
|
||||
the \keyword{with} statement).
|
||||
\end{methoddesc}
|
||||
|
||||
\begin{methoddesc}[with statement context]{__exit__}{exc_type, exc_val, exc_tb}
|
||||
\begin{methoddesc}[context manager]{__exit__}{exc_type, exc_val, exc_tb}
|
||||
Exit the runtime context and return a Boolean flag indicating if any
|
||||
expection that occurred should be suppressed. If an exception
|
||||
occurred while executing the body of the \keyword{with} statement, the
|
||||
arguments contain the exception type, value and traceback information.
|
||||
Otherwise, all three arguments are \var{None}.
|
||||
|
||||
Returning a true value from this method will cause the \keyword{with}
|
||||
statement to suppress the exception and continue execution with the
|
||||
statement immediately following the \keyword{with} statement. Otherwise
|
||||
|
@ -1821,6 +1804,7 @@ following three methods, which together form the
|
|||
executing. Exceptions that occur during execution of this method will
|
||||
replace any exception that occurred in the body of the \keyword{with}
|
||||
statement.
|
||||
|
||||
The exception passed in should never be reraised explicitly - instead,
|
||||
this method should return a false value to indicate that the method
|
||||
completed successfully and does not want to suppress the raised
|
||||
|
@ -1829,20 +1813,18 @@ following three methods, which together form the
|
|||
\method{__exit__()} method has actually failed.
|
||||
\end{methoddesc}
|
||||
|
||||
Python defines several context objects and managers to support
|
||||
easy thread synchronisation, prompt closure of files or other
|
||||
objects, and thread-safe manipulation of the decimal arithmetic
|
||||
context. The specific types are not important beyond their
|
||||
implementation of the context management and with statement context
|
||||
protocols.
|
||||
Python defines several context managers to support easy thread
|
||||
synchronisation, prompt closure of files or other objects, and
|
||||
simpler manipulation of the active decimal arithmetic
|
||||
context. The specific types are not treated specially beyond
|
||||
their implementation of the context management protocol.
|
||||
|
||||
Python's generators and the \code{contextlib.contextfactory} decorator
|
||||
provide a convenient way to implement these protocols. If a context
|
||||
manager's \method{__context__()} method is implemented as a
|
||||
generator decorated with the \code{contextlib.contextfactory}
|
||||
decorator, it will automatically return a with statement context
|
||||
object supplying the necessary \method{__context__()},
|
||||
\method{__enter__()} and \method{__exit__()} methods.
|
||||
provide a convenient way to implement these protocols. If a generator
|
||||
function is decorated with the \code{contextlib.contextfactory}
|
||||
decorator, it will return a context manager implementing the necessary
|
||||
\method{__enter__()} and \method{__exit__()} methods, rather than the
|
||||
iterator produced by an undecorated generator function.
|
||||
|
||||
Note that there is no specific slot for any of these methods in the
|
||||
type structure for Python objects in the Python/C API. Extension
|
||||
|
|
|
@ -2112,14 +2112,13 @@ implement a \method{__coerce__()} method, for use by the built-in
|
|||
|
||||
\end{itemize}
|
||||
|
||||
\subsection{With Statement Contexts and Context Managers\label{context-managers}}
|
||||
\subsection{With Statement Context Managers\label{context-managers}}
|
||||
|
||||
\versionadded{2.5}
|
||||
|
||||
A \dfn{context manager} is an object that defines the runtime
|
||||
context to be established when executing a \keyword{with}
|
||||
statement. The context manager provides a
|
||||
\dfn{with statement context object} which manages the entry into,
|
||||
statement. The context manager handles the entry into,
|
||||
and the exit from, the desired runtime context for the execution
|
||||
of the block of code. Context managers are normally invoked using
|
||||
the \keyword{with} statement (described in section~\ref{with}), but
|
||||
|
@ -2127,18 +2126,16 @@ can also be used by directly invoking their methods.
|
|||
|
||||
\stindex{with}
|
||||
\index{context manager}
|
||||
\index{context (with statement)}
|
||||
\index{with statement context}
|
||||
|
||||
Typical uses of context managers and contexts include saving and
|
||||
Typical uses of context managers include saving and
|
||||
restoring various kinds of global state, locking and unlocking
|
||||
resources, closing opened files, etc.
|
||||
|
||||
For more information on context managers and context objects,
|
||||
see ``\ulink{Context Types}{../lib/typecontext.html}'' in the
|
||||
For more information on context managers, see
|
||||
``\ulink{Context Types}{../lib/typecontextmanager.html}'' in the
|
||||
\citetitle[../lib/lib.html]{Python Library Reference}.
|
||||
|
||||
\begin{methoddesc}[with statement context]{__enter__}{self}
|
||||
\begin{methoddesc}[context manager]{__enter__}{self}
|
||||
Enter the runtime context related to this object. The \keyword{with}
|
||||
statement will bind this method's return value to the target(s)
|
||||
specified in the \keyword{as} clause of the statement, if any.
|
||||
|
|
|
@ -315,8 +315,8 @@ statement to generate exceptions may be found in section~\ref{raise}.
|
|||
\versionadded{2.5}
|
||||
|
||||
The \keyword{with} statement is used to wrap the execution of a block
|
||||
with methods defined by a context manager or \keyword{with} statement context
|
||||
object (see section~\ref{context-managers}). This allows common
|
||||
with methods defined by a context manager (see
|
||||
section~\ref{context-managers}). This allows common
|
||||
\keyword{try}...\keyword{except}...\keyword{finally} usage patterns to
|
||||
be encapsulated for convenient reuse.
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import sys
|
||||
|
||||
__all__ = ["contextfactory", "nested", "closing"]
|
||||
__all__ = ["contextmanager", "nested", "closing"]
|
||||
|
||||
class GeneratorContext(object):
|
||||
"""Helper for @contextfactory decorator."""
|
||||
class GeneratorContextManager(object):
|
||||
"""Helper for @contextmanager decorator."""
|
||||
|
||||
def __init__(self, gen):
|
||||
self.gen = gen
|
||||
|
@ -45,12 +45,12 @@ class GeneratorContext(object):
|
|||
raise
|
||||
|
||||
|
||||
def contextfactory(func):
|
||||
"""@contextfactory decorator.
|
||||
def contextmanager(func):
|
||||
"""@contextmanager decorator.
|
||||
|
||||
Typical usage:
|
||||
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def some_generator(<arguments>):
|
||||
<setup>
|
||||
try:
|
||||
|
@ -74,7 +74,7 @@ def contextfactory(func):
|
|||
|
||||
"""
|
||||
def helper(*args, **kwds):
|
||||
return GeneratorContext(func(*args, **kwds))
|
||||
return GeneratorContextManager(func(*args, **kwds))
|
||||
try:
|
||||
helper.__name__ = func.__name__
|
||||
helper.__doc__ = func.__doc__
|
||||
|
@ -84,7 +84,7 @@ def contextfactory(func):
|
|||
return helper
|
||||
|
||||
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def nested(*managers):
|
||||
"""Support multiple context managers in a single with-statement.
|
||||
|
||||
|
|
|
@ -2173,7 +2173,7 @@ for name in rounding_functions:
|
|||
|
||||
del name, val, globalname, rounding_functions
|
||||
|
||||
class WithStatementContext(object):
|
||||
class ContextManager(object):
|
||||
"""Helper class to simplify Context management.
|
||||
|
||||
Sample usage:
|
||||
|
@ -2248,8 +2248,8 @@ class Context(object):
|
|||
s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']')
|
||||
return ', '.join(s) + ')'
|
||||
|
||||
def context_manager(self):
|
||||
return WithStatementContext(self.copy())
|
||||
def get_manager(self):
|
||||
return ContextManager(self.copy())
|
||||
|
||||
def clear_flags(self):
|
||||
"""Reset all flags to zero"""
|
||||
|
|
|
@ -13,9 +13,9 @@ from test.test_support import run_suite
|
|||
|
||||
class ContextManagerTestCase(unittest.TestCase):
|
||||
|
||||
def test_contextfactory_plain(self):
|
||||
def test_contextmanager_plain(self):
|
||||
state = []
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
state.append(1)
|
||||
yield 42
|
||||
|
@ -26,9 +26,9 @@ class ContextManagerTestCase(unittest.TestCase):
|
|||
state.append(x)
|
||||
self.assertEqual(state, [1, 42, 999])
|
||||
|
||||
def test_contextfactory_finally(self):
|
||||
def test_contextmanager_finally(self):
|
||||
state = []
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
state.append(1)
|
||||
try:
|
||||
|
@ -47,8 +47,8 @@ class ContextManagerTestCase(unittest.TestCase):
|
|||
self.fail("Expected ZeroDivisionError")
|
||||
self.assertEqual(state, [1, 42, 999])
|
||||
|
||||
def test_contextfactory_no_reraise(self):
|
||||
@contextfactory
|
||||
def test_contextmanager_no_reraise(self):
|
||||
@contextmanager
|
||||
def whee():
|
||||
yield
|
||||
ctx = whee()
|
||||
|
@ -56,8 +56,8 @@ class ContextManagerTestCase(unittest.TestCase):
|
|||
# Calling __exit__ should not result in an exception
|
||||
self.failIf(ctx.__exit__(TypeError, TypeError("foo"), None))
|
||||
|
||||
def test_contextfactory_trap_yield_after_throw(self):
|
||||
@contextfactory
|
||||
def test_contextmanager_trap_yield_after_throw(self):
|
||||
@contextmanager
|
||||
def whoo():
|
||||
try:
|
||||
yield
|
||||
|
@ -69,9 +69,9 @@ class ContextManagerTestCase(unittest.TestCase):
|
|||
RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
|
||||
)
|
||||
|
||||
def test_contextfactory_except(self):
|
||||
def test_contextmanager_except(self):
|
||||
state = []
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
state.append(1)
|
||||
try:
|
||||
|
@ -86,14 +86,14 @@ class ContextManagerTestCase(unittest.TestCase):
|
|||
raise ZeroDivisionError(999)
|
||||
self.assertEqual(state, [1, 42, 999])
|
||||
|
||||
def test_contextfactory_attribs(self):
|
||||
def test_contextmanager_attribs(self):
|
||||
def attribs(**kw):
|
||||
def decorate(func):
|
||||
for k,v in kw.items():
|
||||
setattr(func,k,v)
|
||||
return func
|
||||
return decorate
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
@attribs(foo='bar')
|
||||
def baz(spam):
|
||||
"""Whee!"""
|
||||
|
@ -106,13 +106,13 @@ class NestedTestCase(unittest.TestCase):
|
|||
# XXX This needs more work
|
||||
|
||||
def test_nested(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
yield 1
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def b():
|
||||
yield 2
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def c():
|
||||
yield 3
|
||||
with nested(a(), b(), c()) as (x, y, z):
|
||||
|
@ -122,14 +122,14 @@ class NestedTestCase(unittest.TestCase):
|
|||
|
||||
def test_nested_cleanup(self):
|
||||
state = []
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
state.append(1)
|
||||
try:
|
||||
yield 2
|
||||
finally:
|
||||
state.append(3)
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def b():
|
||||
state.append(4)
|
||||
try:
|
||||
|
@ -148,7 +148,7 @@ class NestedTestCase(unittest.TestCase):
|
|||
|
||||
def test_nested_right_exception(self):
|
||||
state = []
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
yield 1
|
||||
class b(object):
|
||||
|
@ -170,10 +170,10 @@ class NestedTestCase(unittest.TestCase):
|
|||
self.fail("Didn't raise ZeroDivisionError")
|
||||
|
||||
def test_nested_b_swallows(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
yield
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def b():
|
||||
try:
|
||||
yield
|
||||
|
@ -187,7 +187,7 @@ class NestedTestCase(unittest.TestCase):
|
|||
self.fail("Didn't swallow ZeroDivisionError")
|
||||
|
||||
def test_nested_break(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
yield
|
||||
state = 0
|
||||
|
@ -199,7 +199,7 @@ class NestedTestCase(unittest.TestCase):
|
|||
self.assertEqual(state, 1)
|
||||
|
||||
def test_nested_continue(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
yield
|
||||
state = 0
|
||||
|
@ -211,7 +211,7 @@ class NestedTestCase(unittest.TestCase):
|
|||
self.assertEqual(state, 3)
|
||||
|
||||
def test_nested_return(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def a():
|
||||
try:
|
||||
yield
|
||||
|
@ -339,12 +339,12 @@ class DecimalContextTestCase(unittest.TestCase):
|
|||
orig_context = ctx.copy()
|
||||
try:
|
||||
ctx.prec = save_prec = decimal.ExtendedContext.prec + 5
|
||||
with decimal.ExtendedContext.context_manager():
|
||||
with decimal.ExtendedContext.get_manager():
|
||||
self.assertEqual(decimal.getcontext().prec,
|
||||
decimal.ExtendedContext.prec)
|
||||
self.assertEqual(decimal.getcontext().prec, save_prec)
|
||||
try:
|
||||
with decimal.ExtendedContext.context_manager():
|
||||
with decimal.ExtendedContext.get_manager():
|
||||
self.assertEqual(decimal.getcontext().prec,
|
||||
decimal.ExtendedContext.prec)
|
||||
1/0
|
||||
|
|
|
@ -10,25 +10,26 @@ __email__ = "mbland at acm dot org"
|
|||
import sys
|
||||
import unittest
|
||||
from collections import deque
|
||||
from contextlib import GeneratorContext, contextfactory
|
||||
from contextlib import GeneratorContextManager, contextmanager
|
||||
from test.test_support import run_unittest
|
||||
|
||||
|
||||
class MockContextManager(GeneratorContext):
|
||||
class MockContextManager(GeneratorContextManager):
|
||||
def __init__(self, gen):
|
||||
GeneratorContext.__init__(self, gen)
|
||||
GeneratorContextManager.__init__(self, gen)
|
||||
self.enter_called = False
|
||||
self.exit_called = False
|
||||
self.exit_args = None
|
||||
|
||||
def __enter__(self):
|
||||
self.enter_called = True
|
||||
return GeneratorContext.__enter__(self)
|
||||
return GeneratorContextManager.__enter__(self)
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.exit_called = True
|
||||
self.exit_args = (type, value, traceback)
|
||||
return GeneratorContext.__exit__(self, type, value, traceback)
|
||||
return GeneratorContextManager.__exit__(self, type,
|
||||
value, traceback)
|
||||
|
||||
|
||||
def mock_contextmanager(func):
|
||||
|
@ -439,7 +440,7 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
|
|||
self.assertAfterWithGeneratorInvariantsNoError(self.bar)
|
||||
|
||||
def testRaisedStopIteration1(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def cm():
|
||||
yield
|
||||
|
||||
|
@ -463,7 +464,7 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
|
|||
self.assertRaises(StopIteration, shouldThrow)
|
||||
|
||||
def testRaisedGeneratorExit1(self):
|
||||
@contextfactory
|
||||
@contextmanager
|
||||
def cm():
|
||||
yield
|
||||
|
||||
|
|
Loading…
Reference in New Issue