PyO3 / PyO3/pyo3

[Stubs] Inner classes extending outer for data-carrying enums

Open
#6,163 5 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
16.2k
Forks
1k
Avg merge
2d 6h
Merged PRs (30d)
66

Description

Bug Description

Hi all!

I have the following enum in my Rust code:

/// An expectation instead of what was found
#[pyclass(eq, from_py_object, module = "scarf_python")]
#[derive(Clone, PartialEq, Eq)]
pub enum Expectation {
    /// A specific token
    Token { token: Token },
    /// A verbose human-readable label
    Label { label: String },
    /// The end of a file
    EOI(),
}

When I attempt to generate stubs for this, I get the following:

class Expectation:
    """
    An expectation instead of what was found
    """
    def __eq__(self, /, other: object) -> bool: ...
    def __ne__(self, /, other: object) -> bool: ...
    @final
    class EOI(Expectation):
        """
        The end of a file
        """
        __match_args__: Final = ()
        def __getitem__(self, /, key: int) -> Any: ...
        def __len__(self, /) -> int: ...
        def __new__(cls, /) -> Expectation.EOI: ...
    @final
    class Label(Expectation):
        """
        A verbose human-readable label
        """
        __match_args__: Final = ("label",)
        def __new__(cls, /, label: str) -> Expectation.Label: ...
        @property
        def label(self, /) -> str: ...
    @final
    class Token(Expectation):
        """
        A specific token
        """
        __match_args__: Final = ("token",)
        def __new__(cls, /, token: Token) -> Expectation.Token: ...
        @property
        def token(self, /) -> Token: ...

In Python, inner classes cannot extend outer classes, as the outer isn't fully elaborated by the time the inner is encountered. The easiest way to see this is trying to run python on the following:

class Outer:
    class Inner(Outer):
        pass

My proposed fix for the above would just be to avoid nesting the inner classes; since they already extend the parent, they already have access to all methods/attributes/etc. The simplest change that I believe would fix this would be to remove the replace call here. However, I don't know what other changes I'd need to make to the actual generated code, beyond the stubs.

I believe similar issues would be present when generating stubs for any data-carrying enums.

Steps to Reproduce

My particular steps to encounter this involved trying to document the generated stubs (above) with sphinx-autodoc2; can provide issues to reproduce if needed (may be convoluted :), but I believe the issue is already identified

Backtrace

Your operating system and version

MacOS Monterey (12.6.5)

Your Python version (python --version)

Python 3.14.2

Your Rust version (rustc --version)

rustc 1.93.1 (01f6ddf75 2026-02-11)

Your PyO3 version

0.29.0

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

virtualenv

Additional Info

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading pyo3-introspection/src/stubs.rs around the linked replace call and reproduce the nested-class output with the shown Expectation enum. Verify the generated stubs are valid Python and that data-carrying enums no longer produce inner classes extending an incompletely defined outer class.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
developer-experience, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.