@@ -577,6 +577,10 @@ def unused_annotations(self):
577
577
yield name , binding
578
578
579
579
580
+ class TypeScope (Scope ):
581
+ pass
582
+
583
+
580
584
class GeneratorScope (Scope ):
581
585
pass
582
586
@@ -1039,15 +1043,20 @@ def handleNodeLoad(self, node, parent):
1039
1043
if not name :
1040
1044
return
1041
1045
1042
- in_generators = None
1046
+ # only the following can access class scoped variables (since classes
1047
+ # aren't really a scope)
1048
+ # - direct accesses (not within a nested scope)
1049
+ # - generators
1050
+ # - type annotations (for generics, etc.)
1051
+ can_access_class_vars = None
1043
1052
importStarred = None
1044
1053
1045
1054
# try enclosing function scopes and global scope
1046
1055
for scope in self .scopeStack [- 1 ::- 1 ]:
1047
1056
if isinstance (scope , ClassScope ):
1048
1057
if name == '__class__' :
1049
1058
return
1050
- elif in_generators is False :
1059
+ elif can_access_class_vars is False :
1051
1060
# only generators used in a class scope can access the
1052
1061
# names of the class. this is skipped during the first
1053
1062
# iteration
@@ -1082,8 +1091,10 @@ def handleNodeLoad(self, node, parent):
1082
1091
1083
1092
importStarred = importStarred or scope .importStarred
1084
1093
1085
- if in_generators is not False :
1086
- in_generators = isinstance (scope , GeneratorScope )
1094
+ if can_access_class_vars is not False :
1095
+ can_access_class_vars = isinstance (
1096
+ scope , (TypeScope , GeneratorScope ),
1097
+ )
1087
1098
1088
1099
if importStarred :
1089
1100
from_list = []
@@ -1310,6 +1321,10 @@ def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
1310
1321
1311
1322
self .handleNode (parsed_annotation , node )
1312
1323
1324
+ def handle_annotation_always_deferred (self , annotation , parent ):
1325
+ fn = in_annotation (Checker .handleNode )
1326
+ self .deferFunction (lambda : fn (self , annotation , parent ))
1327
+
1313
1328
@in_annotation
1314
1329
def handleAnnotation (self , annotation , node ):
1315
1330
if (
@@ -1326,8 +1341,7 @@ def handleAnnotation(self, annotation, node):
1326
1341
messages .ForwardAnnotationSyntaxError ,
1327
1342
))
1328
1343
elif self .annotationsFutureEnabled :
1329
- fn = in_annotation (Checker .handleNode )
1330
- self .deferFunction (lambda : fn (self , annotation , node ))
1344
+ self .handle_annotation_always_deferred (annotation , node )
1331
1345
else :
1332
1346
self .handleNode (annotation , node )
1333
1347
@@ -1902,7 +1916,10 @@ def YIELD(self, node):
1902
1916
def FUNCTIONDEF (self , node ):
1903
1917
for deco in node .decorator_list :
1904
1918
self .handleNode (deco , node )
1905
- self .LAMBDA (node )
1919
+
1920
+ with self ._type_param_scope (node ):
1921
+ self .LAMBDA (node )
1922
+
1906
1923
self .addBinding (node , FunctionDefinition (node .name , node ))
1907
1924
# doctest does not process doctest within a doctest,
1908
1925
# or in nested functions.
@@ -1951,7 +1968,10 @@ def LAMBDA(self, node):
1951
1968
1952
1969
def runFunction ():
1953
1970
with self .in_scope (FunctionScope ):
1954
- self .handleChildren (node , omit = ['decorator_list' , 'returns' ])
1971
+ self .handleChildren (
1972
+ node ,
1973
+ omit = ('decorator_list' , 'returns' , 'type_params' ),
1974
+ )
1955
1975
1956
1976
self .deferFunction (runFunction )
1957
1977
@@ -1969,19 +1989,22 @@ def CLASSDEF(self, node):
1969
1989
"""
1970
1990
for deco in node .decorator_list :
1971
1991
self .handleNode (deco , node )
1972
- for baseNode in node .bases :
1973
- self .handleNode (baseNode , node )
1974
- for keywordNode in node .keywords :
1975
- self .handleNode (keywordNode , node )
1976
- with self .in_scope (ClassScope ):
1977
- # doctest does not process doctest within a doctest
1978
- # classes within classes are processed.
1979
- if (self .withDoctest and
1980
- not self ._in_doctest () and
1981
- not isinstance (self .scope , FunctionScope )):
1982
- self .deferFunction (lambda : self .handleDoctests (node ))
1983
- for stmt in node .body :
1984
- self .handleNode (stmt , node )
1992
+
1993
+ with self ._type_param_scope (node ):
1994
+ for baseNode in node .bases :
1995
+ self .handleNode (baseNode , node )
1996
+ for keywordNode in node .keywords :
1997
+ self .handleNode (keywordNode , node )
1998
+ with self .in_scope (ClassScope ):
1999
+ # doctest does not process doctest within a doctest
2000
+ # classes within classes are processed.
2001
+ if (self .withDoctest and
2002
+ not self ._in_doctest () and
2003
+ not isinstance (self .scope , FunctionScope )):
2004
+ self .deferFunction (lambda : self .handleDoctests (node ))
2005
+ for stmt in node .body :
2006
+ self .handleNode (stmt , node )
2007
+
1985
2008
self .addBinding (node , ClassDefinition (node .name , node ))
1986
2009
1987
2010
def AUGASSIGN (self , node ):
@@ -2155,3 +2178,21 @@ def _match_target(self, node):
2155
2178
self .handleChildren (node )
2156
2179
2157
2180
MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target
2181
+
2182
+ @contextlib .contextmanager
2183
+ def _type_param_scope (self , node ):
2184
+ with contextlib .ExitStack () as ctx :
2185
+ if sys .version_info >= (3 , 12 ):
2186
+ ctx .enter_context (self .in_scope (TypeScope ))
2187
+ for param in node .type_params :
2188
+ self .handleNode (param , node )
2189
+ yield
2190
+
2191
+ def TYPEVAR (self , node ):
2192
+ self .handleNodeStore (node )
2193
+ self .handle_annotation_always_deferred (node .bound , node )
2194
+
2195
+ def TYPEALIAS (self , node ):
2196
+ self .handleNode (node .name , node )
2197
+ with self ._type_param_scope (node ):
2198
+ self .handle_annotation_always_deferred (node .value , node )
0 commit comments