Add except* support by youknowone · Pull Request #6530 · RustPython/RustPython
🏁 Script executed:
# First, examine the prep_reraise_star and is_same_exception_metadata implementations
wc -l crates/vm/src/exceptions.rsRepository: RustPython/RustPython
Length of output: 97
🏁 Script executed:
# Check the is_same_exception_metadata function to understand the "fragile membership check" claim rg -A 20 "fn is_same_exception_metadata" crates/vm/src/exceptions.rs
Repository: RustPython/RustPython
Length of output: 789
🏁 Script executed:
# Look for tests related to prep_reraise_star and exception groups fd -e rs -e py . crates/ | xargs grep -l "prep_reraise_star\|PrepReraiseStar" 2>/dev/null
Repository: RustPython/RustPython
Length of output: 154
🏁 Script executed:
# Search for tests in the test directory or within exceptions.rs rg -n "test.*except\*\|test.*reraise" crates/vm/src/exceptions.rs | head -20
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check exception_group.rs to understand the design decisions mentioned in the learnings cat -n crates/vm/src/exception_group.rs | sed -n '170,190p'
Repository: RustPython/RustPython
Length of output: 881
🏁 Script executed:
# Read the full prep_reraise_star implementation to see the context sed -n '2493,2538p' crates/vm/src/exceptions.rs
Repository: RustPython/RustPython
Length of output: 1587
🏁 Script executed:
# Look at the complete function after line 2538 (mentioned as "Also applies to: 2540-2558") sed -n '2530,2560p' crates/vm/src/exceptions.rs
Repository: RustPython/RustPython
Length of output: 1209
🏁 Script executed:
# Search for subgroup method to understand how it works rg -A 15 "fn subgroup\|pub fn subgroup" crates/
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Look for tests related to except* and exception groups find crates tests -name "*.rs" -o -name "*.py" | xargs grep -l "except\*\|ExceptionGroup" 2>/dev/null | head -10
Repository: RustPython/RustPython
Length of output: 333
🏁 Script executed:
# Search for test files that might test exception groups fd -e py . tests/ | xargs grep -l "except\*" 2>/dev/null | head -5
Repository: RustPython/RustPython
Length of output: 158
🏁 Script executed:
# Find test directories in the repository find . -type d -name test* 2>/dev/null | head -10
Repository: RustPython/RustPython
Length of output: 122
🏁 Script executed:
# Look at repo structure git ls-files | grep -E "test|spec" | head -20
Repository: RustPython/RustPython
Length of output: 750
🏁 Script executed:
# Search for Python test files related to exception groups find . -name "*.py" -type f 2>/dev/null | xargs grep -l "except\*\|ExceptionGroup" 2>/dev/null | head -10
Repository: RustPython/RustPython
Length of output: 109
🏁 Script executed:
# Look for subgroup implementation in exception_group.rs rg -B 5 -A 20 "fn subgroup" crates/vm/src/exception_group.rs
Repository: RustPython/RustPython
Length of output: 999
🏁 Script executed:
# Check CPython's _PyExc_PrepReraiseStar to understand the expected behavior
web_search
CPython _PyExc_PrepReraiseStar implementation nested exception groupsRepository: RustPython/RustPython
Length of output: 161
🌐 Web query:
CPython _PyExc_PrepReraiseStar implementation exception groups
💡 Result:
Short answer — what _PyExc_PrepReraiseStar does and why
-
_PyExc_PrepReraiseStar is an internal CPython helper used when re‑raising an exception group with the "raise " (reraise‑star) semantics introduced by PEP 654. Its job is to prepare a value suitable for re‑raising from an original exception object and a sequence (or group) of nested exceptions: normalizing/constructing the appropriate ExceptionGroup/BaseExceptionGroup object, and making sure context/cause/traceback behavior matches the semantics PEP 654 requires for except / reraise-star. [1][2]
-
The function is declared in CPython’s internal pycore pyerrors header as:
PyObject* _PyExc_PrepReraiseStar(PyObject *orig, PyObject *excs);
(where orig is the original exception object and excs is the sequence / group of exceptions to be reraised). [2] -
Conceptually it implements the re‑raise semantics described in PEP 654: when an except* handler yields a subset of a group (or re‑raises parts), CPython must produce a (possibly new) exception group that preserves appropriate metadata (message, cause/context, and composed tracebacks for leaf exceptions) and obeys ephemeral/handler semantics documented in PEP 654. _PyExc_PrepReraiseStar centralizes those preparation steps before the interpreter actually raises the resulting exception object. [1]
References
- PEP 654 — Exception Groups and except*. [1]
- CPython internal declaration (pycore pyerrors.h) showing _PyExc_PrepReraiseStar. [2]
🏁 Script executed:
# Check the full subgroup implementation rg -B 5 -A 40 "fn subgroup" crates/vm/src/exception_group.rs
Repository: RustPython/RustPython
Length of output: 1625
🏁 Script executed:
# Check for exception group tests in the test directory find Lib/test -name "*.py" | xargs grep -l "except\*\|ExceptionGroup" 2>/dev/null | head -10
Repository: RustPython/RustPython
Length of output: 108
🏁 Script executed:
# Look at the builtin_exceptions.py test file cat extra_tests/snippets/builtin_exceptions.py | head -100
Repository: RustPython/RustPython
Length of output: 2105
🏁 Script executed:
# Search for prep_reraise_star usage and tests rg -B 5 -A 10 "prep_reraise_star" crates/
Repository: RustPython/RustPython
Length of output: 2371
🏁 Script executed:
# Search for tests related to except* more thoroughly find . -type f \( -name "*.py" -o -name "*.rs" \) | xargs grep -l "except\s*\*" 2>/dev/null | head -20
Repository: RustPython/RustPython
Length of output: 235
🏁 Script executed:
# Look at the compiler to see how except* is handled and what excs contains rg -B 10 -A 10 "PrepReraiseStar\|prep_reraise_star" crates/compiler-core/ 2>/dev/null
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check if there are any tests files in extra_tests find extra_tests -name "*.py" | xargs grep -l "except\s*\*\|ExceptionGroup" 2>/dev/null
Repository: RustPython/RustPython
Length of output: 107
🏁 Script executed:
# Look at the derive method in exception_group.rs to understand the design decision mentioned in learnings rg -B 5 -A 30 "#\[pymethod\].*fn derive" crates/vm/src/exception_group.rs
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Search for derive method in exception_group.rs with different pattern rg -B 3 -A 25 "fn derive" crates/vm/src/exception_group.rs
Repository: RustPython/RustPython
Length of output: 1931
🏁 Script executed:
# Check builtin_exceptions.py for except* tests grep -n "except\s*\*\|ExceptionGroup" extra_tests/snippets/builtin_exceptions.py | head -20
Repository: RustPython/RustPython
Length of output: 255
🏁 Script executed:
# Read the full builtin_exceptions.py to see if there are exception group tests
wc -l extra_tests/snippets/builtin_exceptions.pyRepository: RustPython/RustPython
Length of output: 111
🏁 Script executed:
# Check what's in the codegen compile.rs regarding except* handling rg -B 5 -A 15 "except\s*\*" crates/codegen/src/compile.rs | head -60
Repository: RustPython/RustPython
Length of output: 2105
🏁 Script executed:
# Look for how exceptions are handled in except* blocks to understand what excs contains rg -B 10 -A 10 "except_star\|except\*" crates/compiler-core/src/bytecode.rs | head -100
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Read more of the builtin_exceptions.py test to see what except* coverage exists sed -n '371,390p' extra_tests/snippets/builtin_exceptions.py
Repository: RustPython/RustPython
Length of output: 409
🏁 Script executed:
# Search for more except* test cases rg -B 3 -A 10 "except\s*\*" extra_tests/snippets/builtin_exceptions.py
Repository: RustPython/RustPython
Length of output: 451
🏁 Script executed:
# Look at what CheckEgMatch does and how it populates excs for prep_reraise_star rg -B 5 -A 15 "CheckEgMatch" crates/vm/src/
Repository: RustPython/RustPython
Length of output: 1538
🏁 Script executed:
# Understand the flow: check bytecode for how excs list is built before calling prep_reraise_star rg -B 20 "PrepReraiseStar" crates/compiler-core/src/bytecode.rs | head -80
Repository: RustPython/RustPython
Length of output: 619
🏁 Script executed:
# Look at how the compiler generates the excs list that gets passed to prep_reraise_star rg -B 15 -A 5 "PrepReraiseStar\|prep_reraise" crates/codegen/src/compile.rs | head -100
Repository: RustPython/RustPython
Length of output: 47
prep_reraise_star and exception grouping behavior need verification against complex PEP 654 scenarios
To improve confidence, add tests for nested groups and complex except* patterns (matching the PEP 654 examples), then compare behavior against CPython. The design decision to always create BaseExceptionGroup (rather than preserving the original exception class) is intentional per prior decisions, but the exception grouping logic itself should be validated against CPython's behavior for correctness.