@@ -467,6 +467,101 @@ impl Compiler<'_> {
467467 . to_u32 ( )
468468 }
469469
470+ /// Set the qualified name for the current code object, based on CPython's compiler_set_qualname
471+ fn set_qualname ( & mut self ) -> String {
472+ let qualname = self . make_qualname ( ) ;
473+ self . current_code_info ( ) . qualname = Some ( qualname. clone ( ) ) ;
474+ qualname
475+ }
476+ fn make_qualname ( & mut self ) -> String {
477+ let stack_size = self . code_stack . len ( ) ;
478+ assert ! ( stack_size >= 1 ) ;
479+
480+ let current_obj_name = self . current_code_info ( ) . obj_name . clone ( ) ;
481+
482+ // If we're at the module level (stack_size == 1), qualname is just the name
483+ if stack_size <= 1 {
484+ return current_obj_name;
485+ }
486+
487+ // Check parent scope
488+ let mut parent_idx = stack_size - 2 ;
489+ let mut parent = & self . code_stack [ parent_idx] ;
490+
491+ // If parent is a type parameter scope, look at grandparent
492+ if parent. obj_name . starts_with ( "<generic parameters of " ) {
493+ if stack_size == 2 {
494+ // If we're immediately within the module after type params,
495+ // qualname is just the name
496+ return current_obj_name;
497+ }
498+ parent_idx = stack_size - 3 ;
499+ parent = & self . code_stack [ parent_idx] ;
500+ }
501+
502+ // Check if this is a global class/function
503+ let mut force_global = false ;
504+ if stack_size > self . symbol_table_stack . len ( ) {
505+ // We might be in a situation where symbol table isn't pushed yet
506+ // In this case, check the parent symbol table
507+ if let Some ( parent_table) = self . symbol_table_stack . last ( ) {
508+ if let Some ( symbol) = parent_table. lookup ( & current_obj_name) {
509+ if symbol. scope == SymbolScope :: GlobalExplicit {
510+ force_global = true ;
511+ }
512+ }
513+ }
514+ } else if let Some ( _current_table) = self . symbol_table_stack . last ( ) {
515+ // Mangle the name if necessary (for private names in classes)
516+ let mangled_name = self . mangle ( & current_obj_name) ;
517+
518+ // Look up in parent symbol table to check scope
519+ if self . symbol_table_stack . len ( ) >= 2 {
520+ let parent_table = & self . symbol_table_stack [ self . symbol_table_stack . len ( ) - 2 ] ;
521+ if let Some ( symbol) = parent_table. lookup ( & mangled_name) {
522+ if symbol. scope == SymbolScope :: GlobalExplicit {
523+ force_global = true ;
524+ }
525+ }
526+ }
527+ }
528+
529+ // Build the qualified name
530+ if force_global {
531+ // For global symbols, qualname is just the name
532+ current_obj_name
533+ } else {
534+ // Check parent scope type
535+ let parent_obj_name = & parent. obj_name ;
536+
537+ // Determine if parent is a function-like scope
538+ let is_function_parent = parent. flags . contains ( bytecode:: CodeFlags :: IS_OPTIMIZED )
539+ && !parent_obj_name. starts_with ( "<" ) // Not a special scope like <lambda>, <listcomp>, etc.
540+ && parent_obj_name != "<module>" ; // Not the module scope
541+
542+ let path_len = self . qualified_path . len ( ) ;
543+
544+ if is_function_parent {
545+ // For functions, append .<locals> to parent qualname
546+ // Use parent's qualname if available, otherwise use parent_obj_name
547+ let parent_qualname = parent. qualname . as_ref ( ) . unwrap_or ( parent_obj_name) ;
548+ format ! ( "{parent_qualname}.<locals>.{current_obj_name}" )
549+ } else {
550+ // For classes and other scopes, use qualified_path without current name
551+ // (since current name is already pushed to qualified_path)
552+ if path_len > 0 && self . qualified_path [ path_len - 1 ] == current_obj_name {
553+ // Current name is already in qualified_path, just join
554+ self . qualified_path . join ( "." )
555+ } else if self . qualified_path . is_empty ( ) {
556+ current_obj_name
557+ } else {
558+ // Append current name to qualified_path
559+ format ! ( "{}.{}" , self . qualified_path. join( "." ) , current_obj_name)
560+ }
561+ }
562+ }
563+ }
564+
470565 fn compile_program (
471566 & mut self ,
472567 body : & ModModule ,
@@ -1548,10 +1643,9 @@ impl Compiler<'_> {
15481643 } ;
15491644
15501645 self . push_qualified_path ( name) ;
1551- let qualified_name = self . qualified_path . join ( "." ) ;
15521646
1553- // Update the qualname in the current code info
1554- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( qualified_name . clone ( ) ) ;
1647+ // Set qualname using the new method
1648+ let qualname = self . set_qualname ( ) ;
15551649
15561650 self . push_qualified_path ( "<locals>" ) ;
15571651
@@ -1646,7 +1740,7 @@ impl Compiler<'_> {
16461740 code : Box :: new ( code) ,
16471741 } ) ;
16481742 self . emit_load_const ( ConstantData :: Str {
1649- value : qualified_name . into ( ) ,
1743+ value : qualname . into ( ) ,
16501744 } ) ;
16511745
16521746 // Turn code object into function object:
@@ -1771,7 +1865,6 @@ impl Compiler<'_> {
17711865 global_path_prefix. append ( & mut self . qualified_path ) ;
17721866 }
17731867 self . push_qualified_path ( name) ;
1774- let qualified_name = self . qualified_path . join ( "." ) ;
17751868
17761869 // If there are type params, we need to push a special symbol table just for them
17771870 if let Some ( type_params) = type_params {
@@ -1790,8 +1883,8 @@ impl Compiler<'_> {
17901883
17911884 self . push_output ( bytecode:: CodeFlags :: empty ( ) , 0 , 0 , 0 , name. to_owned ( ) ) ;
17921885
1793- // Update the qualname in the current code info
1794- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( qualified_name . clone ( ) ) ;
1886+ // Set qualname using the new method
1887+ let qualname = self . set_qualname ( ) ;
17951888
17961889 // For class scopes, set u_private to the class name for name mangling
17971890 self . code_stack . last_mut ( ) . unwrap ( ) . private = Some ( name. to_owned ( ) ) ;
@@ -1803,10 +1896,10 @@ impl Compiler<'_> {
18031896 let dunder_module = self . name ( "__module__" ) ;
18041897 emit ! ( self , Instruction :: StoreLocal ( dunder_module) ) ;
18051898 self . emit_load_const ( ConstantData :: Str {
1806- value : qualified_name . into ( ) ,
1899+ value : qualname . into ( ) ,
18071900 } ) ;
1808- let qualname = self . name ( "__qualname__" ) ;
1809- emit ! ( self , Instruction :: StoreLocal ( qualname ) ) ;
1901+ let qualname_name = self . name ( "__qualname__" ) ;
1902+ emit ! ( self , Instruction :: StoreLocal ( qualname_name ) ) ;
18101903 self . load_docstring ( doc_str) ;
18111904 let doc = self . name ( "__doc__" ) ;
18121905 emit ! ( self , Instruction :: StoreLocal ( doc) ) ;
@@ -3606,8 +3699,8 @@ impl Compiler<'_> {
36063699 let mut func_flags = self
36073700 . enter_function ( & name, parameters. as_deref ( ) . unwrap_or ( & Default :: default ( ) ) ) ?;
36083701
3609- // Lambda qualname should be < lambda>
3610- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( name . clone ( ) ) ;
3702+ // Set qualname for lambda
3703+ self . set_qualname ( ) ;
36113704
36123705 self . ctx = CompileContext {
36133706 loop_data : Option :: None ,
@@ -4078,7 +4171,7 @@ impl Compiler<'_> {
40784171 self . push_output ( flags, 1 , 1 , 0 , name. to_owned ( ) ) ;
40794172
40804173 // Set qualname for comprehension
4081- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( name . to_owned ( ) ) ;
4174+ self . set_qualname ( ) ;
40824175
40834176 let arg0 = self . varname ( ".0" ) ?;
40844177
0 commit comments