Add some more to the rationale section in PEP 377

This commit is contained in:
Nick Coghlan 2009-03-14 07:28:35 +00:00
parent 2d3b72243a
commit 01ddf563f6
1 changed files with 87 additions and 29 deletions

View File

@ -129,40 +129,95 @@ then handles and suppresses::
# instead of as classes!) # instead of as classes!)
class CM(object): class CM(object):
def __init__(self): def __init__(self):
self.cmA = None self.cmA = None
self.cmB = None self.cmB = None
def __enter__(self): def __enter__(self):
if self.cmA is not None: if self.cmA is not None:
raise RuntimeError("Can't re-use this CM") raise RuntimeError("Can't re-use this CM")
self.cmA = cmA() self.cmA = cmA()
self.cmA.__enter__() self.cmA.__enter__()
try: try:
self.cmB = cmB() self.cmB = cmB()
self.cmB.__enter__() self.cmB.__enter__()
except: except:
self.cmA.__exit__(*sys.exc_info()) self.cmA.__exit__(*sys.exc_info())
# Can't suppress in __enter__(), so must raise # Can't suppress in __enter__(), so must raise
raise raise
def __exit__(self, *args): def __exit__(self, *args):
suppress = False suppress = False
try: try:
if self.cmB is not None: if self.cmB is not None:
suppress = self.cmB.__exit__(*args) suppress = self.cmB.__exit__(*args)
except: except:
suppress = self.cmA.__exit__(*sys.exc_info()): suppress = self.cmA.__exit__(*sys.exc_info()):
if not suppress: if not suppress:
# Exception has changed, so reraise explicitly # Exception has changed, so reraise explicitly
raise raise
else:
if suppress:
# cmB already suppressed the exception,
# so don't pass it to cmA
suppress = self.cmA.__exit__(None, None, None):
else: else:
if suppress: suppress = self.cmA.__exit__(*args):
# cmB already suppressed the exception, return suppress
# so don't pass it to cmA
suppress = self.cmA.__exit__(None, None, None): With the proposed semantic change in place, the contextlib based examples
else: above would then "just work", but the class based version would need
suppress = self.cmA.__exit__(*args): adjustment to take advantage of the new semantics::
return suppress
class CM(object):
def __init__(self):
self.cmA = None
self.cmB = None
def __enter__(self):
if self.cmA is not None:
raise RuntimeError("Can't re-use this CM")
self.cmA = cmA()
self.cmA.__enter__()
try:
self.cmB = cmB()
self.cmB.__enter__()
except:
if self.cmA.__exit__(*sys.exc_info()):
# Suppress the exception, but don't run
# the body of the with statement either
raise SkipStatement
raise
def __exit__(self, *args):
suppress = False
try:
if self.cmB is not None:
suppress = self.cmB.__exit__(*args)
except:
suppress = self.cmA.__exit__(*sys.exc_info()):
if not suppress:
# Exception has changed, so reraise explicitly
raise
else:
if suppress:
# cmB already suppressed the exception,
# so don't pass it to cmA
suppress = self.cmA.__exit__(None, None, None):
else:
suppress = self.cmA.__exit__(*args):
return suppress
There is currently a tentative suggestion [3] to add import-style syntax to
the ``with`` statement to allow multiple context managers to be included in
a single ``with`` statement without needing to use ``contextlib.nested``. In
that case the compiler has the option of simply emitting multiple ``with``
statements at the AST level, thus allowing the semantics of actual nested
``with`` statements to be reproduced accurately. However, such a change
would highlight rather than alleviate the problem the current PEP aims to
address: it would not be possible to use ``contextlib.contextmanager``to
reliably factor out such ``with`` statements, as they would exhibit exactly
the same semantic differences as are seen with the ``combined()`` context
manager in the above example.
Reference Implementation Reference Implementation
@ -186,6 +241,9 @@ References
.. [2] PEP 343: The "with" Statement .. [2] PEP 343: The "with" Statement
(http://www.python.org/dev/peps/pep-0343/) (http://www.python.org/dev/peps/pep-0343/)
.. [3] Import-style syntax to reduce indentation of nested with statements
(http://mail.python.org/pipermail/python-ideas/2009-March/003188.html)
Copyright Copyright
========= =========