[Python-Dev] PEP 343 and with (original) (raw)
Jason Orendorff jason.orendorff at gmail.com
Tue Oct 4 16:38:49 CEST 2005
- Previous message: [Python-Dev] PEP 343 and __with__
- Next message: [Python-Dev] PEP 343 and __with__
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
The argument I am going to try to make is that Python coroutines need a more usable API.
Try to explain the semantics of the with statement without referring to the enter and exit methods, and then see if you still think they're superfluous ;)
The @contextmanager generator decorator is just syntactic sugar [...] [T]he semantics of the with statement itself can only be explained in terms of the enter and exit methods.
That's not true. It can certainly use the coroutine API instead.
Now... as specified in PEP 342, the coroutine API can be used to implement 'with', but it's ugly. I think this is a problem with the coroutine API, not the idea of using coroutines per se. Actually I think 'with' is a pretty tame use case for coroutines. Other Python objects (dicts, lists, strings) have convenience methods that are strictly redundant but make them much easier to use. Coroutines should, too.
This:
with EXPR as VAR:
BLOCK
expands to this under PEP 342:
_cm = contextmanager(EXPR)
VAR = _cm.next()
try:
BLOCK
except:
try:
_cm.throw(*sys.exc_info())
except:
pass
raise
finally:
try:
_cm.next()
except StopIteration:
pass
except:
raise
else:
raise RuntimeError
Blah. But it could look like this:
_cm = (EXPR).__with__()
VAR = _cm.start()
try:
BLOCK
except:
_cm.throw(*excinfo)
else:
_cm.finish()
I think that looks quite nice.
Here is the proposed specification for start() and finish():
class coroutine: # pseudocode
...
def start(self):
""" Convenience method -- exactly like next(), but
assert that this coroutine hasn't already been started.
"""
if self.__started:
raise ValueError # or whatever
return self.next()
def finish(self):
""" Convenience method -- like next(), but expect the
coroutine to complete without yielding again.
"""
try:
self.next()
except (StopIteration, GeneratorExit):
pass
else:
raise RuntimeError("coroutine didn't finish")
Why is this good?
- Makes coroutines more usable for everyone, not just for implementing 'with'.
- For example, if you want to feed values to a coroutine, call start() first and then send() repeatedly. Quite sensible.
- Single mental model for 'with' (always uses a coroutine or lookalike object).
- No need for "contextmanager" wrapper.
- Harder to implement a context manager object incorrectly (it's quite easy to screw up with begin and end).
-j
- Previous message: [Python-Dev] PEP 343 and __with__
- Next message: [Python-Dev] PEP 343 and __with__
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]