Repository: PyCQA/pyflakes
Pull request #84 (2016-12-22 03:47:16)

Enable support for PEP 526 annotated assignments
Without this change, code with annotated assignments is crashing pyflakes with an attribute error (`object has no attribute 'ANNASSIGN'`).

**Test plan**: new tests introduced conforming to behavior described in the PEP.

ambv: The AppVeyor failure is unrelated (`RuntimeError: Python 3.3 or later is required`, covered in #83).

Patch:
diff --git a/NEWS.txt b/NEWS.txt
index d54109b..021ff6e 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,3 +1,7 @@
+1.4.0 (2016-12-30):
+  - Change formatting of ImportStarMessage to be consistent with other errors
+  - Support PEP 498 "f-strings"
+
 1.3.0 (2016-09-01):
   - Fix PyPy2 Windows IntegrationTests
   - Check for duplicate dictionary keys
diff --git a/pyflakes/__init__.py b/pyflakes/__init__.py
index 19b4f1d..96e3ce8 100644
--- a/pyflakes/__init__.py
+++ b/pyflakes/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '1.3.0'
+__version__ = '1.4.0'
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index caa9db5..3e8dc8d 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -1335,3 +1335,21 @@ class Checker(object):
                 del self.scope[node.name]
             except KeyError:
                 pass
+
+    def ANNASSIGN(self, node):
+        """
+        Annotated assignments don't have annotations evaluated on function
+        scope, hence the custom implementation.
+
+        See: PEP 526.
+        """
+        if node.value:
+            # Only bind the *targets* if the assignment has a value.
+            # Otherwise it's not really ast.Store and shouldn't silence
+            # UndefinedLocal warnings.
+            self.handleNode(node.target, node)
+        if not isinstance(self.scope, FunctionScope):
+            self.handleNode(node.annotation, node)
+        if node.value:
+            # If the assignment has value, handle the *value* now.
+            self.handleNode(node.value, node)


Test patch:
diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py
index 109bc66..6f02001 100644
--- a/pyflakes/test/test_other.py
+++ b/pyflakes/test/test_other.py
@@ -1808,3 +1808,64 @@ class TestAsyncStatements(TestCase):
         mom = 'mom'
         f'{hi} {mom}'
         ''')
+
+    @skipIf(version_info < (3, 6), 'new in Python 3.6')
+    def test_variable_annotations(self):
+        self.flakes('''
+        name: str
+        age: int
+        ''')
+        self.flakes('''
+        name: str = 'Bob'
+        age: int = 18
+        ''')
+        self.flakes('''
+        class C:
+            name: str
+            age: int
+        ''')
+        self.flakes('''
+        class C:
+            name: str = 'Bob'
+            age: int = 18
+        ''')
+        self.flakes('''
+        def f():
+            name: str
+            age: int
+        ''')
+        self.flakes('''
+        def f():
+            name: str = 'Bob'
+            age: int = 18
+            foo: not_a_real_type = None
+        ''', m.UnusedVariable, m.UnusedVariable, m.UnusedVariable)
+        self.flakes('''
+        def f():
+            name: str
+            print(name)
+        ''', m.UndefinedName)
+        self.flakes('''
+        foo: not_a_real_type
+        ''', m.UndefinedName)
+        self.flakes('''
+        foo: not_a_real_type = None
+        ''', m.UndefinedName)
+        self.flakes('''
+        class C:
+            foo: not_a_real_type
+        ''', m.UndefinedName)
+        self.flakes('''
+        class C:
+            foo: not_a_real_type = None
+        ''', m.UndefinedName)
+        self.flakes('''
+        def f():
+            class C:
+                foo: not_a_real_type
+        ''', m.UndefinedName)
+        self.flakes('''
+        def f():
+            class C:
+                foo: not_a_real_type = None
+        ''', m.UndefinedName)