This bug report is the opposite of issue #14718. The interpreter did not raise an error when it encountered a `yield` expression inside the `finally` part of a `try/finally` statement.
My system's info: Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32
More detail
=======================================================
My interpreter does not raise any errors when yielding from a `finally` block. The documentation states, "Yield expressions are allowed in the try clause of a try ... finally construct."[1] Though not explicitly stated, the suggestion is that `yield` is not allowed in the `finally` clause. Issue #14718 suggests that this interpretation is correct.
Not raising an exception for `yield` inside of `finally` can cause incorrect behavior when the generator raises its own unhandled exception in the `try` block. PEP 342 says, "If the generator raises an uncaught exception, it is propagated to send()'s caller." In this case, however, the exception gets paused at the `yield` expression, instead of propagating to the caller.
Here's an example that can clarify the issue:
>>> def f(): # I expected this function not to compile
... try:
... raise ValueError
... finally:
... yield 1 # Execution freezes here instead of propagating the ValueError
... yield "done"
...
>>> g = f()
>>> g.send(None) # PEP 342 would require ValueError to be raised here
1
>>> g.send(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f2
ValueError
I may be arguing over the meaning of "uncaught exception," but I expected (given that the function compiles and doesn't raise a `RuntimeError`) the `ValueError` to propagate rather than get frozen.
Example 2
-------------------------------------------------------
The second example is adapted from http://bugs.python.org/issue14718, where the submitter received a `RuntimeError`, but I did not:
>>> def f2(): # I also expected this function not to compile
... try:
... yield 1
... finally:
... yield 4
...
>>> g = f() # issue 14718 suggests this should raise RuntimeError
>>> next(g)
1
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Possible resolution:
=========================================================
1. Enforce the ban on `yield` inside a finally `clause`. It seems like this should
already be happening, so I'm not sure why my version isn't performing the check.
This could be a run-time check (which seems like it may already be implemented),
but I think this could even be a compile-time check (by looking at the AST).
2. Clarify the documentation to make explicit the ban on the use of `yield` inside
the `finally` clause, and specify what type of error it will raise (i.e.,
`SyntaxError` or `RuntimeError`? or something else?).
I'll submit a patch for 2 if there's support for this change, and I will work on 1 if someone can point me in the right direction. I've engaged with the C source relating to the different protocols, but have never looked through the compiler/VM.
Notes
============================================
[1] https://docs.python.org/3.4/reference/expressions.html#yield-expressions
|