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 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. 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 ```rust
# mod declarative_module_module_attr_test { # mod declarative_module_module_attr_test {
use pyo3::prelude::*; use pyo3::prelude::*;
@ -166,7 +165,7 @@ mod my_extension {
#[pymodule_export] #[pymodule_export]
use super::Ext; use super::Ext;
#[pymodule(submodule)] #[pymodule]
mod submodule { mod submodule {
use super::*; use super::*;
// This is a submodule // 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. 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<()> { fn set_submodule(&mut self, submod: SubmoduleAttribute) -> Result<()> {
ensure_spanned!( ensure_spanned!(
!self.is_submodule, !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; self.is_submodule = true;
@ -116,7 +116,14 @@ pub fn pymodule_module_impl(
} else { } else {
name.to_string() 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 = Vec::new();
let mut module_items_cfg_attrs = 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); set_module_attribute(&mut item_mod.attrs, &full_name);
} }
item_mod.attrs.push(parse_quote!(#[pyo3(submodule)]));
} }
} }
Item::ForeignMod(item) => { Item::ForeignMod(item) => {

View File

@ -66,4 +66,5 @@ fn test_compile_errors() {
t.compile_fail("tests/ui/abi3_weakref.rs"); t.compile_fail("tests/ui/abi3_weakref.rs");
#[cfg(all(Py_LIMITED_API, not(Py_3_9)))] #[cfg(all(Py_LIMITED_API, not(Py_3_9)))]
t.compile_fail("tests/ui/abi3_dict.rs"); 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")] #[pyo3(module = "custom_root")]
mod inner_custom_root { mod inner_custom_root {
use super::*; 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`