Rename @with_template and class Wrapper to @contextmanager and class

ContextManager.  Mark open issues as resolved.
This commit is contained in:
Guido van Rossum 2005-07-12 16:27:53 +00:00
parent 050ac22ecc
commit f2d367de26
1 changed files with 24 additions and 22 deletions

View File

@ -111,7 +111,7 @@ Motivation and Summary
example, it was impossible to do this for the opening example.
The idea was to define the template like this:
@with_template
@contextmanager
def opening(filename):
f = open(filename)
try:
@ -258,7 +258,7 @@ Generator Decorator
that makes it possible to use a generator that yields exactly once
to control a with-statement. Here's a sketch of such a decorator:
class Wrapper(object):
class ContextManager(object):
def __init__(self, gen):
self.gen = gen
@ -285,16 +285,16 @@ Generator Decorator
else:
raise RuntimeError("generator caught exception")
def with_template(func):
def contextmanager(func):
def helper(*args, **kwds):
return Wrapper(func(*args, **kwds))
return ContextManager(func(*args, **kwds))
return helper
This decorator could be used as follows:
@with_template
@contextmanager
def opening(filename):
f = open(filename) # IOError here is untouched by Wrapper
f = open(filename) # IOError is untouched by ContextManager
try:
yield f
finally:
@ -332,18 +332,20 @@ Optional Extensions
is entered).
OTOH such mistakes are easily diagnosed; for example, the
with_template decorator above raises RuntimeError when the second
contextmanager decorator above raises RuntimeError when the second
with-statement calls f.__enter__() again.
Open Issues
Resolved Open Issues
Discussion on python-dev has revealed some open issues. I list
them here, with my preferred resolution and its motivation. The
PEP as currently written reflects this preferred resolution.
Discussion on python-dev revealed some open issues. I list them
here, with my preferred resolution and its motivation. The PEP
has been accepted without these being challenged, so the issues
are now resolved.
1. The __exit__() method of the with_template decorator class catches
StopIteration and considers it equivalent to re-raising the exception
passed to throw(). Is allowing StopIteration right here?
1. The __exit__() method of the contextmanager decorator class
catches StopIteration and considers it equivalent to re-raising
the exception passed to throw(). Is allowing StopIteration
right here?
This is so that a generator doing cleanup depending on the
exception thrown (like the transactional() example below) can
@ -368,7 +370,7 @@ Examples
1. A template for ensuring that a lock, acquired at the start of a
block, is released when the block is left:
@with_template
@contextmanager
def locking(lock):
lock.acquire()
try:
@ -390,7 +392,7 @@ Examples
2. A template for opening a file that ensures the file is closed
when the block is left:
@with_template
@contextmanager
def opening(filename, mode="r"):
f = open(filename, mode)
try:
@ -407,7 +409,7 @@ Examples
3. A template for committing or rolling back a database
transaction:
@with_template
@contextmanager
def transactional(db):
db.begin()
try:
@ -433,7 +435,7 @@ Examples
5. Redirect stdout temporarily:
@with_template
@contextmanager
def redirecting_stdout(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
@ -454,7 +456,7 @@ Examples
6. A variant on opening() that also returns an error condition:
@with_template
@contextmanager
def opening_w_error(filename, mode="r"):
try:
f = open(filename, mode)
@ -491,7 +493,7 @@ Examples
import decimal
@with_template
@contextmanager
def extra_precision(places=2):
c = decimal.getcontext()
saved_prec = c.prec
@ -520,7 +522,7 @@ Examples
9. Here's a more general Decimal-context-switching template:
@with_template
@contextmanager
def decimal_context(newctx=None):
oldctx = decimal.getcontext()
if newctx is None:
@ -545,7 +547,7 @@ Examples
10. A generic "object-closing" template:
@with_template
@contextmanager
def closing(obj):
try:
yield obj