
Why can't generator.close() return the value if a StopIteration is raised? The PEPs mentioned that it was proposed before, but I can't find any definitive reason, and it's terribly convenient if it does.

Matt Joiner wrote:
Why can't generator.close() return the value if a StopIteration is raised?
No reason as far as I can see. The semantics are clear enough. From an implementation point of view it would be a simple patch.
The PEPs mentioned that it was proposed before, but I can't find any definitive reason, and it's terribly convenient if it does.
I'm sure it is convenient, but I believe it is convention to provide a use case ;) Cheers, Mark.

Hello Matt On 04/11/2012 11:38 AM, Matt Joiner wrote:
Why can't generator.close() return the value if a StopIteration is raised?
The PEPs mentioned that it was proposed before, but I can't find any definitive reason, and it's terribly convenient if it does.
What should be returned when you call close on an already-exhausted generator? You can't return the value of the final StopIteration unless you arrange to have that value stored somewhere. Storing the value was deemed undesireable by the powers that be. The alternative is to return None if the generator is already exhausted. That would work, but severely reduces the usefulness of the change. If you don't care about the performance of yield-from, it is quite easy to write a class you can use to wrap your generator-iterators and get the desired result (see untested example below). - Jacob import functools class generator_result_wrapper(object): __slots__ = ('_it', '_result') def __init__(self, it): self._it = it def __iter__(self): return self def next(self): try: return self._it.next() except StopIteration as e: self._result = e.result raise def send(self, value): try: return self._it.send(value) except StopIteration as e: self._result = e.result raise def throw(self, *args, **kwargs): try: return self._it.throw(*args, **kwargs) except StopIteration as e: self._result = e.result raise def close(self): try: return self._result except AttributeError: pass try: self._it.throw(GeneratorExit) except StopIteration as e: self._result = e.result return self._result except GeneratorExit: pass def close_result(func): @functools.wraps(func) def factory(*args, **kwargs): return generator_result_wrapper(func(*args, **kwargs)) return factory

You make an excellent point. I'm inclined to agree with you. Cheers On Apr 11, 2012 6:50 PM, "Jacob Holm" <jh@improva.dk> wrote:
Hello Matt
On 04/11/2012 11:38 AM, Matt Joiner wrote:
Why can't generator.close() return the value if a StopIteration is raised?
The PEPs mentioned that it was proposed before, but I can't find any definitive reason, and it's terribly convenient if it does.
What should be returned when you call close on an already-exhausted generator?
You can't return the value of the final StopIteration unless you arrange to have that value stored somewhere. Storing the value was deemed undesireable by the powers that be.
The alternative is to return None if the generator is already exhausted. That would work, but severely reduces the usefulness of the change.
If you don't care about the performance of yield-from, it is quite easy to write a class you can use to wrap your generator-iterators and get the desired result (see untested example below).
- Jacob
import functools
class generator_result_wrapper(**object): __slots__ = ('_it', '_result')
def __init__(self, it): self._it = it
def __iter__(self): return self
def next(self): try: return self._it.next() except StopIteration as e: self._result = e.result raise
def send(self, value): try: return self._it.send(value) except StopIteration as e: self._result = e.result raise
def throw(self, *args, **kwargs): try: return self._it.throw(*args, **kwargs) except StopIteration as e: self._result = e.result raise
def close(self): try: return self._result except AttributeError: pass try: self._it.throw(GeneratorExit) except StopIteration as e: self._result = e.result return self._result except GeneratorExit: pass
def close_result(func): @functools.wraps(func) def factory(*args, **kwargs): return generator_result_wrapper(func(***args, **kwargs)) return factory

On Wed, Apr 11, 2012 at 9:28 PM, Matt Joiner <anacrolix@gmail.com> wrote:
You make an excellent point. I'm inclined to agree with you.
While Jacob does make a valid point about the question of what to do when close() is called multiple times (or on a generator that has already been exhausted through iteration), the specific reason that got the idea killed in PEP 380 [1] is that generators shouldn't be raising StopIteration as a result of a close() invocation anyway - they should usually just reraise the GeneratorExit that gets thrown in to finalise the generator body. If inner generators start making a habit of converting GeneratorExit to StopIteration, then intervening "yield from" operations may yield another value instead of terminating the way they're supposed to in response to close(). Cheers, Nick. [1] http://www.python.org/dev/peps/pep-0380/#rejected-ideas -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (4)
-
Jacob Holm
-
Mark Shannon
-
Matt Joiner
-
Nick Coghlan