[New-bugs-announce] [issue22988] No error when yielding from `finally`

Felipe report at bugs.python.org
Wed Dec 3 18:51:14 CET 2014


New submission from Felipe:

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

----------
components: Interpreter Core
messages: 232081
nosy: fov
priority: normal
severity: normal
status: open
title: No error when yielding from `finally`
type: behavior
versions: Python 3.4

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue22988>
_______________________________________


More information about the New-bugs-announce mailing list