Skip to content

experimental-inspect: re-export submodules in the parent module stubs - #6360

Open
jonasdedden wants to merge 2 commits into
PyO3:mainfrom
jonasdedden:introspection-submodule-declarations
Open

experimental-inspect: re-export submodules in the parent module stubs#6360
jonasdedden wants to merge 2 commits into
PyO3:mainfrom
jonasdedden:introspection-submodule-declarations

Conversation

@jonasdedden

@jonasdedden jonasdedden commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What was wrong

add_module_stub_files writes each submodule to its own sibling .pyi, but module_stubs never referenced those files from the parent __init__.pyi. At runtime #[pymodule_export] use child; calls add, which is a setattr, so the submodule is an attribute of its parent. Stub and runtime disagreed and stubtest reported pyo3_pytests.<name> is not present in stub for all 17 submodules. The module-level __getattr__ from incomplete does not suppress these.

The fix

In Imports::create, when the module is a package, emit one from . import a as a, b as b line with the names sorted.

The redundant as alias follows the typeshed convention for submodule re-exports, for example os/__init__.pyi writing from . import path as path. Current checkers such as mypy or pyright are lenient about this, but the spec sees it as required.

Submodule names are also chained into the local-elements seed of local_name_to_module_and_attribute, so an annotation import that wants the same local name gets aliased instead of silently shadowing the submodule binding.

. is correct rather than .. because only packages emit this line, and a package's own stub is always the __init__.pyi sitting beside its submodule .pyi files.

@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, something seems off here

Comment on lines +8 to +24
annotations as annotations,
awaitable as awaitable,
buf_and_str as buf_and_str,
comparisons as comparisons,
consts as consts,
datetime as datetime,
dict_iter as dict_iter,
enums as enums,
exception as exception,
misc as misc,
objstore as objstore,
othermod as othermod,
path as path,
pyclasses as pyclasses,
pyfunctions as pyfunctions,
sequence as sequence,
subclassing as subclassing,

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 are there identity renames here?

@jonasdedden jonasdedden Aug 31, 2026

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.

Please note that this is one of the things already explained in the PR description:

The redundant as alias follows the typeshed convention for submodule re-exports, for example os/__init__.pyi writing from . import path as path. Current checkers such as mypy or pyright are lenient about this, but the spec sees it as required.

The import X as X way is to signal to type-checkers that an import is explicitly meant as a re-export (and not just a necessary import to make some internal functionality work). The typing spec defines it here: https://typing.python.org/en/latest/spec/distributing.html#import-conventions

The example described in the PR description is this one: https://github.com/python/typeshed/blob/f40e0da70e10e818692c3a771d5d5ac7010042f9/stdlib/os/__init__.pyi#L49

Note that there commonly are actually two ways of explicitly re-exporting symbols, either through import X as X or by including the re-export in the __all__ list. The latter shall only be used for re-exports if you do a from X import * import and have to select which symbols of X to re-export. __all__ is typically reserved for symbols defined in the same module.

FYI, there is actually a PEP that tries to clean up this mess by introducing an export keyword: https://peps.python.org/pep-0843/

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