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