@@ -1315,7 +1315,8 @@ def add_to_non_ext_dict(self, key: str, val: Value, line: int) -> None:
1315
1315
self .primitive_op (dict_set_item_op , [self .non_ext_info .dict , key_unicode , val ], line )
1316
1316
1317
1317
def add_non_ext_class_attr (self , lvalue : NameExpr ,
1318
- stmt : AssignmentStmt , class_ir : ClassIR ) -> None :
1318
+ stmt : AssignmentStmt , cdef : ClassDef ,
1319
+ attr_to_cache : List [Lvalue ]) -> None :
1319
1320
"""
1320
1321
Add a class attribute to __annotations__ of a non-extension class. If the
1321
1322
attribute is assigned to a value, it is also added to __dict__.
@@ -1332,7 +1333,12 @@ def add_non_ext_class_attr(self, lvalue: NameExpr,
1332
1333
# Only add the attribute to the __dict__ if the assignment is of the form:
1333
1334
# x: type = value (don't add attributes of the form 'x: type' to the __dict__).
1334
1335
if not isinstance (stmt .rvalue , TempNode ):
1335
- self .add_to_non_ext_dict (lvalue .name , self .accept (stmt .rvalue ), stmt .line )
1336
+ rvalue = self .accept (stmt .rvalue )
1337
+ self .add_to_non_ext_dict (lvalue .name , rvalue , stmt .line )
1338
+ # We cache enum attributes to speed up enum attribute lookup since they
1339
+ # are final.
1340
+ if cdef .info .bases and cdef .info .bases [0 ].type .fullname () == 'enum.Enum' :
1341
+ attr_to_cache .append (lvalue )
1336
1342
1337
1343
def setup_non_ext_dict (self , cdef : ClassDef , bases : Value ) -> Value :
1338
1344
"""
@@ -1367,6 +1373,14 @@ def setup_non_ext_dict(self, cdef: ClassDef, bases: Value) -> Value:
1367
1373
1368
1374
return non_ext_dict
1369
1375
1376
+ def cache_class_attrs (self , attrs_to_cache : List [Lvalue ], cdef : ClassDef ) -> None :
1377
+ """Add class attributes to be cached to the global cache"""
1378
+ typ = self .load_native_type_object (cdef .fullname )
1379
+ for lval in attrs_to_cache :
1380
+ assert isinstance (lval , NameExpr )
1381
+ rval = self .py_get_attr (typ , lval .name , cdef .line )
1382
+ self .init_final_static (lval , rval , cdef .fullname )
1383
+
1370
1384
def visit_class_def (self , cdef : ClassDef ) -> None :
1371
1385
ir = self .mapper .type_to_ir [cdef .info ]
1372
1386
# Currently, we only create non-extension classes for classes that are
@@ -1382,9 +1396,10 @@ def visit_class_def(self, cdef: ClassDef) -> None:
1382
1396
# because dataclasses uses it to determine which attributes to compute on.
1383
1397
# TODO: Maybe generate more precise types for annotations
1384
1398
non_ext_anns = self .primitive_op (new_dict_op , [], cdef .line )
1385
-
1386
1399
self .non_ext_info = NonExtClassInfo (non_ext_dict , non_ext_bases , non_ext_anns )
1387
1400
1401
+ attrs_to_cache = [] # type: List[Lvalue]
1402
+
1388
1403
for stmt in cdef .defs .body :
1389
1404
if isinstance (stmt , OverloadedFuncDef ) and stmt .is_property :
1390
1405
if not ir .is_ext_class :
@@ -1415,7 +1430,7 @@ def visit_class_def(self, cdef: ClassDef) -> None:
1415
1430
stmt .line )
1416
1431
continue
1417
1432
if not ir .is_ext_class :
1418
- self .add_non_ext_class_attr (lvalue , stmt , ir )
1433
+ self .add_non_ext_class_attr (lvalue , stmt , cdef , attrs_to_cache )
1419
1434
continue
1420
1435
# Variable declaration with no body
1421
1436
if isinstance (stmt .rvalue , TempNode ):
@@ -1451,6 +1466,9 @@ def visit_class_def(self, cdef: ClassDef) -> None:
1451
1466
[self .load_globals_dict (), self .load_static_unicode (cdef .name ),
1452
1467
non_ext_class ], cdef .line )
1453
1468
1469
+ # Cache any cachable class attributes
1470
+ self .cache_class_attrs (attrs_to_cache , cdef )
1471
+
1454
1472
# Set this attribute back to None until the next non-extension class is visited.
1455
1473
self .non_ext_info = None
1456
1474
@@ -2724,10 +2742,14 @@ def get_final_ref(self, expr: MemberExpr) -> Optional[Tuple[str, Var, bool]]:
2724
2742
if isinstance (expr .expr , RefExpr ) and isinstance (expr .expr .node , TypeInfo ):
2725
2743
# a class attribute
2726
2744
sym = expr .expr .node .get (expr .name )
2727
- if sym and isinstance (sym .node , Var ) and sym .node .is_final :
2728
- final_var = sym .node
2729
- fullname = '{}.{}' .format (sym .node .info .fullname (), final_var .name ())
2730
- native = expr .expr .node .module_name in self .modules
2745
+ if sym and isinstance (sym .node , Var ):
2746
+ # Enum attribute are treated as final since they are added to the global cache
2747
+ expr_fullname = expr .expr .node .bases [0 ].type .fullname ()
2748
+ is_final = sym .node .is_final or expr_fullname == 'enum.Enum'
2749
+ if is_final :
2750
+ final_var = sym .node
2751
+ fullname = '{}.{}' .format (sym .node .info .fullname (), final_var .name ())
2752
+ native = expr .expr .node .module_name in self .modules
2731
2753
elif self .is_module_member_expr (expr ):
2732
2754
# a module attribute
2733
2755
if isinstance (expr .node , Var ) and expr .node .is_final :
0 commit comments