@@ -1363,19 +1363,38 @@ impl ExecutingFrame<'_> {
1363
1363
fn import_from ( & mut self , vm : & VirtualMachine , idx : bytecode:: NameIdx ) -> PyResult {
1364
1364
let module = self . top_value ( ) ;
1365
1365
let name = self . code . names [ idx as usize ] ;
1366
- let err = || vm . new_import_error ( format ! ( "cannot import name '{name}'" ) , name . to_owned ( ) ) ;
1366
+
1367
1367
// Load attribute, and transform any error into import error.
1368
1368
if let Some ( obj) = vm. get_attribute_opt ( module. to_owned ( ) , name) ? {
1369
1369
return Ok ( obj) ;
1370
1370
}
1371
1371
// fallback to importing '{module.__name__}.{name}' from sys.modules
1372
- let mod_name = module
1373
- . get_attr ( identifier ! ( vm, __name__) , vm)
1374
- . map_err ( |_| err ( ) ) ?;
1375
- let mod_name = mod_name. downcast :: < PyStr > ( ) . map_err ( |_| err ( ) ) ?;
1376
- let full_mod_name = format ! ( "{mod_name}.{name}" ) ;
1377
- let sys_modules = vm. sys_module . get_attr ( "modules" , vm) . map_err ( |_| err ( ) ) ?;
1378
- sys_modules. get_item ( & full_mod_name, vm) . map_err ( |_| err ( ) )
1372
+ let fallback_module = ( || {
1373
+ let mod_name = module. get_attr ( identifier ! ( vm, __name__) , vm) . ok ( ) ?;
1374
+ let mod_name = mod_name. downcast_ref :: < PyStr > ( ) ?;
1375
+ let full_mod_name = format ! ( "{mod_name}.{name}" ) ;
1376
+ let sys_modules = vm. sys_module . get_attr ( "modules" , vm) . ok ( ) ?;
1377
+ sys_modules. get_item ( & full_mod_name, vm) . ok ( )
1378
+ } ) ( ) ;
1379
+
1380
+ if let Some ( sub_module) = fallback_module {
1381
+ return Ok ( sub_module) ;
1382
+ }
1383
+
1384
+ if is_module_initializing ( module, vm) {
1385
+ let module_name = module
1386
+ . get_attr ( identifier ! ( vm, __name__) , vm)
1387
+ . ok ( )
1388
+ . and_then ( |n| n. downcast_ref :: < PyStr > ( ) . map ( |s| s. as_str ( ) . to_owned ( ) ) )
1389
+ . unwrap_or_else ( || "<unknown>" . to_owned ( ) ) ;
1390
+
1391
+ let msg = format ! (
1392
+ "cannot import name '{name}' from partially initialized module '{module_name}' (most likely due to a circular import)" ,
1393
+ ) ;
1394
+ Err ( vm. new_import_error ( msg, name. to_owned ( ) ) )
1395
+ } else {
1396
+ Err ( vm. new_import_error ( format ! ( "cannot import name '{name}'" ) , name. to_owned ( ) ) )
1397
+ }
1379
1398
}
1380
1399
1381
1400
#[ cfg_attr( feature = "flame-it" , flame( "Frame" ) ) ]
@@ -2372,3 +2391,16 @@ impl fmt::Debug for Frame {
2372
2391
)
2373
2392
}
2374
2393
}
2394
+
2395
+ fn is_module_initializing ( module : & PyObject , vm : & VirtualMachine ) -> bool {
2396
+ let Ok ( spec) = module. get_attr ( & vm. ctx . new_str ( "__spec__" ) , vm) else {
2397
+ return false ;
2398
+ } ;
2399
+ if vm. is_none ( & spec) {
2400
+ return false ;
2401
+ }
2402
+ let Ok ( initializing_attr) = spec. get_attr ( & vm. ctx . new_str ( "_initializing" ) , vm) else {
2403
+ return false ;
2404
+ } ;
2405
+ initializing_attr. try_to_bool ( vm) . unwrap_or ( false )
2406
+ }
0 commit comments