Conversation
2552158 to
e083ebc
Compare
|
Continued in #275 |
e083ebc to
f1ae578
Compare
d93d341 to
5a184c7
Compare
This modifies the inheritance unit test to demonstrate the sorting issue.
1e96b24 to
07662e4
Compare
Classes in generated .pyi stubs were sorted alphabetically, causing derived classes to appear before their base classes and breaking type checkers. Three changes fix this: - Parser: use module.__dict__.items() instead of inspect.getmembers() to preserve the pybind11 registration order (definition order) - Printer: replace alphabetical sort with a configurable _order_classes() dispatch supporting "definition" (default), "topological" (Kahn's algorithm ensuring bases precede derived classes), and "alphabetical" - CLI: add --sort-by option to select the class ordering strategy The topological sort ignores external bases (from other modules) and breaks ties by input position for deterministic output. Cyclic cross- references between classes (e.g. aliases, method signatures) are not inheritance cycles and are already handled by `from __future__ import annotations` in the generated stubs. Closes pybind#231 Based on the approaches in PR pybind#275 by @juelg and PR pybind#294 by @daltairwalter, informed by review feedback from @skarndev and @sizmailov. Co-Authored-By: juelg <20750040+juelg@users.noreply.github.com> Co-Authored-By: daltairwalter <daltairwalter@users.noreply.github.com> Co-Authored-By: skarndev <skarndev@users.noreply.github.com> Co-Authored-By: sizmailov <sizmailov@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
07662e4 to
13dbbf3
Compare
New `--sort-by` CLI option
Co-authored-by: Skarn <skarnproject@gmail.com>
Extend topological class ordering in the printer to account for executable class-body dependencies, not just inheritance. In addition to base-class edges, the sorter now treats class aliases and print-safe field values that reference sibling classes as runtime dependencies, deduplicates edges, and updates cycle diagnostics to reflect the broader graph. This keeps generated .pyi files valid Python without moving declarations out of class bodies, preserving the conventional stub shape that type checkers expect. Before: ```py class ParIterBase: level: int class ParticleContainer: name: str Iterator = ParIter def process(self, arg0: ParIter) -> None: ... class ParIter(ParIterBase): def __init__(self, particle_container: ParticleContainer, level: int) -> None: ... ``` After: ```py class ParIterBase: level: int class ParIter(ParIterBase): def __init__(self, particle_container: ParticleContainer, level: int) -> None: ... class ParticleContainer: name: str Iterator = ParIter def process(self, arg0: ParIter) -> None: ... ```
|
I do not think we can easily support robust |
Remove the CLI option, we cannot robustly support it with class-body dependencies.
d363ee4 to
51dfa72
Compare
In `pybind11_stubgen/parser/mixins/parse.py`, module traversal now preserves `module.__dict__` definition order for normal pybind11 exports but also appends lazily exposed members from `dir()`/`getattr()`, so the PR keeps the ordering fix without regressing PEP 562-style module attributes. In `pybind11_stubgen/printer.py`, class dependency edges now use the first identifier component of a reference, not the last. That fixes the false-local-match issue for external names like `pkg.other.Foo`, and it also makes dotted local references like `Outer.Inner` depend on `Outer`, which is the actual runtime lookup requirement.
`handle_class()` now uses a new _iter_class_members() helper that: - preserves class_.__dict__ definition order for class-local members - then appends additional visible members from dir()/getattr() so inherited or lazily exposed names are still seen That makes nested class discovery consistent with the earlier module-level fix, so the printer's topological sort now gets real definition order as its stable tie-break for nested classes too.
185a5b8 to
acca73d
Compare
virtuald
left a comment
There was a problem hiding this comment.
I'm slightly wary of having a home grown topological sort implementation here without a thorough test of the algorithm directly (would probably just vendor toposort if I was making this PR). But, it all looks right, so I guess that's fine.
|
Good point, we could depend on the |
This modifies the inheritance unit test to demonstrate the sorting issue in #231.
Classes in generated
.pyistubs were sorted alphabetically, causing derived classes to appear before their base classes and breaking type checkers. Likewise, class-body dependencies were not sorted properly, creating invalid Python stubs.Three changes fix this:
module.__dict__.items()instead ofinspect.getmembers()to preserve the pybind11 registration order (definition order)a configurable"topological" (Kahn's algorithm ensuring bases precede dependent (derived classes, class-body type references))_order_classes()dispatch supporting "definition" (default),, and "alphabetical"CLI: add--sort-byoption to select the class ordering strategyThe topological sort ignores external bases (from other modules) and breaks ties by input position for deterministic output. Cyclic cross-references between classes (e.g. aliases, method signatures) are no inheritance cycles and are already handled by
from __future__ import annotationsin the generated stubs.Fixes #231
Closes #294
Closes #275
Based on the approaches in PR #275 by @juelg and PR #294 by @daltairwalter, informed by review feedback from @skarndev and @sizmailov. Combined via Claude Code and reviewed via Codex, and manually.