@@ -79,6 +79,10 @@ def getAlternatives(n):
79
79
LOOP_TYPES = (ast .While , ast .For )
80
80
FUNCTION_TYPES = (ast .FunctionDef ,)
81
81
82
+ if PY36_PLUS :
83
+ ANNASSIGN_TYPES = (ast .AnnAssign ,)
84
+ else :
85
+ ANNASSIGN_TYPES = ()
82
86
83
87
if PY38_PLUS :
84
88
def _is_singleton (node ): # type: (ast.AST) -> bool
@@ -528,6 +532,16 @@ class Assignment(Binding):
528
532
"""
529
533
530
534
535
+ class Annotation (Binding ):
536
+ """
537
+ Represents binding a name to a type without an associated value.
538
+
539
+ As long as this name is not assigned a value in another binding, it is considered
540
+ undefined for most purposes. One notable exception is using the name as a type
541
+ annotation.
542
+ """
543
+
544
+
531
545
class FunctionDefinition (Definition ):
532
546
pass
533
547
@@ -732,6 +746,12 @@ def is_typing_overload(value, scope_stack):
732
746
)
733
747
734
748
749
+ class AnnotationState :
750
+ NONE = 0
751
+ STRING = 1
752
+ BARE = 2
753
+
754
+
735
755
def in_annotation (func ):
736
756
@functools .wraps (func )
737
757
def in_annotation_func (self , * args , ** kwargs ):
@@ -740,6 +760,14 @@ def in_annotation_func(self, *args, **kwargs):
740
760
return in_annotation_func
741
761
742
762
763
+ def in_string_annotation (func ):
764
+ @functools .wraps (func )
765
+ def in_annotation_func (self , * args , ** kwargs ):
766
+ with self ._enter_annotation (AnnotationState .STRING ):
767
+ return func (self , * args , ** kwargs )
768
+ return in_annotation_func
769
+
770
+
743
771
def make_tokens (code ):
744
772
# PY3: tokenize.tokenize requires readline of bytes
745
773
if not isinstance (code , bytes ):
@@ -826,7 +854,7 @@ class Checker(object):
826
854
nodeDepth = 0
827
855
offset = None
828
856
traceTree = False
829
- _in_annotation = False
857
+ _in_annotation = AnnotationState . NONE
830
858
_in_typing_literal = False
831
859
_in_deferred = False
832
860
@@ -1146,8 +1174,11 @@ def handleNodeLoad(self, node):
1146
1174
# iteration
1147
1175
continue
1148
1176
1149
- if (name == 'print' and
1150
- isinstance (scope .get (name , None ), Builtin )):
1177
+ binding = scope .get (name , None )
1178
+ if isinstance (binding , Annotation ) and not self ._in_postponed_annotation :
1179
+ continue
1180
+
1181
+ if name == 'print' and isinstance (binding , Builtin ):
1151
1182
parent = self .getParent (node )
1152
1183
if (isinstance (parent , ast .BinOp ) and
1153
1184
isinstance (parent .op , ast .RShift )):
@@ -1222,7 +1253,9 @@ def handleNodeStore(self, node):
1222
1253
break
1223
1254
1224
1255
parent_stmt = self .getParent (node )
1225
- if isinstance (parent_stmt , (FOR_TYPES , ast .comprehension )) or (
1256
+ if isinstance (parent_stmt , ANNASSIGN_TYPES ) and parent_stmt .value is None :
1257
+ binding = Annotation (name , node )
1258
+ elif isinstance (parent_stmt , (FOR_TYPES , ast .comprehension )) or (
1226
1259
parent_stmt != node ._pyflakes_parent and
1227
1260
not self .isLiteralTupleUnpacking (parent_stmt )):
1228
1261
binding = Binding (name , node )
@@ -1265,13 +1298,20 @@ def on_conditional_branch():
1265
1298
self .report (messages .UndefinedName , node , name )
1266
1299
1267
1300
@contextlib .contextmanager
1268
- def _enter_annotation (self ):
1269
- orig , self ._in_annotation = self ._in_annotation , True
1301
+ def _enter_annotation (self , ann_type = AnnotationState . BARE ):
1302
+ orig , self ._in_annotation = self ._in_annotation , ann_type
1270
1303
try :
1271
1304
yield
1272
1305
finally :
1273
1306
self ._in_annotation = orig
1274
1307
1308
+ @property
1309
+ def _in_postponed_annotation (self ):
1310
+ return (
1311
+ self ._in_annotation == AnnotationState .STRING or
1312
+ self .annotationsFutureEnabled
1313
+ )
1314
+
1275
1315
def _handle_type_comments (self , node ):
1276
1316
for (lineno , col_offset ), comment in self ._type_comments .get (node , ()):
1277
1317
comment = comment .split (':' , 1 )[1 ].strip ()
@@ -1399,7 +1439,7 @@ def handleDoctests(self, node):
1399
1439
self .popScope ()
1400
1440
self .scopeStack = saved_stack
1401
1441
1402
- @in_annotation
1442
+ @in_string_annotation
1403
1443
def handleStringAnnotation (self , s , node , ref_lineno , ref_col_offset , err ):
1404
1444
try :
1405
1445
tree = ast .parse (s )
@@ -1611,7 +1651,7 @@ def CALL(self, node):
1611
1651
len (node .args ) >= 1 and
1612
1652
isinstance (node .args [0 ], ast .Str )
1613
1653
):
1614
- with self ._enter_annotation ():
1654
+ with self ._enter_annotation (AnnotationState . STRING ):
1615
1655
self .handleNode (node .args [0 ], node )
1616
1656
1617
1657
self .handleChildren (node )
@@ -2224,11 +2264,7 @@ def EXCEPTHANDLER(self, node):
2224
2264
self .scope [node .name ] = prev_definition
2225
2265
2226
2266
def ANNASSIGN (self , node ):
2227
- if node .value :
2228
- # Only bind the *targets* if the assignment has a value.
2229
- # Otherwise it's not really ast.Store and shouldn't silence
2230
- # UndefinedLocal warnings.
2231
- self .handleNode (node .target , node )
2267
+ self .handleNode (node .target , node )
2232
2268
self .handleAnnotation (node .annotation , node )
2233
2269
if node .value :
2234
2270
# If the assignment has value, handle the *value* now.
0 commit comments