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