mirror of https://github.com/python/cpython
Write most of the 'writing context managers' section. I'd like comments on it,
but wait for a few hours before you read it; I'm still revising it and will be tackling contextlib next. Untabify
This commit is contained in:
parent
cb284197f2
commit
d058d0036a
|
@ -323,7 +323,7 @@ perform the relative import starting from the parent of the current
|
||||||
package. For example, code in the \module{A.B.C} module can do:
|
package. For example, code in the \module{A.B.C} module can do:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
from . import D # Imports A.B.D
|
from . import D # Imports A.B.D
|
||||||
from .. import E # Imports A.E
|
from .. import E # Imports A.E
|
||||||
from ..F import G # Imports A.F.G
|
from ..F import G # Imports A.F.G
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
@ -431,7 +431,7 @@ def counter (maximum):
|
||||||
i = 0
|
i = 0
|
||||||
while i < maximum:
|
while i < maximum:
|
||||||
yield i
|
yield i
|
||||||
i += 1
|
i += 1
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
When you call \code{counter(10)}, the result is an iterator that
|
When you call \code{counter(10)}, the result is an iterator that
|
||||||
|
@ -473,11 +473,11 @@ def counter (maximum):
|
||||||
i = 0
|
i = 0
|
||||||
while i < maximum:
|
while i < maximum:
|
||||||
val = (yield i)
|
val = (yield i)
|
||||||
# If value provided, change counter
|
# If value provided, change counter
|
||||||
if val is not None:
|
if val is not None:
|
||||||
i = val
|
i = val
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
And here's an example of changing the counter:
|
And here's an example of changing the counter:
|
||||||
|
@ -578,33 +578,34 @@ Sugalski.}
|
||||||
%======================================================================
|
%======================================================================
|
||||||
\section{PEP 343: The 'with' statement}
|
\section{PEP 343: The 'with' statement}
|
||||||
|
|
||||||
The \keyword{with} statement allows a clearer
|
The \keyword{with} statement allows a clearer version of code that
|
||||||
version of code that uses \code{try...finally} blocks
|
uses \code{try...finally} blocks to ensure that clean-up code is
|
||||||
|
executed.
|
||||||
|
|
||||||
First, I'll discuss the statement as it will commonly be used, and
|
First, I'll discuss the statement as it will commonly be used, and
|
||||||
then I'll discuss the detailed implementation and how to write objects
|
then a subsection will examine the implementation details and how to
|
||||||
(called ``context managers'') that can be used with this statement.
|
write objects (called ``context managers'') that can be used with this
|
||||||
Most people, who will only use \keyword{with} in company with an
|
statement. Most people will only use \keyword{with} in company with
|
||||||
existing object, don't need to know these details and can
|
existing objects that are documented to work as context managers, and
|
||||||
just use objects that are documented to work as context managers.
|
don't need to know these details, so you can skip the subsection if
|
||||||
Authors of new context managers will need to understand the details of
|
you like. Authors of new context managers will need to understand the
|
||||||
the underlying implementation.
|
details of the underlying implementation.
|
||||||
|
|
||||||
The \keyword{with} statement is a new control-flow structure whose
|
The \keyword{with} statement is a new control-flow structure whose
|
||||||
basic structure is:
|
basic structure is:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
with expression as variable:
|
with expression [as variable]:
|
||||||
with-block
|
with-block
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
The expression is evaluated, and it should result in a type of object
|
The expression is evaluated, and it should result in a type of object
|
||||||
that's called a context manager. The context manager can return a
|
that's called a context manager. The context manager can return a
|
||||||
value that will be bound to the name \var{variable}. (Note carefully:
|
value that can optionally be bound to the name \var{variable}. (Note
|
||||||
\var{variable} is \emph{not} assigned the result of \var{expression}.
|
carefully: \var{variable} is \emph{not} assigned the result of
|
||||||
One method of the context manager is run before \var{with-block} is
|
\var{expression}.) One method of the context manager is run before
|
||||||
executed, and another method is run after the block is done, even if
|
\var{with-block} is executed, and another method is run after the
|
||||||
the block raised an exception.
|
block is done, even if the block raised an exception.
|
||||||
|
|
||||||
To enable the statement in Python 2.5, you need
|
To enable the statement in Python 2.5, you need
|
||||||
to add the following directive to your module:
|
to add the following directive to your module:
|
||||||
|
@ -613,17 +614,22 @@ to add the following directive to your module:
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
Some standard Python objects can now behave as context managers. For
|
The statement will always be enabled in Python 2.6.
|
||||||
example, file objects:
|
|
||||||
|
Some standard Python objects can now behave as context managers. File
|
||||||
|
objects are one example:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
with open('/etc/passwd', 'r') as f:
|
with open('/etc/passwd', 'r') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
print line
|
print line
|
||||||
|
... more processing code ...
|
||||||
# f has been automatically closed at this point.
|
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
After this statement has executed, the file object in \var{f} will
|
||||||
|
have been automatically closed at this point, even if the 'for' loop
|
||||||
|
raised an exception part-way through the block.
|
||||||
|
|
||||||
The \module{threading} module's locks and condition variables
|
The \module{threading} module's locks and condition variables
|
||||||
also support the \keyword{with} statement:
|
also support the \keyword{with} statement:
|
||||||
|
|
||||||
|
@ -634,7 +640,7 @@ with lock:
|
||||||
...
|
...
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
The lock is acquired before the block is executed, and released once
|
The lock is acquired before the block is executed, and always released once
|
||||||
the block is complete.
|
the block is complete.
|
||||||
|
|
||||||
The \module{decimal} module's contexts, which encapsulate the desired
|
The \module{decimal} module's contexts, which encapsulate the desired
|
||||||
|
@ -644,9 +650,8 @@ used as context managers.
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
import decimal
|
import decimal
|
||||||
|
|
||||||
v1 = decimal.Decimal('578')
|
|
||||||
|
|
||||||
# Displays with default precision of 28 digits
|
# Displays with default precision of 28 digits
|
||||||
|
v1 = decimal.Decimal('578')
|
||||||
print v1.sqrt()
|
print v1.sqrt()
|
||||||
|
|
||||||
with decimal.Context(prec=16):
|
with decimal.Context(prec=16):
|
||||||
|
@ -657,9 +662,170 @@ with decimal.Context(prec=16):
|
||||||
|
|
||||||
\subsection{Writing Context Managers}
|
\subsection{Writing Context Managers}
|
||||||
|
|
||||||
% XXX write this
|
Under the hood, the \keyword{with} statement is fairly complicated.
|
||||||
|
The interface demanded of context managers contains several methods.
|
||||||
|
|
||||||
|
A high-level explanation of the context management protocol is:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item The expression is evaluated and should result in an object
|
||||||
|
that's a context manager, meaning that it has a
|
||||||
|
\method{__context__()} method.
|
||||||
|
|
||||||
|
\item This object's \method{__context__()} method is called, and must
|
||||||
|
return a context object.
|
||||||
|
|
||||||
|
\item The context's \method{__enter__()} method is called.
|
||||||
|
The value returned is assigned to \var{VAR}. If no \code{as \var{VAR}}
|
||||||
|
clause is present, the value is simply discarded.
|
||||||
|
|
||||||
|
\item The code in \var{BLOCK} is executed.
|
||||||
|
|
||||||
|
\item If \var{BLOCK} raises an exception, the context object's
|
||||||
|
\method{__exit__(\var{type}, \var{value}, \var{traceback})} is called
|
||||||
|
with the exception's information, the same values returned by
|
||||||
|
\function{sys.exc_info()}. The method's return value
|
||||||
|
controls whether the exception is re-raised: any false value
|
||||||
|
re-raises the exception, and \code{True} will result in suppressing it.
|
||||||
|
You'll only rarely want to suppress the exception; the
|
||||||
|
author of the code containing the \keyword{with} statement will
|
||||||
|
never realize anything went wrong.
|
||||||
|
|
||||||
|
\item If \var{BLOCK} didn't raise an exception,
|
||||||
|
the context object's \method{__exit__()} is still called,
|
||||||
|
but \var{type}, \var{value}, and \var{traceback} are all \code{None}.
|
||||||
|
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Let's think through an example. I won't present detailed code but
|
||||||
|
will only sketch the necessary code. The example will be writing a
|
||||||
|
context manager for a database that supports transactions.
|
||||||
|
|
||||||
|
(For people unfamiliar with database terminology: a set of changes to
|
||||||
|
the database are grouped into a transaction. Transactions can be
|
||||||
|
either committed, meaning that all the changes are written into the
|
||||||
|
database, or rolled back, meaning that the changes are all discarded
|
||||||
|
and the database is unchanged. See any database textbook for more
|
||||||
|
information.)
|
||||||
|
% XXX find a shorter reference?
|
||||||
|
|
||||||
|
Let's assume there's an object representing a database connection.
|
||||||
|
Our goal will be to let the user write code like this:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
db_connection = DatabaseConnection()
|
||||||
|
with db_connection as cursor:
|
||||||
|
cursor.execute('insert into ...')
|
||||||
|
cursor.execute('delete from ...')
|
||||||
|
# ... more operations ...
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
The transaction should either be committed if the code in the block
|
||||||
|
runs flawlessly, or rolled back if there's an exception.
|
||||||
|
|
||||||
|
First, the \class{DatabaseConnection} needs a \method{__context__()}
|
||||||
|
method. Sometimes an object can be its own context manager and can
|
||||||
|
simply return \code{self}; the \module{threading} module's lock objects
|
||||||
|
can do this. For our database example, though, we need to
|
||||||
|
create a new object; I'll call this class \class{DatabaseContext}.
|
||||||
|
Our \method{__context__()} must therefore look like this:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
class DatabaseConnection:
|
||||||
|
...
|
||||||
|
def __context__ (self):
|
||||||
|
return DatabaseContext(self)
|
||||||
|
|
||||||
|
# Database interface
|
||||||
|
def cursor (self):
|
||||||
|
"Returns a cursor object and starts a new transaction"
|
||||||
|
def commit (self):
|
||||||
|
"Commits current transaction"
|
||||||
|
def rollback (self):
|
||||||
|
"Rolls back current transaction"
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
The context needs the connection object so that the connection
|
||||||
|
object's \method{commit()} or \method{rollback()} methods can be
|
||||||
|
called:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
class DatabaseContext:
|
||||||
|
def __init__ (self, connection):
|
||||||
|
self.connection = connection
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
The \method {__enter__()} method is pretty easy, having only
|
||||||
|
to start a new transaction. In this example,
|
||||||
|
the resulting cursor object would be a useful result,
|
||||||
|
so the method will return it. The user can
|
||||||
|
then add \code{as cursor} to their \keyword{with} statement
|
||||||
|
to bind the cursor to a variable name.
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
class DatabaseContext:
|
||||||
|
...
|
||||||
|
def __enter__ (self):
|
||||||
|
# Code to start a new transaction
|
||||||
|
cursor = self.connection.cursor()
|
||||||
|
return cursor
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
The \method{__exit__()} method is the most complicated because it's
|
||||||
|
where most of the work has to be done. The method has to check if an
|
||||||
|
exception occurred. If there was no exception, the transaction is
|
||||||
|
committed. The transaction is rolled back if there was an exception.
|
||||||
|
Here the code will just fall off the end of the function, returning
|
||||||
|
the default value of \code{None}. \code{None} is false, so the exception
|
||||||
|
will be re-raised automatically. If you wished, you could be more explicit
|
||||||
|
and add a \keyword{return} at the marked location.
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
class DatabaseContext:
|
||||||
|
...
|
||||||
|
def __exit__ (self, type, value, tb):
|
||||||
|
if tb is None:
|
||||||
|
# No exception, so commit
|
||||||
|
self.connection.commit()
|
||||||
|
else:
|
||||||
|
# Exception occurred, so rollback.
|
||||||
|
self.connection.rollback()
|
||||||
|
# return False
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
\begin{comment}
|
||||||
|
% XXX should I give the code, or is the above explanation sufficient?
|
||||||
|
\pep{343} shows the code generated for a \keyword{with} statement. A
|
||||||
|
statement such as:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
with EXPR as VAR:
|
||||||
|
BLOCK
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
is translated into:
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
ctx = (EXPR).__context__()
|
||||||
|
exit = ctx.__exit__ # Not calling it yet
|
||||||
|
value = ctx.__enter__()
|
||||||
|
exc = True
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
VAR = value # Only if "as VAR" is present
|
||||||
|
BLOCK
|
||||||
|
except:
|
||||||
|
# The exceptional case is handled here
|
||||||
|
exc = False
|
||||||
|
if not exit(*sys.exc_info()):
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
# The normal and non-local-goto cases are handled here
|
||||||
|
if exc:
|
||||||
|
exit(None, None, None)
|
||||||
|
\end{verbatim}
|
||||||
|
\end{comment}
|
||||||
|
|
||||||
This section still needs to be written.
|
|
||||||
|
|
||||||
The new \module{contextlib} module provides some functions and a
|
The new \module{contextlib} module provides some functions and a
|
||||||
decorator that are useful for writing context managers.
|
decorator that are useful for writing context managers.
|
||||||
|
@ -670,7 +836,9 @@ Future versions will go into more detail.
|
||||||
\begin{seealso}
|
\begin{seealso}
|
||||||
|
|
||||||
\seepep{343}{The ``with'' statement}{PEP written by
|
\seepep{343}{The ``with'' statement}{PEP written by
|
||||||
Guido van Rossum and Nick Coghlan. }
|
Guido van Rossum and Nick Coghlan.
|
||||||
|
The PEP shows the code generated for a \keyword{with} statement,
|
||||||
|
which can be helpful in learning how context managers work.}
|
||||||
|
|
||||||
\end{seealso}
|
\end{seealso}
|
||||||
|
|
||||||
|
@ -1027,10 +1195,10 @@ Printing \code{index} results in the following output:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
defaultdict(<type 'list'>, {'c': ['cammin', 'che'], 'e': ['era'],
|
defaultdict(<type 'list'>, {'c': ['cammin', 'che'], 'e': ['era'],
|
||||||
'd': ['del', 'di', 'diritta'], 'm': ['mezzo', 'mi'],
|
'd': ['del', 'di', 'diritta'], 'm': ['mezzo', 'mi'],
|
||||||
'l': ['la'], 'o': ['oscura'], 'n': ['nel', 'nostra'],
|
'l': ['la'], 'o': ['oscura'], 'n': ['nel', 'nostra'],
|
||||||
'p': ['per'], 's': ['selva', 'smarrita'],
|
'p': ['per'], 's': ['selva', 'smarrita'],
|
||||||
'r': ['ritrovai'], 'u': ['una'], 'v': ['vita', 'via']}
|
'r': ['ritrovai'], 'u': ['una'], 'v': ['vita', 'via']}
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
The \class{deque} double-ended queue type supplied by the
|
The \class{deque} double-ended queue type supplied by the
|
||||||
|
@ -1435,7 +1603,7 @@ h = hashlib.sha384()
|
||||||
h = hashlib.sha512()
|
h = hashlib.sha512()
|
||||||
|
|
||||||
# Alternative form
|
# Alternative form
|
||||||
h = hashlib.new('md5') # Provide algorithm as a string
|
h = hashlib.new('md5') # Provide algorithm as a string
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
Once a hash object has been created, its methods are the same as before:
|
Once a hash object has been created, its methods are the same as before:
|
||||||
|
@ -1515,9 +1683,9 @@ c.execute('select * from stocks where symbol=?', ('IBM',))
|
||||||
|
|
||||||
# Larger example
|
# Larger example
|
||||||
for t in (('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
|
for t in (('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
|
||||||
('2006-04-05', 'BUY', 'MSOFT', 1000, 72.00),
|
('2006-04-05', 'BUY', 'MSOFT', 1000, 72.00),
|
||||||
('2006-04-06', 'SELL', 'IBM', 500, 53.00),
|
('2006-04-06', 'SELL', 'IBM', 500, 53.00),
|
||||||
):
|
):
|
||||||
c.execute('insert into stocks values (?,?,?,?,?)', t)
|
c.execute('insert into stocks values (?,?,?,?,?)', t)
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue