@@ -1363,19 +1363,48 @@ impl ExecutingFrame<'_> {
13631363 fn import_from ( & mut self , vm : & VirtualMachine , idx : bytecode:: NameIdx ) -> PyResult {
13641364 let module = self . top_value ( ) ;
13651365 let name = self . code . names [ idx as usize ] ;
1366- let err = || vm . new_import_error ( format ! ( "cannot import name '{name}'" ) , name . to_owned ( ) ) ;
1366+
13671367 // Load attribute, and transform any error into import error.
13681368 if let Some ( obj) = vm. get_attribute_opt ( module. to_owned ( ) , name) ? {
13691369 return Ok ( obj) ;
13701370 }
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 ( ) )
1371+
1372+ let fallback_result: Option < PyResult > = module
1373+ . get_attr ( & vm. ctx . new_str ( "__name__" ) , vm)
1374+ . ok ( )
1375+ . and_then ( |mod_name| mod_name. downcast_ref :: < PyStr > ( ) . map ( |s| s. to_owned ( ) ) )
1376+ . and_then ( |mod_name_str| {
1377+ let full_mod_name = format ! ( "{}.{}" , mod_name_str. as_str( ) , name. as_str( ) ) ;
1378+ vm. sys_module
1379+ . get_attr ( "modules" , vm)
1380+ . ok ( )
1381+ . and_then ( |sys_modules| sys_modules. get_item ( & full_mod_name, vm) . ok ( ) )
1382+ } )
1383+ . map ( Ok ) ;
1384+
1385+ if let Some ( Ok ( sub_module) ) = fallback_result {
1386+ return Ok ( sub_module) ;
1387+ }
1388+
1389+ if is_module_initializing ( module, vm) {
1390+ let module_name = module
1391+ . get_attr ( & vm. ctx . new_str ( "__name__" ) , vm)
1392+ . ok ( )
1393+ . and_then ( |n| n. downcast_ref :: < PyStr > ( ) . map ( |s| s. as_str ( ) . to_owned ( ) ) )
1394+ . unwrap_or_else ( || "<unknown>" . to_owned ( ) ) ;
1395+
1396+ let msg = format ! (
1397+ "cannot import name '{}' from partially initialized module '{}' (most likely due to a circular import)" ,
1398+ name. as_str( ) ,
1399+ module_name
1400+ ) ;
1401+ Err ( vm. new_import_error ( msg, name. to_owned ( ) ) )
1402+ } else {
1403+ Err ( vm. new_import_error (
1404+ format ! ( "cannot import name '{}'" , name. as_str( ) ) ,
1405+ name. to_owned ( ) ,
1406+ ) )
1407+ }
13791408 }
13801409
13811410 #[ cfg_attr( feature = "flame-it" , flame( "Frame" ) ) ]
@@ -2372,3 +2401,16 @@ impl fmt::Debug for Frame {
23722401 )
23732402 }
23742403}
2404+
2405+ fn is_module_initializing ( module : & PyObject , vm : & VirtualMachine ) -> bool {
2406+ let Ok ( spec) = module. get_attr ( & vm. ctx . new_str ( "__spec__" ) , vm) else {
2407+ return false ;
2408+ } ;
2409+ if vm. is_none ( & spec) {
2410+ return false ;
2411+ }
2412+ let Ok ( initializing_attr) = spec. get_attr ( & vm. ctx . new_str ( "_initializing" ) , vm) else {
2413+ return false ;
2414+ } ;
2415+ initializing_attr. try_to_bool ( vm) . unwrap_or ( false )
2416+ }
0 commit comments