summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libcontextlib.tex73
-rw-r--r--Doc/lib/libstdtypes.tex83
-rw-r--r--Doc/ref/ref3.tex49
-rw-r--r--Doc/ref/ref7.tex16
-rw-r--r--Lib/contextlib.py31
-rw-r--r--Lib/decimal.py4
-rw-r--r--Lib/test/test_contextlib.py46
7 files changed, 163 insertions, 139 deletions
diff --git a/Doc/lib/libcontextlib.tex b/Doc/lib/libcontextlib.tex
index 2a9eb0e..f212174 100644
--- a/Doc/lib/libcontextlib.tex
+++ b/Doc/lib/libcontextlib.tex
@@ -11,18 +11,19 @@ This module provides utilities for common tasks involving the
Functions provided:
-\begin{funcdesc}{contextmanager}{func}
-This function is a decorator that can be used to define context managers
-for use with the \keyword{with} statement, without needing to create a
-class or separate \method{__enter__()} and \method{__exit__()} methods.
+\begin{funcdesc}{context}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:
\begin{verbatim}
from __future__ import with_statement
-from contextlib import contextmanager
+from contextlib import contextfactory
-@contextmanager
+@contextfactory
def tag(name):
print "<%s>" % name
yield
@@ -36,9 +37,10 @@ foo
</h1>
\end{verbatim}
-When called, the decorated function must return a generator-iterator.
-This iterator must yield exactly one value, which will be bound to the
-targets in the \keyword{with} statement's \keyword{as} clause, if any.
+The function being decorated must return a generator-iterator when
+called. This iterator must yield exactly one value, which will be
+bound to the targets in the \keyword{with} statement's \keyword{as}
+clause, if any.
At the point where the generator yields, the block nested in the
\keyword{with} statement is executed. The generator is then resumed
@@ -53,20 +55,20 @@ reraise that exception. Otherwise the \keyword{with} statement will
treat the exception as having been handled, and resume execution with
the statement immediately following the \keyword{with} statement.
-Note that you can use \code{@contextmanager} to define a context
-specifier's \method{__context__} method. This is usually more
+Note that you can use \code{@contextfactory} to define a context
+manager's \method{__context__} method. This is usually more
convenient than creating another class just to serve as a context
-manager. For example:
+object. For example:
\begin{verbatim}
from __future__ import with_statement
-from contextlib import contextmanager
+from contextlib import contextfactory
class Tag:
def __init__(self, name):
self.name = name
- @contextmanager
+ @contextfactory
def __context__(self):
print "<%s>" % self.name
yield self
@@ -83,7 +85,7 @@ hello from <__main__.Tag instance at 0x402ce8ec>
\end{funcdesc}
\begin{funcdesc}{nested}{ctx1\optional{, ctx2\optional{, ...}}}
-Combine multiple context specifiers into a single nested context manager.
+Combine multiple context managers into a single nested context manager.
Code like this:
@@ -104,12 +106,12 @@ with A as X:
\end{verbatim}
Note that if the \method{__exit__()} method of one of the nested
-context managers indicates an exception should be suppressed, no
+context objects indicates an exception should be suppressed, no
exception information will be passed to any remaining outer context
-managers. Similarly, if the \method{__exit__()} method of one of the
-nested context managers raises an exception, any previous exception
+objects. Similarly, if the \method{__exit__()} method of one of the
+nested context objects 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 managers.
+\method{__exit__()} methods of any remaining outer context objects.
In general, \method{__exit__()} methods should avoid raising
exceptions, and in particular they should not re-raise a
passed-in exception.
@@ -117,13 +119,13 @@ passed-in exception.
\label{context-closing}
\begin{funcdesc}{closing}{thing}
-Return a context manager that closes \var{thing} upon completion of the
+Return a context that closes \var{thing} upon completion of the
block. This is basically equivalent to:
\begin{verbatim}
-from contextlib import contextmanager
+from contextlib import contextfactory
-@contextmanager
+@contextfactory
def closing(thing):
try:
yield thing
@@ -137,14 +139,33 @@ from __future__ import with_statement
from contextlib import closing
import codecs
-with closing(urllib.urlopen('http://www.python.org')) as f:
- for line in f:
+with closing(urllib.urlopen('http://www.python.org')) as page:
+ for line in page:
print line
\end{verbatim}
-without needing to explicitly close \code{f}. Even if an error occurs,
-\code{f.close()} will be called when the \keyword{with} block is exited.
+without needing to explicitly close \code{page}. Even if an error
+occurs, \code{page.close()} will be called when the \keyword{with}
+block is exited.
+Context managers with a close method can use this context factory
+directly without needing to implement their own
+\method{__context__()} method.
+\begin{verbatim}
+from __future__ import with_statement
+from contextlib import closing
+
+class MyClass:
+ def close(self):
+ print "Closing", self
+ __context__ = closing
+
+>>> with MyClass() as x:
+... print "Hello from", x
+...
+Hello from <__main__.MyClass instance at 0xb7df02ec>
+Closing <__main__.MyClass instance at 0xb7df02ec>
+\end{verbatim}
\end{funcdesc}
\begin{seealso}
diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex
index ea950c8..50be0fa 100644
--- a/Doc/lib/libstdtypes.tex
+++ b/Doc/lib/libstdtypes.tex
@@ -1756,59 +1756,59 @@ implemented in C will have to provide a writable
\subsection{Context Types \label{typecontext}}
\versionadded{2.5}
-\index{context specification protocol}
+\index{with statement context protocol}
\index{context management protocol}
-\index{protocol!context specification}
+\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 specifier. This is implemented using
+context defined by a context manager. This is implemented using
three distinct methods; these are used to allow user-defined
-classes to define a context.
+classes to define a runtime context.
-The \dfn{context specification protocol} consists of a single
-method that needs to be provided for a context specifier object to
+The \dfn{context management protocol} consists of a single
+method that needs to be provided for a context manager object to
define a runtime context:
-\begin{methoddesc}[context specifier]{__context__}{}
- Return a context manager object. The object is required to support
- the context management protocol described below. If an object
- supports different kinds of runtime context, additional methods can
- be provided to specifically request context managers for those
- kinds of 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)
+\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}
-The context manager objects themselves are required to support the
+The with statement context objects themselves are required to support the
following three methods, which together form the
-\dfn{context management protocol}:
+\dfn{with statement context protocol}:
-\begin{methoddesc}[context manager]{__context__}{}
- Return the context manager object itself. This is required to
- allow both context specifiers and context managers to be used with
- the \keyword{with} statement.
+\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}
+ statement.
\end{methoddesc}
-\begin{methoddesc}[context manager]{__enter__}{}
+\begin{methoddesc}[with statement context]{__enter__}{}
Enter the runtime context and return either the defining context
- specifier or another object related to the runtime context. The value
+ 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 with a context manager that returns the
- original context specifier 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 manager that returns a related
+ (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 specifier and then returns the copy
- to allow changes to be made to the current decimal context in the
- body of the \keyword{with} statement) without affecting code outside
+ 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}[context manager]{__exit__}{exc_type, exc_val, exc_tb}
+\begin{methoddesc}[with statement context]{__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
@@ -1829,19 +1829,18 @@ following three methods, which together form the
\method{__exit__()} method has actually failed.
\end{methoddesc}
-Python defines several context specifiers and managers to support
+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 specification and context
-management protocols.
-
-Python's generators and the \code{contextlib.contextmanager}
-decorator provide a convenient way to implement the context
-specification and context management protocols. If a context
-specifier's \method{__context__()} method is implemented as a
-generator decorated with the \code{contextlib.contextmanager}
-decorator, it will automatically return a context manager
+implementation of the context management and with statement context
+protocols.
+
+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.
diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex
index b1e1ee9..7b4089d 100644
--- a/Doc/ref/ref3.tex
+++ b/Doc/ref/ref3.tex
@@ -2112,59 +2112,60 @@ implement a \method{__coerce__()} method, for use by the built-in
\end{itemize}
-\subsection{Context Specifiers and Managers\label{context-managers}}
+\subsection{With Statement Contexts and Context Managers\label{context-managers}}
\versionadded{2.5}
-A \dfn{context specifier} is an object that defines the runtime
+A \dfn{context manager} is an object that defines the runtime
context to be established when executing a \keyword{with}
-statement. The context specifier provides a \dfn{context manager}
-which manages 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 can also be used by
-directly invoking their methods.
+statement. The context manager provides a
+\dfn{with statement context object} which manages 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
+can also be used by directly invoking their methods.
\stindex{with}
\index{context manager}
-\index{context specifier}
+\index{context (with statement)}
+\index{with statement context}
-Typical uses of context specifiers and managers include saving and
+Typical uses of context managers and contexts include saving and
restoring various kinds of global state, locking and unlocking
resources, closing opened files, etc.
-For more information on context specifiers and context manager objects,
+For more information on context managers and context objects,
see ``\ulink{Context Types}{../lib/typecontext.html}'' in the
\citetitle[../lib/lib.html]{Python Library Reference}.
-\begin{methoddesc}[context specifier]{__context__}{self}
+\begin{methoddesc}[context manager]{__context__}{self}
Invoked when the object is used as the context expression of a
\keyword{with} statement. The returned object must implement
\method{__enter__()} and \method{__exit__()} methods.
-Context specifiers written in Python can also implement this method
+Context managers written in Python can also implement this method
using a generator function decorated with the
-\function{contextlib.contextmanager} decorator, as this can be simpler
+\function{contextlib.contextfactory} decorator, as this can be simpler
than writing individual \method{__enter__()} and \method{__exit__()}
methods on a separate object when the state to be managed is complex.
-Context manager objects also need to implement this method; they are
-required to return themselves (that is, this method will simply
+With statement context objects also need to implement this method; they
+are required to return themselves (that is, this method will simply
return \var{self}).
\end{methoddesc}
-\begin{methoddesc}[context manager]{__enter__}{self}
-Enter the context managed by 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.
+\begin{methoddesc}[with statement context]{__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.
\end{methoddesc}
\begin{methoddesc}[context manager]{__exit__}
{self, exc_type, exc_value, traceback}
-Exit the context managed by this object. The parameters describe the
-exception that caused the context to be exited. If the context was
-exited without an exception, all three arguments will be
-\constant{None}.
+Exit the runtime context related to this object. The parameters
+describe the exception that caused the context to be exited. If
+the context was exited without an exception, all three arguments
+will be \constant{None}.
If an exception is supplied, and the method wishes to suppress the
exception (i.e., prevent it from being propagated), it should return a
diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex
index 0c59847..180e22f 100644
--- a/Doc/ref/ref7.tex
+++ b/Doc/ref/ref7.tex
@@ -315,10 +315,10 @@ 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 specifier or manager (see
-section~\ref{context-managers}). This allows common
+with methods defined by a context manager or with statement context
+object (see section~\ref{context-managers}). This allows common
\keyword{try}...\keyword{except}...\keyword{finally} usage patterns to
-be encapsulated as context specifiers or managers for convenient reuse.
+be encapsulated for convenient reuse.
\begin{productionlist}
\production{with_stmt}
@@ -329,12 +329,12 @@ The execution of the \keyword{with} statement proceeds as follows:
\begin{enumerate}
-\item The expression is evaluated, to obtain a context specifier.
+\item The context expression is evaluated, to obtain a context manager.
-\item The context specifier's \method{__context__()} method is
-invoked to obtain a context manager object.
+\item The context manger's \method{__context__()} method is
+invoked to obtain a with statement context object.
-\item The context manager's \method{__enter__()} method is invoked.
+\item The context object's \method{__enter__()} method is invoked.
\item If a target list was included in the \keyword{with}
statement, the return value from \method{__enter__()} is assigned to it.
@@ -347,7 +347,7 @@ an error occurring within the suite would be. See step 6 below.}
\item The suite is executed.
-\item The context manager's \method{__exit__()} method is invoked. If
+\item The context object's \method{__exit__()} method is invoked. If
an exception caused the suite to be exited, its type, value, and
traceback are passed as arguments to \method{__exit__()}. Otherwise,
three \constant{None} arguments are supplied.
diff --git a/Lib/contextlib.py b/Lib/contextlib.py
index 157b4cc..b2902a4 100644
--- a/Lib/contextlib.py
+++ b/Lib/contextlib.py
@@ -2,10 +2,10 @@
import sys
-__all__ = ["contextmanager", "nested", "closing"]
+__all__ = ["contextfactory", "nested", "closing"]
-class GeneratorContextManager(object):
- """Helper for @contextmanager decorator."""
+class GeneratorContext(object):
+ """Helper for @contextfactory decorator."""
def __init__(self, gen):
self.gen = gen
@@ -48,8 +48,8 @@ class GeneratorContextManager(object):
raise
-def contextmanager(func):
- """@contextmanager decorator.
+def contextfactory(func):
+ """@contextfactory decorator.
Typical usage:
@@ -77,7 +77,7 @@ def contextmanager(func):
"""
def helper(*args, **kwds):
- return GeneratorContextManager(func(*args, **kwds))
+ return GeneratorContext(func(*args, **kwds))
try:
helper.__name__ = func.__name__
helper.__doc__ = func.__doc__
@@ -87,7 +87,7 @@ def contextmanager(func):
return helper
-@contextmanager
+@contextfactory
def nested(*contexts):
"""Support multiple context managers in a single with-statement.
@@ -133,9 +133,8 @@ def nested(*contexts):
raise exc[0], exc[1], exc[2]
-@contextmanager
-def closing(thing):
- """Context manager to automatically close something at the end of a block.
+class closing(object):
+ """Context to automatically close something at the end of a block.
Code like this:
@@ -151,7 +150,11 @@ def closing(thing):
f.close()
"""
- try:
- yield thing
- finally:
- thing.close()
+ def __init__(self, thing):
+ self.thing = thing
+ def __context__(self):
+ return self
+ def __enter__(self):
+ return self.thing
+ def __exit__(self, *exc_info):
+ self.thing.close()
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 967f101..875e38a 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -2173,7 +2173,7 @@ for name in rounding_functions:
del name, val, globalname, rounding_functions
-class ContextManager(object):
+class WithStatementContext(object):
"""Helper class to simplify Context management.
Sample usage:
@@ -2249,7 +2249,7 @@ class Context(object):
return ', '.join(s) + ')'
def __context__(self):
- return ContextManager(self.copy())
+ return WithStatementContext(self.copy())
def clear_flags(self):
"""Reset all flags to zero"""
diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py
index 1a70997..53f23b2 100644
--- a/Lib/test/test_contextlib.py
+++ b/Lib/test/test_contextlib.py
@@ -13,9 +13,9 @@ from test.test_support import run_suite
class ContextManagerTestCase(unittest.TestCase):
- def test_contextmanager_plain(self):
+ def test_contextfactory_plain(self):
state = []
- @contextmanager
+ @contextfactory
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_contextmanager_finally(self):
+ def test_contextfactory_finally(self):
state = []
- @contextmanager
+ @contextfactory
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_contextmanager_no_reraise(self):
- @contextmanager
+ def test_contextfactory_no_reraise(self):
+ @contextfactory
def whee():
yield
ctx = whee().__context__()
@@ -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_contextmanager_trap_yield_after_throw(self):
- @contextmanager
+ def test_contextfactory_trap_yield_after_throw(self):
+ @contextfactory
def whoo():
try:
yield
@@ -69,9 +69,9 @@ class ContextManagerTestCase(unittest.TestCase):
RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
)
- def test_contextmanager_except(self):
+ def test_contextfactory_except(self):
state = []
- @contextmanager
+ @contextfactory
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_contextmanager_attribs(self):
+ def test_contextfactory_attribs(self):
def attribs(**kw):
def decorate(func):
for k,v in kw.items():
setattr(func,k,v)
return func
return decorate
- @contextmanager
+ @contextfactory
@attribs(foo='bar')
def baz(spam):
"""Whee!"""
@@ -106,13 +106,13 @@ class NestedTestCase(unittest.TestCase):
# XXX This needs more work
def test_nested(self):
- @contextmanager
+ @contextfactory
def a():
yield 1
- @contextmanager
+ @contextfactory
def b():
yield 2
- @contextmanager
+ @contextfactory
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 = []
- @contextmanager
+ @contextfactory
def a():
state.append(1)
try:
yield 2
finally:
state.append(3)
- @contextmanager
+ @contextfactory
def b():
state.append(4)
try:
@@ -148,7 +148,7 @@ class NestedTestCase(unittest.TestCase):
def test_nested_right_exception(self):
state = []
- @contextmanager
+ @contextfactory
def a():
yield 1
class b(object):
@@ -172,10 +172,10 @@ class NestedTestCase(unittest.TestCase):
self.fail("Didn't raise ZeroDivisionError")
def test_nested_b_swallows(self):
- @contextmanager
+ @contextfactory
def a():
yield
- @contextmanager
+ @contextfactory
def b():
try:
yield
@@ -189,7 +189,7 @@ class NestedTestCase(unittest.TestCase):
self.fail("Didn't swallow ZeroDivisionError")
def test_nested_break(self):
- @contextmanager
+ @contextfactory
def a():
yield
state = 0
@@ -201,7 +201,7 @@ class NestedTestCase(unittest.TestCase):
self.assertEqual(state, 1)
def test_nested_continue(self):
- @contextmanager
+ @contextfactory
def a():
yield
state = 0
@@ -213,7 +213,7 @@ class NestedTestCase(unittest.TestCase):
self.assertEqual(state, 3)
def test_nested_return(self):
- @contextmanager
+ @contextfactory
def a():
try:
yield