◐ Shell
clean mode source ↗

Add except* support by youknowone · Pull Request #6530 · RustPython/RustPython

🧩 Analysis chain

🏁 Script executed:

# First, examine the prep_reraise_star and is_same_exception_metadata implementations
wc -l crates/vm/src/exceptions.rs

Repository: 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 groups

Repository: 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.py

Repository: 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.