From b9267e139cb5ce0bfaa189c0ab7597afb9e90ec5 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Fri, 19 Jul 2019 13:42:04 -0700 Subject: [PATCH 1/2] In PEP 484 type comments, allow text after "# type: ignore" This is to support allowing typecheckers to implement ignores for specific errors, using syntax like `# type: ignore=E1000` or `# type: ignore[type-mismatch` or some such. mypy is about to add support for ignoring specific errors following this design: https://github.com/python/mypy/issues/7239 Support for extra text in type comments was implemented in CPython as https://bugs.python.org/issue36878 and in typed_ast as https://github.com/python/typed_ast/pull/116. --- pyflakes/checker.py | 10 ++++++---- pyflakes/test/test_checker.py | 4 ---- pyflakes/test/test_type_annotations.py | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/pyflakes/checker.py b/pyflakes/checker.py index acc2ae74..6bf9f9ea 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -73,11 +73,13 @@ def getAlternatives(n): FOR_TYPES = (ast.For,) LOOP_TYPES = (ast.While, ast.For) -# https://github.com/python/typed_ast/blob/55420396/ast27/Parser/tokenizer.c#L102-L104 +# https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') -# https://github.com/python/typed_ast/blob/55420396/ast27/Parser/tokenizer.c#L1400 -TYPE_IGNORE_RE = re.compile(TYPE_COMMENT_RE.pattern + r'ignore\s*(#|$)') -# https://github.com/python/typed_ast/blob/55420396/ast27/Grammar/Grammar#L147 +# https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413 +ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()]) +TYPE_IGNORE_RE = re.compile( + TYPE_COMMENT_RE.pattern + r'ignore([{}]|$)'.format(ASCII_NON_ALNUM)) +# https://github.com/python/typed_ast/blob/1.4.0/ast27/Grammar/Grammar#L147 TYPE_FUNC_RE = re.compile(r'^(\(.*?\))\s*->\s*(.*)$') diff --git a/pyflakes/test/test_checker.py b/pyflakes/test/test_checker.py index f47588d8..986cbb84 100644 --- a/pyflakes/test/test_checker.py +++ b/pyflakes/test/test_checker.py @@ -152,10 +152,6 @@ def test_type_comment_without_whitespace(self): ret = self._collect('x = 1 #type:int') self.assertSetEqual(ret, {(ast.Assign, ('#type:int',))}) - def test_type_comment_starts_with_word_ignore(self): - ret = self._collect('x = 1 # type: ignore[T]') - self.assertSetEqual(ret, {(ast.Assign, ('# type: ignore[T]',))}) - def test_last_node_wins(self): """ Test that when two typeable nodes are present on a line, the last diff --git a/pyflakes/test/test_type_annotations.py b/pyflakes/test/test_type_annotations.py index 9c34dcfd..bebdef7b 100644 --- a/pyflakes/test/test_type_annotations.py +++ b/pyflakes/test/test_type_annotations.py @@ -343,6 +343,27 @@ def test_typeCommentsAssignedToPreviousNode(self): # type: F """) + def test_typeIgnore(self): + self.flakes(""" + a = 0 # type: ignore + b = 0 # type: ignore[excuse] + c = 0 # type: ignore=excuse + d = 0 # type: ignore [excuse] + e = 0 # type: ignore whatever + """) + + def test_typeIgnoreBogus(self): + self.flakes(""" + x = 1 # type: ignored + """, m.UndefinedName) + + def test_typeIgnoreBogusUnicode(self): + error = (m.CommentAnnotationSyntaxError if version_info < (3,) + else m.UndefinedName) + self.flakes(""" + x = 2 # type: ignore\xc3 + """, error) + @skipIf(version_info < (3,), 'new in Python 3') def test_return_annotation_is_class_scope_variable(self): self.flakes(""" From a405497faba6ae3d83dc4bc29d63b96e1f0d1023 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Fri, 19 Jul 2019 15:07:16 -0700 Subject: [PATCH 2/2] add test back --- pyflakes/test/test_checker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyflakes/test/test_checker.py b/pyflakes/test/test_checker.py index 986cbb84..b5275726 100644 --- a/pyflakes/test/test_checker.py +++ b/pyflakes/test/test_checker.py @@ -152,6 +152,10 @@ def test_type_comment_without_whitespace(self): ret = self._collect('x = 1 #type:int') self.assertSetEqual(ret, {(ast.Assign, ('#type:int',))}) + def test_type_comment_starts_with_word_ignore(self): + ret = self._collect('x = 1 # type: ignore[T]') + self.assertSetEqual(ret, set()) + def test_last_node_wins(self): """ Test that when two typeable nodes are present on a line, the last