Skip to content

Commit 8e3c276

Browse files
committed
Automatically treat nested modules as submodules
fixes PyO3#4286
1 parent 9afc38a commit 8e3c276

File tree

6 files changed

+31
-3
lines changed

6 files changed

+31
-3
lines changed

newsfragments/4308.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Nested declarative `#[pymodule]` are automatically treated as submodules (no `PyInit_` entrypoint is created)

pyo3-macros-backend/src/module.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl PyModuleOptions {
8080
fn set_submodule(&mut self, submod: SubmoduleAttribute) -> Result<()> {
8181
ensure_spanned!(
8282
!self.is_submodule,
83-
submod.span() => "`submodule` may only be specified once"
83+
submod.span() => "`submodule` may only be specified once (it is implicitly always specified for nested modules)"
8484
);
8585

8686
self.is_submodule = true;
@@ -116,7 +116,14 @@ pub fn pymodule_module_impl(
116116
} else {
117117
name.to_string()
118118
};
119-
is_submodule = is_submodule || options.is_submodule;
119+
120+
is_submodule = match (is_submodule, options.is_submodule) {
121+
(true, true) => {
122+
bail_spanned!(module.span() => "`submodule` may only be specified once (it is implicitly always specified for nested modules)")
123+
}
124+
(false, false) => false,
125+
(true, false) | (false, true) => true,
126+
};
120127

121128
let mut module_items = Vec::new();
122129
let mut module_items_cfg_attrs = Vec::new();
@@ -273,6 +280,7 @@ pub fn pymodule_module_impl(
273280
)? {
274281
set_module_attribute(&mut item_mod.attrs, &full_name);
275282
}
283+
item_mod.attrs.push(parse_quote!(#[pyo3(submodule)]));
276284
}
277285
}
278286
Item::ForeignMod(item) => {

tests/test_compile_error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,5 @@ fn test_compile_errors() {
6767
t.compile_fail("tests/ui/abi3_weakref.rs");
6868
#[cfg(all(Py_LIMITED_API, not(Py_3_9)))]
6969
t.compile_fail("tests/ui/abi3_dict.rs");
70+
t.compile_fail("tests/ui/duplicate_pymodule_submodule.rs");
7071
}

tests/test_declarative_module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ mod declarative_module {
115115
}
116116
}
117117

118-
#[pymodule(submodule)]
118+
#[pymodule]
119119
#[pyo3(module = "custom_root")]
120120
mod inner_custom_root {
121121
use super::*;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[pyo3::pymodule]
2+
mod mymodule {
3+
#[pyo3::pymodule(submodule)]
4+
mod submod {}
5+
}
6+
7+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: `submodule` may only be specified once (it is implicitly always specified for nested modules)
2+
--> tests/ui/duplicate_pymodule_submodule.rs:4:2
3+
|
4+
4 | mod submod {}
5+
| ^^^
6+
7+
error[E0433]: failed to resolve: use of undeclared crate or module `submod`
8+
--> tests/ui/duplicate_pymodule_submodule.rs:4:6
9+
|
10+
4 | mod submod {}
11+
| ^^^^^^ use of undeclared crate or module `submod`

0 commit comments

Comments
 (0)