Skip to content

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

Description

@Aidan-McNay

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions