Skip to content

Commit

Permalink
Handle string literal annotations (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
taion authored and sigmavirus24 committed Nov 25, 2017
1 parent 8d0f995 commit 8aece72
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 4 deletions.
43 changes: 39 additions & 4 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,39 @@ def handleDoctests(self, node):
self.popScope()
self.scopeStack = saved_stack

def handleAnnotation(self, annotation, node):
if isinstance(annotation, ast.Str):
# Defer handling forward annotation.
def handleForwardAnnotation():
try:
tree = ast.parse(annotation.s)
except SyntaxError:
self.report(
messages.ForwardAnnotationSyntaxError,
node,
annotation.s,
)
return

body = tree.body
if len(body) != 1 or not isinstance(body[0], ast.Expr):
self.report(
messages.ForwardAnnotationSyntaxError,
node,
annotation.s,
)
return

parsed_annotation = tree.body[0].value
for descendant in ast.walk(parsed_annotation):
ast.copy_location(descendant, annotation)

self.handleNode(parsed_annotation, node)

self.deferFunction(handleForwardAnnotation)
else:
self.handleNode(annotation, node)

def ignore(self, node):
pass

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

for child in annotations + defaults:
if child:
self.handleNode(child, node)
for annotation in annotations:
self.handleAnnotation(annotation, node)

for default in defaults:
self.handleNode(default, node)

def runFunction():

Expand Down Expand Up @@ -1375,7 +1410,7 @@ def ANNASSIGN(self, node):
# Otherwise it's not really ast.Store and shouldn't silence
# UndefinedLocal warnings.
self.handleNode(node.target, node)
self.handleNode(node.annotation, node)
self.handleAnnotation(node.annotation, node)
if node.value:
# If the assignment has value, handle the *value* now.
self.handleNode(node.value, node)
8 changes: 8 additions & 0 deletions pyflakes/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,11 @@ class AssertTuple(Message):
Assertion test is a tuple, which are always True.
"""
message = 'assertion is always true, perhaps remove parentheses?'


class ForwardAnnotationSyntaxError(Message):
message = 'syntax error in forward annotation %r'

def __init__(self, filename, loc, annotation):
Message.__init__(self, filename, loc)
self.message_args = (annotation,)
74 changes: 74 additions & 0 deletions pyflakes/test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -1890,3 +1890,77 @@ def f():
class C:
foo: not_a_real_type = None
''', m.UndefinedName)
self.flakes('''
from foo import Bar
bar: Bar
''')
self.flakes('''
from foo import Bar
bar: 'Bar'
''')
self.flakes('''
import foo
bar: foo.Bar
''')
self.flakes('''
import foo
bar: 'foo.Bar'
''')
self.flakes('''
from foo import Bar
def f(bar: Bar): pass
''')
self.flakes('''
from foo import Bar
def f(bar: 'Bar'): pass
''')
self.flakes('''
from foo import Bar
def f(bar) -> Bar: return bar
''')
self.flakes('''
from foo import Bar
def f(bar) -> 'Bar': return bar
''')
self.flakes('''
bar: 'Bar'
''', m.UndefinedName)
self.flakes('''
bar: 'foo.Bar'
''', m.UndefinedName)
self.flakes('''
from foo import Bar
bar: str
''', m.UnusedImport)
self.flakes('''
from foo import Bar
def f(bar: str): pass
''', m.UnusedImport)
self.flakes('''
def f(a: A) -> A: pass
class A: pass
''', m.UndefinedName, m.UndefinedName)
self.flakes('''
def f(a: 'A') -> 'A': return a
class A: pass
''')
self.flakes('''
a: A
class A: pass
''', m.UndefinedName)
self.flakes('''
a: 'A'
class A: pass
''')
self.flakes('''
a: 'A B'
''', m.ForwardAnnotationSyntaxError)
self.flakes('''
a: 'A; B'
''', m.ForwardAnnotationSyntaxError)
self.flakes('''
a: '1 + 2'
''')
self.flakes('''
a: 'a: "A"'
''', m.ForwardAnnotationSyntaxError)

0 comments on commit 8aece72

Please sign in to comment.