From 353874d320fe8cbebfdd4dae0797a57fcd8c1bf9 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Fri, 10 May 2019 13:41:15 -0700 Subject: [PATCH 1/3] bpo-36878: Allow extra text after `# type: ignore` comments In the parser, when using the type_comments=True option, recognize a TYPE_IGNORE as anything containing `# type: ignore` followed by a non-alphanumeric character. This is to allow ignores such as `# type: ignore[E1000]`. --- Lib/test/test_type_comments.py | 7 ++++++- Parser/tokenizer.c | 13 +++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_type_comments.py b/Lib/test/test_type_comments.py index 69a48a104036a0..f6bae0113a1f72 100644 --- a/Lib/test/test_type_comments.py +++ b/Lib/test/test_type_comments.py @@ -76,6 +76,10 @@ def foo(): def bar(): x = 1 # type: ignore + +def baz(): + pass # type: ignore[excuse] + x = 1 # type: ignore whatever """ # Test for long-form type-comments in arguments. A test function @@ -266,7 +270,7 @@ def test_vardecl(self): def test_ignores(self): for tree in self.parse_all(ignores): - self.assertEqual([ti.lineno for ti in tree.type_ignores], [2, 5]) + self.assertEqual([ti.lineno for ti in tree.type_ignores], [2, 5, 8, 9]) tree = self.classic_parse(ignores) self.assertEqual(tree.type_ignores, []) @@ -318,6 +322,7 @@ def check_both_ways(source): check_both_ways("while True:\n continue # type: int\n") check_both_ways("try: # type: int\n pass\nfinally:\n pass\n") check_both_ways("try:\n pass\nfinally: # type: int\n pass\n") + check_both_ways("pass # type: ignorewhatever\n") def test_func_type_input(self): diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index e8068f268074b1..5dc2ae65c42daf 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1272,14 +1272,11 @@ tok_get(struct tok_state *tok, char **p_start, char **p_end) type_start = p; - is_type_ignore = tok->cur >= p + 6 && memcmp(p, "ignore", 6) == 0; - p += 6; - while (is_type_ignore && p < tok->cur) { - if (*p == '#') - break; - is_type_ignore = is_type_ignore && (*p == ' ' || *p == '\t'); - p++; - } + /* A TYPE_IGNORE is "type: ignore" followed by the end of the token + * or anything non-alphanumeric. */ + is_type_ignore = ( + tok->cur >= p + 6 && memcmp(p, "ignore", 6) == 0 + && !(tok->cur > p + 6 && isalnum(p[6]))); if (is_type_ignore) { /* If this type ignore is the only thing on the line, consume the newline also. */ From 75dab92368fafaf0ca550b24137ee9b8f08b2a47 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" Date: Fri, 10 May 2019 22:00:08 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2019-05-10-22-00-06.bpo-36878.iigeqk.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2019-05-10-22-00-06.bpo-36878.iigeqk.rst diff --git a/Misc/NEWS.d/next/Library/2019-05-10-22-00-06.bpo-36878.iigeqk.rst b/Misc/NEWS.d/next/Library/2019-05-10-22-00-06.bpo-36878.iigeqk.rst new file mode 100644 index 00000000000000..20b44dcbf13fce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-10-22-00-06.bpo-36878.iigeqk.rst @@ -0,0 +1,4 @@ +When using `type_comments=True` in `ast.parse`, treat `# type: ignore` followed by +a non-alphanumeric character and then arbitrary text as a type ignore, instead of +requiring nothing but whitespace or another comment. This is to permit formations +such as `# type: ignore[E1000]`. \ No newline at end of file From ec333de99623d5477e9f81194cd0036f311a58bd Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Fri, 10 May 2019 18:24:35 -0700 Subject: [PATCH 3/3] Add some more tests --- Lib/test/test_type_comments.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_type_comments.py b/Lib/test/test_type_comments.py index f6bae0113a1f72..b4318902ee344e 100644 --- a/Lib/test/test_type_comments.py +++ b/Lib/test/test_type_comments.py @@ -79,6 +79,8 @@ def bar(): def baz(): pass # type: ignore[excuse] + pass # type: ignore=excuse + pass # type: ignore [excuse] x = 1 # type: ignore whatever """ @@ -270,7 +272,7 @@ def test_vardecl(self): def test_ignores(self): for tree in self.parse_all(ignores): - self.assertEqual([ti.lineno for ti in tree.type_ignores], [2, 5, 8, 9]) + self.assertEqual([ti.lineno for ti in tree.type_ignores], [2, 5, 8, 9, 10, 11]) tree = self.classic_parse(ignores) self.assertEqual(tree.type_ignores, [])