Created on 2017-02-17 11:01 by njs, last changed 2017-02-17 11:49 by martin.panter.
|msg287987 - (view)||Author: Nathaniel Smith (njs) *||Date: 2017-02-17 11:01|
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'.)
|msg287992 - (view)||Author: Martin Panter (martin.panter) *||Date: 2017-02-17 11:49|
The second example seems like the original complaint in Issue 25612. That spawned a separate bug related to the first situation, which was later closed: Issue 25683.
|2017-02-17 11:49:20||martin.panter||set||type: behavior|
messages: + msg287992
nosy: + martin.panter