Skip to content

BaseException(SystemExit/KeyboardInterrupt) is swallowed and converted to a TypeError when extracting struct fields #5457

@vpoverennov

Description

@vpoverennov

Bug Description

Opening as a bug, but this might be considered a feature request

First - thank you for such a great library
We're using it to speedup some python in a web service running under gunicorn and hit a case where a SystemExit is swallowed and converted to a TypeError if it happened to be raised inside a field extractor in a struct

I believe this code is acting as a bare except: but IMO should be more like except Exception:

pub fn extract_struct_field<'a, 'py, T>(
obj: &'a Bound<'py, PyAny>,
struct_name: &str,
field_name: &str,
) -> PyResult<T>
where
T: FromPyObject<'a, 'py>,
{
match obj.extract() {
Ok(value) => Ok(value),
Err(err) => Err(failed_to_extract_struct_field(
obj.py(),
err.into(),
struct_name,
field_name,
)),
}
}

This swallows exceptions deriving from BaseException like KeyboardInterrupt (user pressing Ctrl+C) and SystemExit (used by gunicorn to signal worker shutdown - https://github.com/benoitc/gunicorn/blob/a86ea1e4e6c271d1cd1823c7e14490123f9238fe/gunicorn/workers/base.py#L204

Steps to Reproduce

Seems to only happen with nested structs, see full repro here - https://github.com/vpoverennov/pyo3-exceptions-repro/blob/main/src/lib.rs

with a Nested class like this (simulating SystemExit being thrown into an otherwise innocuous getter function)

class Nested:
    @property
    def v(self):
        raise SystemExit(1)

This works fine and SystemExit is raised when calling repro_w

#[derive(FromPyObject, Debug)]
struct Nested {
    v: i32,
}

#[pyfunction]
fn repro_n(b: Nested) -> i32 {
    println!("repro_n: {:?}", b);
    b.v
}

But this converts SystemExit into a TypeError: argument 'a': failed to extract field Wrapper.n

#[derive(FromPyObject, Debug)]
struct Wrapper {
    n: Nested,
}

#[derive(FromPyObject, Debug)]
struct Nested {
    v: i32,
}

#[pyfunction]
fn repro_w(a: Wrapper) -> i32 {
    println!("repro_w: {:?}", a);
    a.n.v
}

Backtrace

Your operating system and version

Windows 10 / MacOS

Your Python version (python --version)

Python 3.10

Your Rust version (rustc --version)

rustc 1.90.0 (1159e78c4 2025-09-14)

Your PyO3 version

0.26.0

How did you install python? Did you use a virtualenv?

not relevant

Additional Info

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions