188188 WithStmt ,
189189 YieldExpr ,
190190 YieldFromExpr ,
191+ func_scoped_name ,
191192 get_member_expr_fullname ,
192193 implicit_module_attrs ,
194+ inline_base ,
193195 is_final_node ,
194196 type_aliases ,
195197 type_aliases_source_versions ,
@@ -1992,7 +1994,7 @@ def analyze_class(self, defn: ClassDef) -> None:
19921994 return
19931995
19941996 self .analyze_class_keywords (defn )
1995- bases_result = self .analyze_base_classes (bases )
1997+ bases_result = self .analyze_base_classes (defn . name , bases )
19961998 if bases_result is None or self .found_incomplete_ref (tag ):
19971999 # Something was incomplete. Defer current target.
19982000 self .mark_incomplete (defn .name , defn )
@@ -2112,7 +2114,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> bool:
21122114 if info is None :
21132115 self .mark_incomplete (defn .name , defn )
21142116 else :
2115- self .prepare_class_def (defn , info , custom_names = True )
2117+ self .prepare_class_def (defn , info )
21162118 for decorator in defn .decorators :
21172119 decorator .accept (self )
21182120 if defn .info :
@@ -2136,13 +2138,13 @@ def analyze_namedtuple_classdef(
21362138 info : TypeInfo | None = defn .info
21372139 else :
21382140 is_named_tuple , info = self .named_tuple_analyzer .analyze_namedtuple_classdef (
2139- defn , self .is_stub_file , self . is_func_scope ()
2141+ defn , self .is_stub_file
21402142 )
21412143 if is_named_tuple :
21422144 if info is None :
21432145 self .mark_incomplete (defn .name , defn )
21442146 else :
2145- self .prepare_class_def (defn , info , custom_names = True )
2147+ self .prepare_class_def (defn , info )
21462148 self .setup_type_vars (defn , tvar_defs )
21472149 self .setup_alias_type_vars (defn )
21482150 with self .scope .class_scope (defn .info ):
@@ -2512,51 +2514,26 @@ def get_and_bind_all_tvars(self, type_exprs: list[Expression]) -> list[TypeVarLi
25122514 tvar_defs .append (tvar_def )
25132515 return tvar_defs
25142516
2515- def prepare_class_def (
2516- self , defn : ClassDef , info : TypeInfo | None = None , custom_names : bool = False
2517- ) -> None :
2517+ def class_fullname (self , name : str , line : int ) -> str :
2518+ if not self .is_nested_within_func_scope ():
2519+ return self .qualified_name (name )
2520+ name = func_scoped_name (name , line )
2521+ return f"{ self .cur_mod_id } .{ name } "
2522+
2523+ def prepare_class_def (self , defn : ClassDef , info : TypeInfo | None = None ) -> None :
25182524 """Prepare for the analysis of a class definition.
25192525
25202526 Create an empty TypeInfo and store it in a symbol table, or if the 'info'
25212527 argument is provided, store it instead (used for magic type definitions).
25222528 """
25232529 if not defn .info :
2524- defn .fullname = self .qualified_name (defn .name )
2525- # TODO: Nested classes
2530+ defn .fullname = self .class_fullname (defn .name , defn .line )
25262531 info = info or self .make_empty_type_info (defn )
25272532 defn .info = info
25282533 info .defn = defn
2529- if not custom_names :
2530- # Some special classes (in particular NamedTuples) use custom fullname logic.
2531- # Don't override it here (also see comment below, this needs cleanup).
2532- if not self .is_func_scope ():
2533- info ._fullname = self .qualified_name (defn .name )
2534- else :
2535- info ._fullname = info .name
2536- local_name = defn .name
2537- if "@" in local_name :
2538- local_name = local_name .split ("@" )[0 ]
2539- self .add_symbol (local_name , defn .info , defn )
2534+ self .add_symbol (defn .name , defn .info , defn )
25402535 if self .is_nested_within_func_scope ():
2541- # We need to preserve local classes, let's store them
2542- # in globals under mangled unique names
2543- #
2544- # TODO: Putting local classes into globals breaks assumptions in fine-grained
2545- # incremental mode and we should avoid it. In general, this logic is too
2546- # ad-hoc and needs to be removed/refactored.
2547- if "@" not in defn .info ._fullname :
2548- global_name = defn .info .name + "@" + str (defn .line )
2549- defn .info ._fullname = self .cur_mod_id + "." + global_name
2550- else :
2551- # Preserve name from previous fine-grained incremental run.
2552- global_name = defn .info .name
2553- defn .fullname = defn .info ._fullname
2554- if defn .info .is_named_tuple or defn .info .typeddict_type :
2555- # Named tuples and Typed dicts nested within a class are stored
2556- # in the class symbol table.
2557- self .add_symbol_skip_local (global_name , defn .info )
2558- else :
2559- self .globals [global_name ] = SymbolTableNode (GDEF , defn .info )
2536+ self .add_global_symbol (defn .name , defn , defn .info )
25602537
25612538 def make_empty_type_info (self , defn : ClassDef ) -> TypeInfo :
25622539 if (
@@ -2587,7 +2564,7 @@ def get_name_repr_of_expr(self, expr: Expression) -> str | None:
25872564 return None
25882565
25892566 def analyze_base_classes (
2590- self , base_type_exprs : list [Expression ]
2567+ self , cls_name : str , base_type_exprs : list [Expression ]
25912568 ) -> tuple [list [tuple [ProperType , Expression ]], bool ] | None :
25922569 """Analyze base class types.
25932570
@@ -2599,7 +2576,7 @@ def analyze_base_classes(
25992576 """
26002577 is_error = False
26012578 bases = []
2602- for base_expr in base_type_exprs :
2579+ for i , base_expr in enumerate ( base_type_exprs ) :
26032580 if (
26042581 isinstance (base_expr , RefExpr )
26052582 and base_expr .fullname in TYPED_NAMEDTUPLE_NAMES + TPDICT_NAMES
@@ -2617,7 +2594,10 @@ def analyze_base_classes(
26172594
26182595 try :
26192596 base = self .expr_to_analyzed_type (
2620- base_expr , allow_placeholder = True , allow_type_any = True
2597+ base_expr ,
2598+ allow_placeholder = True ,
2599+ allow_type_any = True ,
2600+ unique_name = inline_base (cls_name , i ),
26212601 )
26222602 except TypeTranslationError :
26232603 name = self .get_name_repr_of_expr (base_expr )
@@ -3594,7 +3574,7 @@ def analyze_enum_assign(self, s: AssignmentStmt) -> bool:
35943574 # This is an analyzed enum definition.
35953575 # It is valid iff it can be stored correctly, failures were already reported.
35963576 return self ._is_single_name_assignment (s )
3597- return self .enum_call_analyzer .process_enum_call (s , self . is_func_scope () )
3577+ return self .enum_call_analyzer .process_enum_call (s )
35983578
35993579 def analyze_namedtuple_assign (self , s : AssignmentStmt ) -> bool :
36003580 """Check if s defines a namedtuple."""
@@ -3618,7 +3598,7 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool:
36183598 namespace = self .qualified_name (name )
36193599 with self .tvar_scope_frame (self .tvar_scope .class_frame (namespace )):
36203600 internal_name , info , tvar_defs = self .named_tuple_analyzer .check_namedtuple (
3621- s .rvalue , name , self . is_func_scope ()
3601+ s .rvalue , name
36223602 )
36233603 if internal_name is None :
36243604 return False
@@ -3655,7 +3635,7 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool:
36553635 namespace = self .qualified_name (name )
36563636 with self .tvar_scope_frame (self .tvar_scope .class_frame (namespace )):
36573637 is_typed_dict , info , tvar_defs = self .typed_dict_analyzer .check_typeddict (
3658- s .rvalue , name , self . is_func_scope ()
3638+ s .rvalue , name
36593639 )
36603640 if not is_typed_dict :
36613641 return False
@@ -5161,17 +5141,18 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
51615141 return True
51625142
51635143 def basic_new_typeinfo (self , name : str , basetype_or_fallback : Instance , line : int ) -> TypeInfo :
5164- if self .is_func_scope () and not self .type and "@" not in name :
5165- name += "@" + str (line )
51665144 class_def = ClassDef (name , Block ([]))
5167- if self .is_func_scope () and not self .type :
5168- # Full names of generated classes should always be prefixed with the module names
5169- # even if they are nested in a function, since these classes will be (de-)serialized.
5170- # (Note that the caller should append @line to the name to avoid collisions.)
5171- # TODO: clean this up, see #6422.
5172- class_def .fullname = self .cur_mod_id + "." + self .qualified_name (name )
5173- else :
5174- class_def .fullname = self .qualified_name (name )
5145+ # Ground rules for classes nested in functions:
5146+ # * Use is_nested_within_func_scope(), not is_func_scope(), to determine whether
5147+ # to use any special logic, because nothing inside top-level functions is serialized.
5148+ # * ClassDef.name is not mangled (i.e. @line suffix is not appended).
5149+ # * ClassDef.fullname, and thus TypeInfo.fullname are always pkg.mod.Name@line, any
5150+ # "intermediate" classes are not included in the fullname.
5151+ # * The caller is responsible for storing the generated TypeInfo twice: once as usual
5152+ # with add_symbol(), and once using add_global_symbol() using the mangled name.
5153+ # The second one is needed to properly serialize any classes nested in functions.
5154+ # TODO: make sure the daemon works well with these rules.
5155+ class_def .fullname = self .class_fullname (name , line )
51755156
51765157 info = TypeInfo (SymbolTable (), class_def , self .cur_mod_id )
51775158 class_def .info = info
@@ -7030,27 +7011,18 @@ def add_symbol(
70307011 name , symbol , context , can_defer , escape_comprehensions , no_progress , type_param
70317012 )
70327013
7033- def add_symbol_skip_local (self , name : str , node : SymbolNode ) -> None :
7034- """Same as above, but skipping the local namespace.
7014+ def add_global_symbol (self , name : str , ctx : Context , node : SymbolNode ) -> None :
7015+ """Add symbol to a global namespace.
70357016
70367017 This doesn't check for previous definition and is only used
7037- for serialization of method-level classes.
7018+ for serialization of classes nested in functions/methods .
70387019
70397020 Classes defined within methods can be exposed through an
70407021 attribute type, but method-level symbol tables aren't serialized.
70417022 This method can be used to add such classes to an enclosing,
70427023 serialized symbol table.
70437024 """
7044- # TODO: currently this is only used by named tuples and typed dicts.
7045- # Use this method also by normal classes, see issue #6422.
7046- if self .type is not None :
7047- names = self .type .names
7048- kind = MDEF
7049- else :
7050- names = self .globals
7051- kind = GDEF
7052- symbol = SymbolTableNode (kind , node )
7053- names [name ] = symbol
7025+ self .globals [func_scoped_name (name , ctx .line )] = SymbolTableNode (GDEF , node )
70547026
70557027 def add_symbol_table_node (
70567028 self ,
@@ -7111,8 +7083,10 @@ def add_symbol_table_node(
71117083 if isinstance (old , Var ) and is_init_only (old ):
71127084 if old .has_explicit_value :
71137085 self .fail ("InitVar with default value cannot be redefined" , context )
7114- elif not (
7115- isinstance (new , (FuncDef , Decorator )) and self .set_original_def (old , new )
7086+ elif (
7087+ not (isinstance (new , (FuncDef , Decorator )) and self .set_original_def (old , new ))
7088+ # Avoid (additional) errors for internal symbols.
7089+ and "@" not in name
71167090 ):
71177091 self .name_already_defined (name , context , existing )
71187092 elif type_param or (
@@ -7710,14 +7684,15 @@ def expr_to_analyzed_type(
77107684 allow_unbound_tvars : bool = False ,
77117685 allow_param_spec_literals : bool = False ,
77127686 allow_unpack : bool = False ,
7687+ unique_name : str | None = None ,
77137688 ) -> Type | None :
7714- if isinstance (expr , CallExpr ):
7689+ if unique_name is not None and isinstance (expr , CallExpr ):
77157690 # This is a legacy syntax intended mostly for Python 2, we keep it for
77167691 # backwards compatibility, but new features like generic named tuples
77177692 # and recursive named tuples will be not supported.
77187693 expr .accept (self )
77197694 internal_name , info , tvar_defs = self .named_tuple_analyzer .check_namedtuple (
7720- expr , None , self . is_func_scope ()
7695+ expr , unique_name
77217696 )
77227697 if tvar_defs :
77237698 self .fail ("Generic named tuples are not supported for legacy class syntax" , expr )
0 commit comments