Skip to content

experimental-inspect: dedup union members - #6273

Open
jonasdedden wants to merge 1 commit into
PyO3:mainfrom
jonasdedden:experimental-inspect-dedup-union-members
Open

experimental-inspect: dedup union members#6273
jonasdedden wants to merge 1 commit into
PyO3:mainfrom
jonasdedden:experimental-inspect-dedup-union-members

Conversation

@jonasdedden

Copy link
Copy Markdown
Contributor

#[derive(FromPyObject)] on an enum builds its INPUT_TYPE by reduce-ing the variants' own input types with |. Two ariants overlapping is normal, and the result repeats the overlap:

    enum FileInput { Path(PathBuf), Name(String), FileLike(PyFileLikeObject) }
    // file: str | os.PathLike[str] | str | Incomplete

The macro cannot collapse that - at expansion time the members are unresolved associated constants, and it has no idea PathBuf contributes a str arm. By the time pyo3-introspection renders the union it is a plain expression tree, so this flattens the | chain and drops structurally equal repeats, keeping first-occurrence order.

Additional formatting improvement: The same match arm also wrote buffer.push(' ') before the | and nothing after it, so every union came out as str |None. That is invisible in this repository because nox -s test-introspection pipes the stubs through ruff format before comparing them to the checked-in fixtures, but it is what the raw output looks like to anyone who does not format it.

@jonasdedden jonasdedden changed the title experimental-inspect dedup union members experimental-inspect: dedup union members Jul 31, 2026
@davidhewitt davidhewitt mentioned this pull request Aug 3, 2026
8 tasks

@davidhewitt davidhewitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. Overall seems like the right implementation but I'd prefer the AI clutter thinned down please.

Comment thread pyo3-introspection/src/stubs.rs Outdated
Comment on lines +483 to +488
// A union is built by `reduce`-ing over the members of an enum or the arms of a
// conversion, each contributing whatever its own `INPUT_TYPE` says. Two of them
// saying the same thing is normal — `PathBuf` contributes `str | os.PathLike[str]`
// and `String` contributes `str` — and repeating a member in a written-out union is
// just noise. The macro cannot collapse it: at that point the members are still
// unresolved associated constants.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is clearly AI comment which is is overkill here

Suggested change
// A union is built by `reduce`-ing over the members of an enum or the arms of a
// conversion, each contributing whatever its own `INPUT_TYPE` says. Two of them
// saying the same thing is normal — `PathBuf` contributes `str | os.PathLike[str]`
// and `String` contributes `str` — and repeating a member in a written-out union is
// just noise. The macro cannot collapse it: at that point the members are still
// unresolved associated constants.
// Union deduplication needs to happen here because the macro
// generation only sees unresolved associated constants.

Comment thread pyo3-introspection/src/stubs.rs Outdated
/// `A | (B | C)` and `(A | B) | C` both yield `[A, B, C]`. Every operand of a type union is an
/// atom, a subscript or an attribute, all of which bind tighter than `|`, so re-emitting the
/// flattened list separated by `|` is the same expression.
fn flatten_union<'a>(expr: &'a Expr, operands: &mut Vec<&'a Expr>) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make flatten_union also take the "seen" set and do deduplication at the same time?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, thanks, now it's better

Comment thread pyo3-introspection/src/stubs.rs Outdated
Comment on lines +981 to +982
/// A union built by `reduce`-ing over enum variants repeats whatever two variants have in
/// common — `PathBuf` contributes `str | os.PathLike[str]` and `String` contributes `str`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// A union built by `reduce`-ing over enum variants repeats whatever two variants have in
/// common — `PathBuf` contributes `str | os.PathLike[str]` and `String` contributes `str`.

Comment thread pyo3-introspection/src/stubs.rs Outdated
functions: Vec::new(),
attributes: vec![Attribute {
name: "VALUE".into(),
// `str | os.PathLike[str] | str`, nested to the right the way `reduce` builds it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// `str | os.PathLike[str] | str`, nested to the right the way `reduce` builds it.
// `str | os.PathLike[str] | str`, nested to the right

Comment thread pyo3-introspection/src/stubs.rs Outdated
right: Box::new(right),
};

let module = Module {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good enough to just test serialize_expr here and skip the module boilerplate?

@jonasdedden
jonasdedden force-pushed the experimental-inspect-dedup-union-members branch from f24b2b5 to d71ee93 Compare August 5, 2026 19:37
@jonasdedden

Copy link
Copy Markdown
Contributor Author

Addressed your comments (thanks @davidhewitt and sorry for the verbosity again; was pushing a lot of PRs at one time..). Now the PR hopefully should be a lot cleaner.

@jonasdedden

jonasdedden commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Regarding the clippy/aarch64-unknown-linux-gnu/stable failing pyo3-ffi-check, I think this is because CI now used 3.14.7?

pyo3-ffi-check/macro/src/lib.rs:432 asserts that Py_CompileStringFlags is macro-only on every non-PyPy CPython below 3.15. CPython commit 6374772d8 (python/cpython@6374772d8) (gh-152132, 2026-07-10) added a real declaration alongside the macro:

+PyAPI_FUNC(PyObject *) Py_CompileStringFlags(
+    const char *str, const char *filename, int start, PyCompilerFlags *flags);

It was backported to both 3.14 and 3.13, and shipped in CPython 3.14.7.

@jonasdedden
jonasdedden force-pushed the experimental-inspect-dedup-union-members branch from c0e79ca to 788378e Compare August 7, 2026 10:33
@jonasdedden

Copy link
Copy Markdown
Contributor Author

Rebased the branch ontop of your fix, it should hopefully work now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants