@@ -194,6 +194,59 @@ pub fn impl_pymodule(args: PyModuleArgs, module_item: Item) -> Result<TokenStrea |
194 | 194 | context.errors.ok_or_push(r); |
195 | 195 | } |
196 | 196 |
|
| 197 | + // Detect nested #[pymodule] items (non-sub) and generate submodule init code |
| 198 | + let mut submodule_inits: Vec<TokenStream> = Vec::new(); |
| 199 | + for item in items.iter() { |
| 200 | + if let Item::Mod(item_mod) = item { |
| 201 | + let r = (|| -> Result<()> { |
| 202 | + let attr = match item_mod |
| 203 | + .attrs |
| 204 | + .iter() |
| 205 | + .find(|a| a.path().is_ident("pymodule")) |
| 206 | + { |
| 207 | + Some(attr) => attr, |
| 208 | + None => return Ok(()), |
| 209 | + }; |
| 210 | + let args_tokens = match &attr.meta { |
| 211 | + syn::Meta::Path(_) => TokenStream::new(), |
| 212 | + syn::Meta::List(list) => list.tokens.clone(), |
| 213 | + _ => return Ok(()), |
| 214 | + }; |
| 215 | + let mod_args: PyModuleArgs = syn::parse2(args_tokens)?; |
| 216 | + let fake_ident = Ident::new("pymodule", attr.span()); |
| 217 | + let mod_meta = ModuleItemMeta::from_nested( |
| 218 | + item_mod.ident.clone(), |
| 219 | + fake_ident, |
| 220 | + mod_args.metas.into_iter(), |
| 221 | + )?; |
| 222 | + if mod_meta.sub()? { |
| 223 | + return Ok(()); |
| 224 | + } |
| 225 | + let py_name = mod_meta.simple_name()?; |
| 226 | + let mod_ident = &item_mod.ident; |
| 227 | + let cfgs: Vec<_> = item_mod |
| 228 | + .attrs |
| 229 | + .iter() |
| 230 | + .filter(|a| a.path().is_ident("cfg")) |
| 231 | + .cloned() |
| 232 | + .collect(); |
| 233 | + submodule_inits.push(quote! { |
| 234 | + #(#cfgs)* |
| 235 | + { |
| 236 | + let child_def = #mod_ident::module_def(ctx); |
| 237 | + let child = child_def.create_module(vm).expect("submodule create_module failed"); |
| 238 | + child.__init_methods(vm).expect("submodule __init_methods failed"); |
| 239 | + #mod_ident::module_exec(vm, &child).expect("submodule module_exec failed"); |
| 240 | + let child: ::rustpython_vm::PyObjectRef = child.into(); |
| 241 | + vm.__module_set_attr(module, ctx.intern_str(#py_name), child).expect("module set_attr submodule failed"); |
| 242 | + } |
| 243 | + }); |
| 244 | + Ok(()) |
| 245 | + })(); |
| 246 | + context.errors.ok_or_push(r); |
| 247 | + } |
| 248 | + } |
| 249 | + |
197 | 250 | // append additional items |
198 | 251 | let module_name = context.name.as_str(); |
199 | 252 | let function_items = context.function_items.validate()?; |
@@ -316,6 +369,7 @@ pub fn impl_pymodule(args: PyModuleArgs, module_item: Item) -> Result<TokenStrea |
316 | 369 | #(#init_with_calls)* |
317 | 370 | let ctx = &vm.ctx; |
318 | 371 | #attribute_items |
| 372 | + #(#submodule_inits)* |
319 | 373 | } |
320 | 374 | }, |
321 | 375 | parse_quote! { |
@@ -783,7 +837,15 @@ impl ModuleItem for StructSequenceItem { |
783 | 837 | "#[pystruct_sequence] requires name parameter", |
784 | 838 | ) |
785 | 839 | })?; |
786 | | - let module_name = meta.module()?.unwrap_or_else(|| args.context.name.clone()); |
| 840 | + let module_opt = meta.module()?; |
| 841 | + let has_module = module_opt.is_some(); |
| 842 | + let module_name = module_opt.unwrap_or_else(|| args.context.name.clone()); |
| 843 | + if !has_module { |
| 844 | + let structseq_attr = &mut args.attrs[self.inner.index]; |
| 845 | + structseq_attr.fill_nested_meta("module", || { |
| 846 | + parse_quote! {module = #module_name} |
| 847 | + })?; |
| 848 | + } |
787 | 849 | let no_attr = meta.no_attr()?; |
788 | 850 |
|
789 | 851 | // Generate the class creation code |
|