@@ -1552,17 +1552,12 @@ def configure_base_classes(self,
1552
1552
elif isinstance (base , Instance ):
1553
1553
if base .type .is_newtype :
1554
1554
self .fail ('Cannot subclass "NewType"' , defn )
1555
- if (
1556
- base .type .is_enum
1557
- and base .type .fullname not in ENUM_BASES
1558
- and base .type .names
1559
- and any (not isinstance (n .node , (FuncBase , Decorator ))
1560
- for n in base .type .names .values ())
1561
- ):
1555
+ if self .enum_has_final_values (base ):
1562
1556
# This means that are trying to subclass a non-default
1563
1557
# Enum class, with defined members. This is not possible.
1564
1558
# In runtime, it will raise. We need to mark this type as final.
1565
1559
# However, methods can be defined on a type: only values can't.
1560
+ # We also don't count values with annotations only.
1566
1561
base .type .is_final = True
1567
1562
base_types .append (base )
1568
1563
elif isinstance (base , AnyType ):
@@ -1601,6 +1596,25 @@ def configure_base_classes(self,
1601
1596
return
1602
1597
self .calculate_class_mro (defn , self .object_type )
1603
1598
1599
+ def enum_has_final_values (self , base : Instance ) -> bool :
1600
+ if (
1601
+ base .type .is_enum
1602
+ and base .type .fullname not in ENUM_BASES
1603
+ and base .type .names
1604
+ and base .type .defn
1605
+ ):
1606
+ for sym in base .type .names .values ():
1607
+ if isinstance (sym .node , (FuncBase , Decorator )):
1608
+ continue # A method
1609
+ if not isinstance (sym .node , Var ):
1610
+ return True # Can be a class
1611
+ if self .is_stub_file or sym .node .has_explicit_value :
1612
+ # Corner case: assignments like `x: int` are fine in `.py` files.
1613
+ # But, not is `.pyi` files, because we don't know
1614
+ # if there's aactually a value or not.
1615
+ return True
1616
+ return False
1617
+
1604
1618
def configure_tuple_base_class (self ,
1605
1619
defn : ClassDef ,
1606
1620
base : TupleType ,
@@ -2040,7 +2054,7 @@ def visit_import_all(self, i: ImportAll) -> None:
2040
2054
2041
2055
def visit_assignment_expr (self , s : AssignmentExpr ) -> None :
2042
2056
s .value .accept (self )
2043
- self .analyze_lvalue (s .target , escape_comprehensions = True )
2057
+ self .analyze_lvalue (s .target , escape_comprehensions = True , has_explicit_value = True )
2044
2058
2045
2059
def visit_assignment_stmt (self , s : AssignmentStmt ) -> None :
2046
2060
self .statement = s
@@ -2333,10 +2347,20 @@ def analyze_lvalues(self, s: AssignmentStmt) -> None:
2333
2347
assert isinstance (s .unanalyzed_type , UnboundType )
2334
2348
if not s .unanalyzed_type .args :
2335
2349
explicit = False
2350
+
2351
+ if s .rvalue :
2352
+ if isinstance (s .rvalue , TempNode ):
2353
+ has_explicit_value = not s .rvalue .no_rhs
2354
+ else :
2355
+ has_explicit_value = True
2356
+ else :
2357
+ has_explicit_value = False
2358
+
2336
2359
for lval in s .lvalues :
2337
2360
self .analyze_lvalue (lval ,
2338
2361
explicit_type = explicit ,
2339
- is_final = s .is_final_def )
2362
+ is_final = s .is_final_def ,
2363
+ has_explicit_value = has_explicit_value )
2340
2364
2341
2365
def apply_dynamic_class_hook (self , s : AssignmentStmt ) -> None :
2342
2366
if not isinstance (s .rvalue , CallExpr ):
@@ -2776,7 +2800,8 @@ def analyze_lvalue(self,
2776
2800
nested : bool = False ,
2777
2801
explicit_type : bool = False ,
2778
2802
is_final : bool = False ,
2779
- escape_comprehensions : bool = False ) -> None :
2803
+ escape_comprehensions : bool = False ,
2804
+ has_explicit_value : bool = False ) -> None :
2780
2805
"""Analyze an lvalue or assignment target.
2781
2806
2782
2807
Args:
@@ -2790,7 +2815,11 @@ def analyze_lvalue(self,
2790
2815
if escape_comprehensions :
2791
2816
assert isinstance (lval , NameExpr ), "assignment expression target must be NameExpr"
2792
2817
if isinstance (lval , NameExpr ):
2793
- self .analyze_name_lvalue (lval , explicit_type , is_final , escape_comprehensions )
2818
+ self .analyze_name_lvalue (
2819
+ lval , explicit_type , is_final ,
2820
+ escape_comprehensions ,
2821
+ has_explicit_value = has_explicit_value ,
2822
+ )
2794
2823
elif isinstance (lval , MemberExpr ):
2795
2824
self .analyze_member_lvalue (lval , explicit_type , is_final )
2796
2825
if explicit_type and not self .is_self_member_ref (lval ):
@@ -2814,7 +2843,8 @@ def analyze_name_lvalue(self,
2814
2843
lvalue : NameExpr ,
2815
2844
explicit_type : bool ,
2816
2845
is_final : bool ,
2817
- escape_comprehensions : bool ) -> None :
2846
+ escape_comprehensions : bool ,
2847
+ has_explicit_value : bool ) -> None :
2818
2848
"""Analyze an lvalue that targets a name expression.
2819
2849
2820
2850
Arguments are similar to "analyze_lvalue".
@@ -2844,7 +2874,7 @@ def analyze_name_lvalue(self,
2844
2874
2845
2875
if (not existing or isinstance (existing .node , PlaceholderNode )) and not outer :
2846
2876
# Define new variable.
2847
- var = self .make_name_lvalue_var (lvalue , kind , not explicit_type )
2877
+ var = self .make_name_lvalue_var (lvalue , kind , not explicit_type , has_explicit_value )
2848
2878
added = self .add_symbol (name , var , lvalue , escape_comprehensions = escape_comprehensions )
2849
2879
# Only bind expression if we successfully added name to symbol table.
2850
2880
if added :
@@ -2895,7 +2925,9 @@ def is_alias_for_final_name(self, name: str) -> bool:
2895
2925
existing = self .globals .get (orig_name )
2896
2926
return existing is not None and is_final_node (existing .node )
2897
2927
2898
- def make_name_lvalue_var (self , lvalue : NameExpr , kind : int , inferred : bool ) -> Var :
2928
+ def make_name_lvalue_var (
2929
+ self , lvalue : NameExpr , kind : int , inferred : bool , has_explicit_value : bool ,
2930
+ ) -> Var :
2899
2931
"""Return a Var node for an lvalue that is a name expression."""
2900
2932
v = Var (lvalue .name )
2901
2933
v .set_line (lvalue )
@@ -2910,6 +2942,7 @@ def make_name_lvalue_var(self, lvalue: NameExpr, kind: int, inferred: bool) -> V
2910
2942
# fullanme should never stay None
2911
2943
v ._fullname = lvalue .name
2912
2944
v .is_ready = False # Type not inferred yet
2945
+ v .has_explicit_value = has_explicit_value
2913
2946
return v
2914
2947
2915
2948
def make_name_lvalue_point_to_existing_def (
@@ -2953,7 +2986,14 @@ def analyze_tuple_or_list_lvalue(self, lval: TupleExpr,
2953
2986
if len (star_exprs ) == 1 :
2954
2987
star_exprs [0 ].valid = True
2955
2988
for i in items :
2956
- self .analyze_lvalue (i , nested = True , explicit_type = explicit_type )
2989
+ self .analyze_lvalue (
2990
+ lval = i ,
2991
+ nested = True ,
2992
+ explicit_type = explicit_type ,
2993
+ # Lists and tuples always have explicit values defined:
2994
+ # `a, b, c = value`
2995
+ has_explicit_value = True ,
2996
+ )
2957
2997
2958
2998
def analyze_member_lvalue (self , lval : MemberExpr , explicit_type : bool , is_final : bool ) -> None :
2959
2999
"""Analyze lvalue that is a member expression.
0 commit comments