Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gen.throw() with multiple yield froms skips intermediate exceptions #84871

Open
cjerdonek opened this issue May 20, 2020 · 2 comments
Open

gen.throw() with multiple yield froms skips intermediate exceptions #84871

cjerdonek opened this issue May 20, 2020 · 2 comments
Labels
3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs)

Comments

@cjerdonek
Copy link
Member

BPO 40694
Nosy @cjerdonek

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2020-05-20.04:47:05.315>
labels = ['interpreter-core', '3.10']
title = 'gen.throw() with multiple yield froms skips intermediate exceptions'
updated_at = <Date 2020-05-20.04:47:05.315>
user = 'https://github.com/cjerdonek'

bugs.python.org fields:

activity = <Date 2020-05-20.04:47:05.315>
actor = 'chris.jerdonek'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Interpreter Core']
creation = <Date 2020-05-20.04:47:05.315>
creator = 'chris.jerdonek'
dependencies = []
files = []
hgrepos = []
issue_num = 40694
keywords = []
message_count = 1.0
messages = ['369416']
nosy_count = 1.0
nosy_names = ['chris.jerdonek']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue40694'
versions = ['Python 3.10']

@cjerdonek
Copy link
Member Author

Here is another gen.throw() exception chain example similar to the examples in bpo-29587: https://bugs.python.org/issue29587

def f():
    yield

def g():
    try:
        raise RuntimeError('a')
    except Exception as exc:
        print(f'handling: {exc!r}')
        yield from f()

def h():
    try:
        raise RuntimeError('b')
    except Exception as exc:
        print(f'handling: {exc!r}')
        yield from g()

gen = h()
gen.send(None)
gen.throw(ValueError)

Output:

handling: RuntimeError('b')
handling: RuntimeError('a')
Traceback (most recent call last):
  File "/.../test.py", line 13, in h
    raise RuntimeError('b')
RuntimeError: b

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/.../test.py", line 20, in <module>
    gen.throw(ValueError)
  File "/.../test.py", line 16, in h
    yield from g()
  File "/.../test.py", line 9, in g
    yield from f()
  File "/.../test.py", line 2, in f
    yield
ValueError

The issue is that "RuntimeError: a" is skipped. It should also appear in the exception chain.

I believe this has the same root cause as bpo-29590: https://bugs.python.org/issue29590

@cjerdonek cjerdonek added 3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) labels May 20, 2020
@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@gschaffner
Copy link

calling outer, middle, inner = h, g, f for clarity, it looks like at the end of yield from in outer(), exc_info[1].__context__ is getting reset to RuntimeError("raised by outer") when it should remain as RuntimeError("raised by middle"):

click to expand
import sys


def outer():
    try:
        raise RuntimeError("raised by outer")
    except Exception:
        try:
            yield from middle()
        finally:
            val_err = sys.exc_info()[1]
            assert isinstance(val_err, ValueError)
            print(
                "outer:"
                f"\n\t{val_err.__context__=} (should be RE('raised by middle'))"
                f"\n\t{val_err.__context__.__context__=} (should be RE('raised by outer'))"
            )


def middle():
    try:
        raise RuntimeError("raised by middle")
    except Exception:
        try:
            yield from inner()
        finally:
            val_err = sys.exc_info()[1]
            assert isinstance(val_err, ValueError)
            print(
                "middle:"
                f"\n\t{val_err.__context__=} (should be RE('raised by middle'))"
                f"\n\t{val_err.__context__.__context__=} (should be RE('raised by outer'))"
            )


def inner():
    yield


gen = outer()
gen.send(None)
gen.throw(ValueError)

output:

middle:
        val_err.__context__=RuntimeError('raised by middle') (should be RE('raised by middle'))
        val_err.__context__.__context__=RuntimeError('raised by outer') (should be RE('raised by outer'))
outer:
        val_err.__context__=RuntimeError('raised by outer') (should be RE('raised by middle'))
        val_err.__context__.__context__=None (should be RE('raised by outer'))
Traceback (most recent call last):
  File "/.../eg.py", line 6, in outer
    raise RuntimeError("raised by outer")
RuntimeError: raised by outer

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/.../eg.py", line 42, in <module>
    gen.throw(ValueError)
  File "/.../eg.py", line 9, in outer
    yield from middle()
  File "/.../eg.py", line 25, in middle
    yield from inner()
ValueError

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs)
Projects
None yet
Development

No branches or pull requests

2 participants