Message287987
Example 1:
-----------
def f():
try:
raise KeyError
except Exception:
yield
gen = f()
gen.send(None)
gen.throw(ValueError)
---------
Output:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in f
ValueError
Expected output:
Something involving the string "During handling of the above exception, another exception occurred", and a traceback for the original KeyError.
Example 2:
-----------
import sys
def f():
try:
raise KeyError
except Exception:
print(sys.exc_info())
try:
yield
except Exception:
pass
print(sys.exc_info())
raise
gen = f()
gen.send(None)
gen.throw(ValueError)
-----------
Output:
(<class 'KeyError'>, KeyError(), <traceback object at 0x7f67ce3c3f88>)
(None, None, None)
Traceback (most recent call last):
File "/tmp/foo.py", line 17, in <module>
gen.throw(ValueError)
File "/tmp/foo.py", line 13, in f
raise
RuntimeError: No active exception to reraise
Expected output: certainly not that :-)
This seems to happen because normally, generators save the current exc_info when yielding, and then restore it when re-entered. But, if we re-enter through 'throw' (throwflag is true), this is disabled:
https://github.com/python/cpython/blob/b2ee40ed9c9041dcff9c898aa19aacf9ec60308a/Python/ceval.c#L1027
This check seems to have been added in ae5f2f4a39e6a3f4c45e9dc95bd4e1fe5dfb60f2 as a fix for:
https://bugs.python.org/issue7173
which had to do with a nasty situation involving a generator object that was part of a reference cycle: the gc sometimes would free the objects stored in the generator's saved exc_info, and then try to clean up the generator by throwing in a GeneratorExit.
AFAICT this situation shouldn't be possible anymore thanks to PEP 442, which makes it so that finalizers are run before any part of the cycle is freed. And in any case it certainly doesn't justify breaking code like the examples above.
(Note: the examples use generators for simplicity, but of course the way I noticed this was that I had some async/await code where exceptions were mysteriously disappearing instead of showing up in __context__ and couldn't figure out why. It's likely that more people will run into this in the future as async/await becomes more widely used. As a workaround for now I'll probably modify my coroutine runner so that it never uses 'throw'.) |
|
Date |
User |
Action |
Args |
2017-02-17 11:01:49 | njs | set | recipients:
+ njs |
2017-02-17 11:01:49 | njs | set | messageid: <1487329309.81.0.182915384821.issue29587@psf.upfronthosting.co.za> |
2017-02-17 11:01:49 | njs | link | issue29587 messages |
2017-02-17 11:01:48 | njs | create | |
|