[Python-ideas] Yielding through context managers
Terry Reedy
tjreedy at udel.edu
Fri Mar 30 03:05:42 CEST 2012
On 3/29/2012 8:00 PM, Joshua Bartlett wrote:
> I'd like to propose adding the ability for context managers to catch and
> handle control passing into and out of them via yield and
> generator.send() / generator.next().
>
> For instance,
>
> class cd(object):
> def __init__(self, path):
> self.inner_path = path
>
> def __enter__(self):
> self.outer_path = os.getcwd()
> os.chdir(self.inner_path)
>
> def __exit__(self, exc_type, exc_val, exc_tb):
> os.chdir(self.outer_path)
>
> def __yield__(self):
> self.inner_path = os.getcwd()
> os.chdir(self.outer_path)
>
> def __send__(self):
> self.outer_path = os.getcwd()
> os.chdir(self.inner_path)
>
> Here __yield__() would be called when control is yielded through the
> with block and __send__() would be called when control is returned via
> .send() or .next(). To maintain compatibility, it would not be an error
> to leave either __yield__ or __send__ undefined.
This strikes me as the wrong solution to the fragility of dubious code.
The context manager protocol is simple: two special methods. Ditto for
the iterator protocol. The generator protocol has been complexified; not
good, but there are benefits and the extra complexity can be ignored.
But I would be reluctant to complexify the cm protocol. This is aside
from technical difficulties.
> The rationale for this is that it's sometimes useful for a context
> manager to set global or thread-global state as in the example above,
> but when the code is used in a generator, the author of the generator
> needs to make assumptions about what the calling code is doing. e.g.
>
> def my_generator(path):
> with cd(path):
> yield do_something()
> do_something_else()
Pull the yield out of the with block.
def my_gen(path):
with cd(path):
directory = <read directory>
yield do_something(directory)
do_else(directory)
or
def my_gen(p):
with cd(p):
res = do_something()
yield res
with cd(p):
do_else()
Use same 'result' trick if do_else also yields.
> Even if the author of this generator knows what effect do_something()
> and do_something_else() have on the current working directory, the
> author needs to assume that the caller of the generator isn't touching
> the working directory. For instance, if someone were to create two
> my_generator() generators with different paths and advance them
> alternately, the resulting behaviour could be most unexpected. With the
> proposed change, the context manager would be able to handle this so
> that the author of the generator doesn't need to make these assumptions.
Or make with manipulation of global resources self-contained, as
suggested above and as intended for with blocks.
--
Terry Jan Reedy
More information about the Python-ideas
mailing list