[Python-Dev] patch: try/finally in generators
Guido van Rossum
guido@python.org
Mon, 29 Jul 2002 16:30:36 -0400
> I'm pretty sure it can be made to work (at least for CPython). The
> proposed patch is not correct since it doesn't handle "finally" code
> that creates a new reference to the generator.
As Oren pointed out, how can you create a reference to the generator
when its reference count was 0? There can't be a global referencing
it, and (unlike __del__) you aren't getting a pointer to yourself.
> Also, setting the instruction pointer to the return statement is
> really ugly, IMO.
Agreed. ;-)
> There could be valid code out there that does not end with
> LOAD_CONST+RETURN.
The current code generator always generates that as the final
instruction. But someone might add an optimizer that takes that out
if it is provably unreachable...
> Those are minor details though. We need to decide if we really want
> this. For example, what happens if 'yield' is inside the finally block?
> With the proposed patch:
>
> >>> def f():
> ... try:
> ... assert 0
> ... finally:
> ... return 1
> ...
> >>> f()
> 1
> >>> def g():
> ... try:
> ... assert 0
> ... finally:
> ... yield 1
> ...
> >>> list(g())
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 3, in g
> AssertionError
>
> Maybe some people whould expect [1] in the second case.
The latter is not new; that example has no yield in the try clause.
If you'd used a for loop or next() calls, you'd have noticed the yield
got executed normally, but following next() call raises
AssertionError.
But this example behaves strangely:
>>> def f():
... try:
... yield 1
... assert 0
... finally:
... yield 2
...
>>> a = f()
>>> a.next()
1
>>> del a
>>>
What happens at the yield here?!?! If I put prints before and after
it, the finally clause is entered, but not exited. Bizarre!!!
--Guido van Rossum (home page: http://www.python.org/~guido/)