Automatically treat nested modules as submodules (#4308)

fixes #4286
This commit is contained in:
Alex Gaynor 2024-07-09 08:15:12 -04:00 committed by GitHub
parent 186c7d3315
commit e73112f3f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 33 additions and 5 deletions

View File

@ -151,7 +151,6 @@ For nested modules, the name of the parent module is automatically added.
In the following example, the `Unit` class will have for `module` `my_extension.submodule` because it is properly nested
but the `Ext` class will have for `module` the default `builtins` because it not nested.
You can provide the `submodule` argument to `pymodule()` for modules that are not top-level modules.
```rust
# mod declarative_module_module_attr_test {
use pyo3::prelude::*;
@ -166,7 +165,7 @@ mod my_extension {
#[pymodule_export]
use super::Ext;
#[pymodule(submodule)]
#[pymodule]
mod submodule {
use super::*;
// This is a submodule
@ -179,3 +178,4 @@ mod my_extension {
```
It is possible to customize the `module` value for a `#[pymodule]` with the `#[pyo3(module = "MY_MODULE")]` option.
You can provide the `submodule` argument to `pymodule()` for modules that are not top-level modules -- it is automatically set for modules nested inside of a `#[pymodule]`.

View File

@ -0,0 +1 @@
Nested declarative `#[pymodule]` are automatically treated as submodules (no `PyInit_` entrypoint is created)

View File

@ -80,7 +80,7 @@ impl PyModuleOptions {
fn set_submodule(&mut self, submod: SubmoduleAttribute) -> Result<()> {
ensure_spanned!(
!self.is_submodule,
submod.span() => "`submodule` may only be specified once"
submod.span() => "`submodule` may only be specified once (it is implicitly always specified for nested modules)"
);
self.is_submodule = true;
@ -116,7 +116,14 @@ pub fn pymodule_module_impl(
} else {
name.to_string()
};
is_submodule = is_submodule || options.is_submodule;
is_submodule = match (is_submodule, options.is_submodule) {
(true, true) => {
bail_spanned!(module.span() => "`submodule` may only be specified once (it is implicitly always specified for nested modules)")
}
(false, false) => false,
(true, false) | (false, true) => true,
};
let mut module_items = Vec::new();
let mut module_items_cfg_attrs = Vec::new();
@ -273,6 +280,7 @@ pub fn pymodule_module_impl(
)? {
set_module_attribute(&mut item_mod.attrs, &full_name);
}
item_mod.attrs.push(parse_quote!(#[pyo3(submodule)]));
}
}
Item::ForeignMod(item) => {

View File

@ -66,4 +66,5 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/abi3_weakref.rs");
#[cfg(all(Py_LIMITED_API, not(Py_3_9)))]
t.compile_fail("tests/ui/abi3_dict.rs");
t.compile_fail("tests/ui/duplicate_pymodule_submodule.rs");
}

View File

@ -115,7 +115,7 @@ mod declarative_module {
}
}
#[pymodule(submodule)]
#[pymodule]
#[pyo3(module = "custom_root")]
mod inner_custom_root {
use super::*;

View File

@ -0,0 +1,7 @@
#[pyo3::pymodule]
mod mymodule {
#[pyo3::pymodule(submodule)]
mod submod {}
}
fn main() {}

View File

@ -0,0 +1,11 @@
error: `submodule` may only be specified once (it is implicitly always specified for nested modules)
--> tests/ui/duplicate_pymodule_submodule.rs:4:2
|
4 | mod submod {}
| ^^^
error[E0433]: failed to resolve: use of undeclared crate or module `submod`
--> tests/ui/duplicate_pymodule_submodule.rs:4:6
|
4 | mod submod {}
| ^^^^^^ use of undeclared crate or module `submod`