@@ -618,40 +618,55 @@ def getNodeName(node):
618
618
return node .name
619
619
620
620
621
- def is_typing_overload (value , scope_stack ):
622
- def name_is_typing_overload (name ): # type: (str) -> bool
621
+ def _is_typing (node , typing_attr , scope_stack ):
622
+ def _bare_name_is_attr (name ):
623
+ expected_typing_names = {
624
+ 'typing.{}' .format (typing_attr ),
625
+ 'typing_extensions.{}' .format (typing_attr ),
626
+ }
623
627
for scope in reversed (scope_stack ):
624
628
if name in scope :
625
629
return (
626
630
isinstance (scope [name ], ImportationFrom ) and
627
- scope [name ].fullName in (
628
- 'typing.overload' , 'typing_extensions.overload' ,
629
- )
631
+ scope [name ].fullName in expected_typing_names
630
632
)
631
633
632
634
return False
633
635
634
- def is_typing_overload_decorator (node ):
635
- return (
636
- (
637
- isinstance (node , ast .Name ) and name_is_typing_overload (node .id )
638
- ) or (
639
- isinstance (node , ast .Attribute ) and
640
- isinstance (node .value , ast .Name ) and
641
- node .value .id == 'typing' and
642
- node .attr == 'overload'
643
- )
636
+ return (
637
+ (
638
+ isinstance (node , ast .Name ) and
639
+ _bare_name_is_attr (node .id )
640
+ ) or (
641
+ isinstance (node , ast .Attribute ) and
642
+ isinstance (node .value , ast .Name ) and
643
+ node .value .id in {'typing' , 'typing_extensions' } and
644
+ node .attr == typing_attr
644
645
)
646
+ )
645
647
648
+
649
+ def is_typing_overload (value , scope_stack ):
646
650
return (
647
651
isinstance (value .source , FUNCTION_TYPES ) and
648
652
any (
649
- is_typing_overload_decorator (dec )
653
+ _is_typing (dec , 'overload' , scope_stack )
650
654
for dec in value .source .decorator_list
651
655
)
652
656
)
653
657
654
658
659
+ def in_annotation (func ):
660
+ @functools .wraps (func )
661
+ def in_annotation_func (self , * args , ** kwargs ):
662
+ orig , self ._in_annotation = self ._in_annotation , True
663
+ try :
664
+ return func (self , * args , ** kwargs )
665
+ finally :
666
+ self ._in_annotation = orig
667
+ return in_annotation_func
668
+
669
+
655
670
def make_tokens (code ):
656
671
# PY3: tokenize.tokenize requires readline of bytes
657
672
if not isinstance (code , bytes ):
@@ -738,6 +753,9 @@ class Checker(object):
738
753
nodeDepth = 0
739
754
offset = None
740
755
traceTree = False
756
+ _in_annotation = False
757
+ _in_typing_literal = False
758
+ _in_deferred = False
741
759
742
760
builtIns = set (builtin_vars ).union (_MAGIC_GLOBALS )
743
761
_customBuiltIns = os .environ .get ('PYFLAKES_BUILTINS' )
@@ -769,6 +787,7 @@ def __init__(self, tree, filename='(none)', builtins=None,
769
787
for builtin in self .builtIns :
770
788
self .addBinding (None , Builtin (builtin ))
771
789
self .handleChildren (tree )
790
+ self ._in_deferred = True
772
791
self .runDeferred (self ._deferredFunctions )
773
792
# Set _deferredFunctions to None so that deferFunction will fail
774
793
# noisily if called after we've run through the deferred functions.
@@ -1299,6 +1318,7 @@ def handleDoctests(self, node):
1299
1318
self .popScope ()
1300
1319
self .scopeStack = saved_stack
1301
1320
1321
+ @in_annotation
1302
1322
def handleStringAnnotation (self , s , node , ref_lineno , ref_col_offset , err ):
1303
1323
try :
1304
1324
tree = ast .parse (s )
@@ -1322,6 +1342,7 @@ def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
1322
1342
1323
1343
self .handleNode (parsed_annotation , node )
1324
1344
1345
+ @in_annotation
1325
1346
def handleAnnotation (self , annotation , node ):
1326
1347
if isinstance (annotation , ast .Str ):
1327
1348
# Defer handling forward annotation.
@@ -1334,7 +1355,8 @@ def handleAnnotation(self, annotation, node):
1334
1355
messages .ForwardAnnotationSyntaxError ,
1335
1356
))
1336
1357
elif self .annotationsFutureEnabled :
1337
- self .deferFunction (lambda : self .handleNode (annotation , node ))
1358
+ fn = in_annotation (Checker .handleNode )
1359
+ self .deferFunction (lambda : fn (self , annotation , node ))
1338
1360
else :
1339
1361
self .handleNode (annotation , node )
1340
1362
@@ -1350,9 +1372,19 @@ def ignore(self, node):
1350
1372
1351
1373
# "expr" type nodes
1352
1374
BOOLOP = UNARYOP = IFEXP = SET = \
1353
- REPR = ATTRIBUTE = SUBSCRIPT = \
1375
+ REPR = ATTRIBUTE = \
1354
1376
STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren
1355
1377
1378
+ def SUBSCRIPT (self , node ):
1379
+ if _is_typing (node .value , 'Literal' , self .scopeStack ):
1380
+ orig , self ._in_typing_literal = self ._in_typing_literal , True
1381
+ try :
1382
+ self .handleChildren (node )
1383
+ finally :
1384
+ self ._in_typing_literal = orig
1385
+ else :
1386
+ self .handleChildren (node )
1387
+
1356
1388
def _handle_string_dot_format (self , node ):
1357
1389
try :
1358
1390
placeholders = tuple (parse_format_string (node .func .value .s ))
@@ -1593,7 +1625,27 @@ def BINOP(self, node):
1593
1625
self ._handle_percent_format (node )
1594
1626
self .handleChildren (node )
1595
1627
1596
- NUM = STR = BYTES = ELLIPSIS = CONSTANT = ignore
1628
+ def STR (self , node ):
1629
+ if self ._in_annotation and not self ._in_typing_literal :
1630
+ fn = functools .partial (
1631
+ self .handleStringAnnotation ,
1632
+ node .s ,
1633
+ node ,
1634
+ node .lineno ,
1635
+ node .col_offset ,
1636
+ messages .ForwardAnnotationSyntaxError ,
1637
+ )
1638
+ if self ._in_deferred :
1639
+ fn ()
1640
+ else :
1641
+ self .deferFunction (fn )
1642
+
1643
+ if PY38_PLUS :
1644
+ def CONSTANT (self , node ):
1645
+ if isinstance (node .value , str ):
1646
+ return self .STR (node )
1647
+ else :
1648
+ NUM = BYTES = ELLIPSIS = CONSTANT = ignore
1597
1649
1598
1650
# "slice" type nodes
1599
1651
SLICE = EXTSLICE = INDEX = handleChildren
0 commit comments