@@ -107,6 +107,7 @@ mod _imp {
107107 #[ pyfunction]
108108 fn is_builtin ( name : PyStrRef , vm : & VirtualMachine ) -> bool {
109109 vm. state . module_inits . contains_key ( name. as_str ( ) )
110+ || vm. state . module_defs . contains_key ( name. as_str ( ) )
110111 }
111112
112113 #[ pyfunction]
@@ -116,22 +117,56 @@ mod _imp {
116117
117118 #[ pyfunction]
118119 fn create_builtin ( spec : PyObjectRef , vm : & VirtualMachine ) -> PyResult {
120+ use crate :: PyPayload ;
121+ use crate :: builtins:: PyModule ;
122+
119123 let sys_modules = vm. sys_module . get_attr ( "modules" , vm) . unwrap ( ) ;
120124 let name: PyStrRef = spec. get_attr ( "name" , vm) ?. try_into_value ( vm) ?;
121125
122- let module = if let Ok ( module) = sys_modules. get_item ( & * name, vm) {
123- module
124- } else if let Some ( make_module_func) = vm. state . module_inits . get ( name. as_str ( ) ) {
125- make_module_func ( vm) . into ( )
126- } else {
127- vm. ctx . none ( )
128- } ;
129- Ok ( module)
126+ // Check sys.modules first
127+ if let Ok ( module) = sys_modules. get_item ( & * name, vm) {
128+ return Ok ( module) ;
129+ }
130+
131+ // Try multi-phase init modules first (they need special handling)
132+ if let Some ( def_func) = vm. state . module_defs . get ( name. as_str ( ) ) {
133+ let def = def_func ( & vm. ctx ) ;
134+
135+ // Phase 1: Create module from definition
136+ let module = PyModule :: from_def ( def) . into_ref ( & vm. ctx ) ;
137+
138+ // Initialize module dict
139+ let dict = vm. ctx . new_dict ( ) ;
140+ dict. set_item ( "__name__" , vm. ctx . new_str ( def. name . as_str ( ) ) . into ( ) , vm) ?;
141+ if let Some ( doc) = def. doc {
142+ dict. set_item ( "__doc__" , vm. ctx . new_str ( doc. as_str ( ) ) . into ( ) , vm) ?;
143+ }
144+ module. set_attr ( "__dict__" , dict, vm) ?;
145+
146+ // Add to sys.modules BEFORE exec (critical for circular import handling)
147+ sys_modules. set_item ( & * name, module. clone ( ) . into ( ) , vm) ?;
148+
149+ // Phase 2: Call exec slot (can safely import other modules now)
150+ if let Some ( exec) = def. slots . exec {
151+ exec ( vm, & module) ?;
152+ }
153+
154+ return Ok ( module. into ( ) ) ;
155+ }
156+
157+ // Fall back to legacy single-phase init
158+ if let Some ( make_module_func) = vm. state . module_inits . get ( name. as_str ( ) ) {
159+ let module = make_module_func ( vm) ;
160+ sys_modules. set_item ( & * name, module. clone ( ) . into ( ) , vm) ?;
161+ return Ok ( module. into ( ) ) ;
162+ }
163+
164+ Ok ( vm. ctx . none ( ) )
130165 }
131166
132167 #[ pyfunction]
133168 fn exec_builtin ( _mod : PyRef < PyModule > ) -> i32 {
134- // TODO: Should we do something here?
169+ // For multi-phase init modules, exec is already called in create_builtin
135170 0
136171 }
137172
0 commit comments