Skip to content

Commit 8aece72

Browse files
taionsigmavirus24
authored andcommitted
Handle string literal annotations (#313)
1 parent 8d0f995 commit 8aece72

File tree

3 files changed

+121
-4
lines changed

3 files changed

+121
-4
lines changed

pyflakes/checker.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,39 @@ def handleDoctests(self, node):
922922
self.popScope()
923923
self.scopeStack = saved_stack
924924

925+
def handleAnnotation(self, annotation, node):
926+
if isinstance(annotation, ast.Str):
927+
# Defer handling forward annotation.
928+
def handleForwardAnnotation():
929+
try:
930+
tree = ast.parse(annotation.s)
931+
except SyntaxError:
932+
self.report(
933+
messages.ForwardAnnotationSyntaxError,
934+
node,
935+
annotation.s,
936+
)
937+
return
938+
939+
body = tree.body
940+
if len(body) != 1 or not isinstance(body[0], ast.Expr):
941+
self.report(
942+
messages.ForwardAnnotationSyntaxError,
943+
node,
944+
annotation.s,
945+
)
946+
return
947+
948+
parsed_annotation = tree.body[0].value
949+
for descendant in ast.walk(parsed_annotation):
950+
ast.copy_location(descendant, annotation)
951+
952+
self.handleNode(parsed_annotation, node)
953+
954+
self.deferFunction(handleForwardAnnotation)
955+
else:
956+
self.handleNode(annotation, node)
957+
925958
def ignore(self, node):
926959
pass
927960

@@ -1160,9 +1193,11 @@ def addArgs(arglist):
11601193
if arg in args[:idx]:
11611194
self.report(messages.DuplicateArgument, node, arg)
11621195

1163-
for child in annotations + defaults:
1164-
if child:
1165-
self.handleNode(child, node)
1196+
for annotation in annotations:
1197+
self.handleAnnotation(annotation, node)
1198+
1199+
for default in defaults:
1200+
self.handleNode(default, node)
11661201

11671202
def runFunction():
11681203

@@ -1375,7 +1410,7 @@ def ANNASSIGN(self, node):
13751410
# Otherwise it's not really ast.Store and shouldn't silence
13761411
# UndefinedLocal warnings.
13771412
self.handleNode(node.target, node)
1378-
self.handleNode(node.annotation, node)
1413+
self.handleAnnotation(node.annotation, node)
13791414
if node.value:
13801415
# If the assignment has value, handle the *value* now.
13811416
self.handleNode(node.value, node)

pyflakes/messages.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,11 @@ class AssertTuple(Message):
231231
Assertion test is a tuple, which are always True.
232232
"""
233233
message = 'assertion is always true, perhaps remove parentheses?'
234+
235+
236+
class ForwardAnnotationSyntaxError(Message):
237+
message = 'syntax error in forward annotation %r'
238+
239+
def __init__(self, filename, loc, annotation):
240+
Message.__init__(self, filename, loc)
241+
self.message_args = (annotation,)

pyflakes/test/test_other.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,3 +1890,77 @@ def f():
18901890
class C:
18911891
foo: not_a_real_type = None
18921892
''', m.UndefinedName)
1893+
self.flakes('''
1894+
from foo import Bar
1895+
bar: Bar
1896+
''')
1897+
self.flakes('''
1898+
from foo import Bar
1899+
bar: 'Bar'
1900+
''')
1901+
self.flakes('''
1902+
import foo
1903+
bar: foo.Bar
1904+
''')
1905+
self.flakes('''
1906+
import foo
1907+
bar: 'foo.Bar'
1908+
''')
1909+
self.flakes('''
1910+
from foo import Bar
1911+
def f(bar: Bar): pass
1912+
''')
1913+
self.flakes('''
1914+
from foo import Bar
1915+
def f(bar: 'Bar'): pass
1916+
''')
1917+
self.flakes('''
1918+
from foo import Bar
1919+
def f(bar) -> Bar: return bar
1920+
''')
1921+
self.flakes('''
1922+
from foo import Bar
1923+
def f(bar) -> 'Bar': return bar
1924+
''')
1925+
self.flakes('''
1926+
bar: 'Bar'
1927+
''', m.UndefinedName)
1928+
self.flakes('''
1929+
bar: 'foo.Bar'
1930+
''', m.UndefinedName)
1931+
self.flakes('''
1932+
from foo import Bar
1933+
bar: str
1934+
''', m.UnusedImport)
1935+
self.flakes('''
1936+
from foo import Bar
1937+
def f(bar: str): pass
1938+
''', m.UnusedImport)
1939+
self.flakes('''
1940+
def f(a: A) -> A: pass
1941+
class A: pass
1942+
''', m.UndefinedName, m.UndefinedName)
1943+
self.flakes('''
1944+
def f(a: 'A') -> 'A': return a
1945+
class A: pass
1946+
''')
1947+
self.flakes('''
1948+
a: A
1949+
class A: pass
1950+
''', m.UndefinedName)
1951+
self.flakes('''
1952+
a: 'A'
1953+
class A: pass
1954+
''')
1955+
self.flakes('''
1956+
a: 'A B'
1957+
''', m.ForwardAnnotationSyntaxError)
1958+
self.flakes('''
1959+
a: 'A; B'
1960+
''', m.ForwardAnnotationSyntaxError)
1961+
self.flakes('''
1962+
a: '1 + 2'
1963+
''')
1964+
self.flakes('''
1965+
a: 'a: "A"'
1966+
''', m.ForwardAnnotationSyntaxError)

0 commit comments

Comments
 (0)