Commit f322d683 authored by Andrew M. Kuchling's avatar Andrew M. Kuchling

Update context manager section for removal of __context__

parent 214db63d
...@@ -638,7 +638,8 @@ The lock is acquired before the block is executed and always released once ...@@ -638,7 +638,8 @@ 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
precision and rounding characteristics for computations, also work. precision and rounding characteristics for computations, provide a
\method{context_manager()} method for getting a context manager:
\begin{verbatim} \begin{verbatim}
import decimal import decimal
...@@ -647,7 +648,8 @@ import decimal ...@@ -647,7 +648,8 @@ import decimal
v1 = decimal.Decimal('578') v1 = decimal.Decimal('578')
print v1.sqrt() print v1.sqrt()
with decimal.Context(prec=16): ctx = decimal.Context(prec=16)
with ctx.context_manager():
# All code in this block uses a precision of 16 digits. # All code in this block uses a precision of 16 digits.
# The original context is restored on exiting the block. # The original context is restored on exiting the block.
print v1.sqrt() print v1.sqrt()
...@@ -665,14 +667,12 @@ keep reading. ...@@ -665,14 +667,12 @@ keep reading.
A high-level explanation of the context management protocol is: A high-level explanation of the context management protocol is:
\begin{itemize} \begin{itemize}
\item The expression is evaluated and should result in an object
with a \method{__context__()} method (called a ``context manager'').
\item The context specifier's \method{__context__()} method is called, \item The expression is evaluated and should result in an object
and must return another object (called a ``with-statement context object'') that has called a ``context manager''. The context manager must have
\method{__enter__()} and \method{__exit__()} methods. \method{__enter__()} and \method{__exit__()} methods.
\item The context object's \method{__enter__()} method is called. The value \item The context manager's \method{__enter__()} method is called. The value
returned is assigned to \var{VAR}. If no \code{'as \var{VAR}'} clause returned is assigned to \var{VAR}. If no \code{'as \var{VAR}'} clause
is present, the value is simply discarded. is present, the value is simply discarded.
...@@ -680,7 +680,7 @@ is present, the value is simply discarded. ...@@ -680,7 +680,7 @@ is present, the value is simply discarded.
\item If \var{BLOCK} raises an exception, the \item If \var{BLOCK} raises an exception, the
\method{__exit__(\var{type}, \var{value}, \var{traceback})} is called \method{__exit__(\var{type}, \var{value}, \var{traceback})} is called
with the exception's information, the same values returned by with the exception details, the same values returned by
\function{sys.exc_info()}. The method's return value controls whether \function{sys.exc_info()}. The method's return value controls whether
the exception is re-raised: any false value re-raises the exception, the exception is re-raised: any false value re-raises the exception,
and \code{True} will result in suppressing it. You'll only rarely and \code{True} will result in suppressing it. You'll only rarely
...@@ -719,20 +719,11 @@ with db_connection as cursor: ...@@ -719,20 +719,11 @@ with db_connection as cursor:
The transaction should be committed if the code in the block The transaction should be committed if the code in the block
runs flawlessly or rolled back if there's an exception. runs flawlessly or rolled back if there's an exception.
Here's the basic interface
First, the \class{DatabaseConnection} needs a \method{__context__()} for \class{DatabaseConnection} that I'll assume:
method. Sometimes an object can simply return \code{self}; the
\module{threading} module's lock objects do this, for example. For
our database example, though, we need to create a new object; I'll
call this class \class{DatabaseContext}. Our \method{__context__()}
method must therefore look like this:
\begin{verbatim} \begin{verbatim}
class DatabaseConnection: class DatabaseConnection:
...
def __context__ (self):
return DatabaseContext(self)
# Database interface # Database interface
def cursor (self): def cursor (self):
"Returns a cursor object and starts a new transaction" "Returns a cursor object and starts a new transaction"
...@@ -742,16 +733,6 @@ class DatabaseConnection: ...@@ -742,16 +733,6 @@ class DatabaseConnection:
"Rolls back current transaction" "Rolls back current transaction"
\end{verbatim} \end{verbatim}
Instances of \class{DatabaseContext} need 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 The \method {__enter__()} method is pretty easy, having only to start
a new transaction. For this application the resulting cursor object a new transaction. For this application the resulting cursor object
would be a useful result, so the method will return it. The user can would be a useful result, so the method will return it. The user can
...@@ -759,11 +740,11 @@ then add \code{as cursor} to their '\keyword{with}' statement to bind ...@@ -759,11 +740,11 @@ then add \code{as cursor} to their '\keyword{with}' statement to bind
the cursor to a variable name. the cursor to a variable name.
\begin{verbatim} \begin{verbatim}
class DatabaseContext: class DatabaseConnection:
... ...
def __enter__ (self): def __enter__ (self):
# Code to start a new transaction # Code to start a new transaction
cursor = self.connection.cursor() cursor = self.cursor()
return cursor return cursor
\end{verbatim} \end{verbatim}
...@@ -779,15 +760,15 @@ wished, you could be more explicit and add a \keyword{return} ...@@ -779,15 +760,15 @@ wished, you could be more explicit and add a \keyword{return}
statement at the marked location. statement at the marked location.
\begin{verbatim} \begin{verbatim}
class DatabaseContext: class DatabaseConnection:
... ...
def __exit__ (self, type, value, tb): def __exit__ (self, type, value, tb):
if tb is None: if tb is None:
# No exception, so commit # No exception, so commit
self.connection.commit() self.commit()
else: else:
# Exception occurred, so rollback. # Exception occurred, so rollback.
self.connection.rollback() self.rollback()
# return False # return False
\end{verbatim} \end{verbatim}
...@@ -830,27 +811,8 @@ with db_transaction(db) as cursor: ...@@ -830,27 +811,8 @@ with db_transaction(db) as cursor:
... ...
\end{verbatim} \end{verbatim}
You can also use this decorator to write the \method{__context__()}
method for a class:
\begin{verbatim}
class DatabaseConnection:
@contextfactory
def __context__ (self):
cursor = self.cursor()
try:
yield cursor
except:
self.rollback()
raise
else:
self.commit()
\end{verbatim}
The \module{contextlib} module also has a \function{nested(\var{mgr1}, The \module{contextlib} module also has a \function{nested(\var{mgr1},
\var{mgr2}, ...)} function that combines a number of contexts so you \var{mgr2}, ...)} function that combines a number of context managers so you
don't need to write nested '\keyword{with}' statements. In this don't need to write nested '\keyword{with}' statements. In this
example, the single '\keyword{with}' statement both starts a database example, the single '\keyword{with}' statement both starts a database
transaction and acquires a thread lock: transaction and acquires a thread lock:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment