[Python-ideas] PEP 380 close and contextmanagers?

Guido van Rossum guido at python.org
Wed Oct 27 20:38:49 CEST 2010


On Wed, Oct 27, 2010 at 9:18 AM, Ron Adam <rrr at ronadam.com> wrote:
>
>
> On 10/27/2010 10:01 AM, Ron Adam wrote:
> It looks like No context managers return values in the finally or __exit__
> part of a context manager.  Is there way to do that?

How would that value be communicated to the code containing the with-clause?

> Here's a context manager version of the min/max with nested coroutines, but
> it doesn't return a value from close.
>
>
> ######
> from contextlib import contextmanager
>
>
> # New close function that enables returning a
> # value.
>
> def gclose(gen):
>   try:
>     gen.throw(GeneratorExit)
>   except StopIteration as err:
>     if err.args:
>       return err.args[0]
>   except GeneratorExit:
>     pass
>   return None
>
>
> # Showing  both the class and geneator based
> # context managers for comparison and to better
> # see how these things may work.
>
> class Consumer:
>    def __init__(self, cofunc):
>        next(cofunc)
>        self.cofunc = cofunc
>    def __enter__(self):
>        return self.cofunc
>    def __exit__(self, *exc_info):
>        gclose(self.cofunc)
>
> @contextmanager
> def consumer(cofunc):
>    next(cofunc)
>    try:
>        yield cofunc
>    finally:
>        gclose(cofunc)
>
>
> class MultiConsumer:
>    def __init__(self, cofuncs):
>        for c in cofuncs:
>            next(c)
>        self.cofuncs = cofuncs
>    def __enter__(self):
>        return self.cofuncs
>    def __exit__(self, *exc_info):
>        for c in self.cofuncs:
>            gclose(c)
>
> @contextmanager
> def multiconsumer(cofuncs):
>    for c in cofuncs:
>        next(c)
>    try:
>        yield cofuncs
>    finally:
>        for c in cofuncs:
>            gclose(c)

So far so good.

> # Min/max coroutine example slpit into
> # nested coroutines for testing these ideas
> # in a more complex situation that may arise
> # when working with cofunctions and generators.
>
> # Question:
> #    How to rewrite this so close returns
> #    a final value?

Change the function to catch GeneratorExit and when it catches that,
raise StopIteration(<returnvalue>).

> def reduce_i(f):
>     i = yield
>     while True:
>         i = f(i, (yield i))

Unfortunately from here on till the end of your example my brain exploded.

> def reduce_it_to(funcs):
>    with multiconsumer([reduce_i(f) for f in funcs]) as mc:
>        values = None
>        while True:
>            i = yield values
>            values = [c.send(i) for c in mc]

Maybe you could have picked a better name than 'i' for this variable...

> def main():
>    with consumer(reduce_it_to([min, max])) as c:
>        for i in range(100):
>            value = c.send(i)
>        print(value)

I sort of get what you are doing here but I think you left one
abstraction out. Something like this:

def blah(it, funcs):
  with consumer(reduce_it_to(funcs) as c:
    for i in it:
      value = c.send(i)
    return value

def main():
  print(blah(range(100), [min, max]))

> if __name__ == '__main__':
>    main()

-- 
--Guido van Rossum (python.org/~guido)



More information about the Python-ideas mailing list