@@ -194,6 +194,59 @@ pub fn impl_pymodule(args: PyModuleArgs, module_item: Item) -> Result<TokenStrea
194194 context. errors . ok_or_push ( r) ;
195195 }
196196
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+
197250 // append additional items
198251 let module_name = context. name . as_str ( ) ;
199252 let function_items = context. function_items . validate ( ) ?;
@@ -316,6 +369,7 @@ pub fn impl_pymodule(args: PyModuleArgs, module_item: Item) -> Result<TokenStrea
316369 #( #init_with_calls) *
317370 let ctx = & vm. ctx;
318371 #attribute_items
372+ #( #submodule_inits) *
319373 }
320374 } ,
321375 parse_quote ! {
@@ -783,7 +837,15 @@ impl ModuleItem for StructSequenceItem {
783837 "#[pystruct_sequence] requires name parameter" ,
784838 )
785839 } ) ?;
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+ }
787849 let no_attr = meta. no_attr ( ) ?;
788850
789851 // Generate the class creation code
0 commit comments