Skip to content

experimental-inspect: no-parameter pymodule_init keeps module complete - #6271

Open
jonasdedden wants to merge 3 commits into
PyO3:mainfrom
jonasdedden:experimental-inspect-pymodule-init-keeps-module-complete
Open

experimental-inspect: no-parameter pymodule_init keeps module complete#6271
jonasdedden wants to merge 3 commits into
PyO3:mainfrom
jonasdedden:experimental-inspect-pymodule-init-keeps-module-complete

Conversation

@jonasdedden

@jonasdedden jonasdedden commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

incomplete was set to pymodule_init.is_some(), so any declarative module with an initialiser got def __getattr__(name: str) -> Incomplete: ... in its stubs. This is also relevant for #6242 where it would lead to no emission of __all__ either, breaking stubtest compliance. That flag is what makes every unknown attribute on the module resolve to Any, which is most of the value of having a stub at all.

The flag is conservative for a good reason: #[pymodule_init] receives &Bound<'_, PyModule> and can add arbitrary attributes the macro cannot see. But the common case does not want the module. pyo3_log::init() is the motivating example - it installs a global log logger and takes nothing, so it would be silly to have such a strong negative impact on typestubs:

    #[pymodule_init]
    fn init() -> PyResult<()> {
        pyo3_log::init();
        Ok(())
    }

An initialiser with no parameters cannot reach the module, so it cannot add members to it, so the module is still fully described. This is inferred from the signature rather than asserted by a new attribute: it is checked by the compiler instead of trusted.

One-argument initialisers keep today's behaviour exactly. Two or more is now a clear error instead of a confusing one from the generated call site.

…plete

`incomplete` was set to `pymodule_init.is_some()`, so any declarative module
with an initialiser got `def __getattr__(name: str) -> Incomplete: ...` in its
stubs and (since PyO3#6242) no `__all__` either. That flag is what makes every
unknown attribute on the module resolve to `Any`, which is most of the value of
having a stub at all.

The flag is conservative for a good reason: `#[pymodule_init]` receives
`&Bound<'_, PyModule>` and can add arbitrary attributes the macro cannot see.
But the common case does not want the module. `pyo3_log::init()` is the
motivating example — it installs a global `log` logger and takes nothing:

    #[pymodule_init]
    fn init() -> PyResult<()> {
        pyo3_log::init();
        Ok(())
    }

An initialiser with no parameters cannot reach the module, so it cannot add
members to it, so the module is still fully described. This is inferred from
the signature rather than asserted by a new attribute: it is checked by the
compiler instead of trusted.

One-argument initialisers keep today's behaviour exactly. Two or more is now a
clear error instead of a confusing one from the generated call site.
@jonasdedden jonasdedden changed the title experimental-inspect no-parameter pymodule init keeps module complete experimental-inspect: no-parameter pymodule init keeps module complete Jul 31, 2026
@jonasdedden jonasdedden changed the title experimental-inspect: no-parameter pymodule init keeps module complete experimental-inspect: no-parameter pymodule_init keeps module complete Jul 31, 2026
Codecov flagged three lines in `pymodule_module_impl` as uncovered: the two
`ensure_spanned!` error arms and the no-argument codegen branch.

- `tests/ui/invalid_pymodule_init_args.rs` covers the new arity check.
- `tests/ui/invalid_pymodule_init_pyfunction.rs` covers the pre-existing
  `#[pyfunction]`-alongside-`#[pymodule_init]` check, which had no test.
- `test_pymodule_init_without_module` compiles a module whose
  `#[pymodule_init]` takes no argument and asserts it still runs, covering the
  `#ident()?` branch.
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.

1 participant