[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

AltStyle によって変換されたページ (->オリジナル) /