diff options
author | Jimmy Jia <tesrin@gmail.com> | 2017-11-25 14:47:21 -0500 |
---|---|---|
committer | Ian Stapleton Cordasco <graffatcolmingov@gmail.com> | 2017-11-25 13:47:21 -0600 |
commit | 8aece72839adb4add6c31ccfcb021cef2d731a81 (patch) | |
tree | 3504711f42a96564d4a578ec335903ff2c05fe87 | |
parent | 8d0f995dafda7105a6966057fd11383f1ea6fb71 (diff) | |
download | pyflakes-8aece72839adb4add6c31ccfcb021cef2d731a81.tar.gz |
Handle string literal annotations (#313)
-rw-r--r-- | pyflakes/checker.py | 43 | ||||
-rw-r--r-- | pyflakes/messages.py | 8 | ||||
-rw-r--r-- | pyflakes/test/test_other.py | 74 |
3 files changed, 121 insertions, 4 deletions
diff --git a/pyflakes/checker.py b/pyflakes/checker.py index d8f093a..a070822 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -922,6 +922,39 @@ class Checker(object): 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 @@ -1160,9 +1193,11 @@ class Checker(object): 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(): @@ -1375,7 +1410,7 @@ class Checker(object): # 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) diff --git a/pyflakes/messages.py b/pyflakes/messages.py index 9e9406c..670f95f 100644 --- a/pyflakes/messages.py +++ b/pyflakes/messages.py @@ -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,) diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py index ba052f1..14f213c 100644 --- a/pyflakes/test/test_other.py +++ b/pyflakes/test/test_other.py @@ -1890,3 +1890,77 @@ class TestAsyncStatements(TestCase): 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) |