From 53748ea9a393d4d42b99ff03faf0e5f815e5ab81 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 3 Sep 2019 16:48:30 +0100 Subject: [PATCH 01/10] Catch various errors in error codes --- mypy/fastparse.py | 23 +++++++++++++++++------ test-data/unit/check-errorcodes.test | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 27501bc92271..56c9abc24ca3 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -170,13 +170,16 @@ def parse(source: Union[str, bytes], return tree -def parse_type_ignore_tag(tag: Optional[str]) -> List[str]: - # TODO: Implement proper parsing and error checking +def parse_type_ignore_tag(tag: Optional[str]) -> Optional[List[str]]: if not tag: return [] - m = re.match(r'\s*\[([^#]*)\]', tag) + m = re.match(r'\s*\[([^]#]*)\]\s*(#.*)?$', tag) if m is None: - return [] + if tag.strip() == '': + return [] + else: + # Invalid "# type: ignore" comment. + return None return [code.strip() for code in m.group(1).split(',')] @@ -206,6 +209,9 @@ def parse_type_comment(type_comment: str, # Typeshed has a non-optional return type for group! tag = cast(Any, extra_ignore).group(1) # type: Optional[str] ignored = parse_type_ignore_tag(tag) # type: Optional[List[str]] + if ignored is None: + errors.report(line, e.offset, 'Invalid "# type: ignore" comment', + code=codes.SYNTAX) else: ignored = None assert isinstance(typ, ast3_Expression) @@ -451,8 +457,13 @@ def translate_module_id(self, id: str) -> str: return id def visit_Module(self, mod: ast3.Module) -> MypyFile: - self.type_ignores = {ti.lineno: parse_type_ignore_tag(ti.tag) # type: ignore[attr-defined] - for ti in mod.type_ignores} + self.type_ignores = {} + for ti in mod.type_ignores: + parsed = parse_type_ignore_tag(ti.tag) + if parsed is not None: + self.type_ignores[ti.lineno] = parsed + else: + self.fail('Invalid "type: ignore" comment', ti.lineno, -1) body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True)) return MypyFile(body, self.imports, diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 6090cc22ebf6..1dcc68073c26 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -163,6 +163,29 @@ from defusedxml import xyz # type: ignore[import] import nostub # type: ignore[import] from defusedxml import xyz # type: ignore[import] +[case testErrorCodeBadIgnore] +import nostub # type: ignore xyz # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[ # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[foo # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[foo, # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[foo]] # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[foo][bar] # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[foo] [bar] # E: Invalid "type: ignore" comment [syntax] + +x = 0 # type: ignore[ # E: Invalid "type: ignore" comment [syntax] + +[case testErrorCodeBadIgnoreNoExtraComment] +# Omit the E: ... comments, as they affect parsing +import nostub # type: ignore xyz +import nostub # type: ignore[xyz +import nostub # type: ignore[xyz][xyz] +x = 0 # type: ignore[ +[out] +main:2: error: Invalid "type: ignore" comment [syntax] +main:3: error: Invalid "type: ignore" comment [syntax] +main:4: error: Invalid "type: ignore" comment [syntax] +main:5: error: Invalid "type: ignore" comment [syntax] + [case testErrorCodeArgKindAndCount] def f(x: int) -> None: pass # N: "f" defined here f() # E: Too few arguments for "f" [call-arg] From 0637acf55d82e9c883c1deb0cd7a2cb04c4c5f1d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 3 Sep 2019 16:59:36 +0100 Subject: [PATCH 02/10] Fix Python 2 --- mypy/fastparse2.py | 9 +++++++-- test-data/unit/check-errorcodes.test | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index 7dc6261ea858..39a2c1bc187d 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -342,8 +342,13 @@ def translate_module_id(self, id: str) -> str: return id def visit_Module(self, mod: ast27.Module) -> MypyFile: - self.type_ignores = {ti.lineno: parse_type_ignore_tag(ti.tag) # type: ignore[attr-defined] - for ti in mod.type_ignores} + self.type_ignores = {} + for ti in mod.type_ignores: + parsed = parse_type_ignore_tag(ti.tag) + if parsed is not None: + self.type_ignores[ti.lineno] = parsed + else: + self.fail('Invalid "type: ignore" comment', ti.lineno, -1) body = self.fix_function_overloads(self.translate_stmt_list(mod.body)) return MypyFile(body, self.imports, diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 1dcc68073c26..9c380b31fbe5 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -186,6 +186,11 @@ main:3: error: Invalid "type: ignore" comment [syntax] main:4: error: Invalid "type: ignore" comment [syntax] main:5: error: Invalid "type: ignore" comment [syntax] +[case testErrorCodeBadIgnore_python2] +import nostub # type: ignore xyz # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[xyz # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore[xyz][xyz] # E: Invalid "type: ignore" comment [syntax] + [case testErrorCodeArgKindAndCount] def f(x: int) -> None: pass # N: "f" defined here f() # E: Too few arguments for "f" [call-arg] From 83b8626b4f6680e946441e6723c7e66059efe3ea Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 3 Sep 2019 17:29:10 +0100 Subject: [PATCH 03/10] Fixes --- mypy/fastparse.py | 16 +++++++++++----- mypy/fastparse2.py | 12 +++++++++--- test-data/unit/check-errorcodes.test | 27 ++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 56c9abc24ca3..e466ad99a256 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -124,7 +124,7 @@ def ast3_parse(source: Union[str, bytes], filename: str, mode: str, TYPE_COMMENT_SYNTAX_ERROR = 'syntax error in type comment' # type: Final -TYPE_IGNORE_PATTERN = re.compile(r'[^#]*#\s*type:\s*ignore\s*(\[[^[#]*\]\s*)?($|#)') +TYPE_IGNORE_PATTERN = re.compile(r'[^#]*#\s*type:\s*ignore\s*\b(.*)') def parse(source: Union[str, bytes], @@ -208,10 +208,16 @@ def parse_type_comment(type_comment: str, if extra_ignore: # Typeshed has a non-optional return type for group! tag = cast(Any, extra_ignore).group(1) # type: Optional[str] - ignored = parse_type_ignore_tag(tag) # type: Optional[List[str]] + if not tag.strip().startswith('#'): + ignored = parse_type_ignore_tag(tag) # type: Optional[List[str]] + else: + ignored = [] # Ignore everything if ignored is None: - errors.report(line, e.offset, 'Invalid "# type: ignore" comment', - code=codes.SYNTAX) + if errors is not None: + errors.report(line, column, 'Invalid "type: ignore" comment', + code=codes.SYNTAX) + else: + raise SyntaxError else: ignored = None assert isinstance(typ, ast3_Expression) @@ -459,7 +465,7 @@ def translate_module_id(self, id: str) -> str: def visit_Module(self, mod: ast3.Module) -> MypyFile: self.type_ignores = {} for ti in mod.type_ignores: - parsed = parse_type_ignore_tag(ti.tag) + parsed = parse_type_ignore_tag(ti.tag) # type: ignore[attr-defined] if parsed is not None: self.type_ignores[ti.lineno] = parsed else: diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index 39a2c1bc187d..b7a0b99f438d 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -344,7 +344,7 @@ def translate_module_id(self, id: str) -> str: def visit_Module(self, mod: ast27.Module) -> MypyFile: self.type_ignores = {} for ti in mod.type_ignores: - parsed = parse_type_ignore_tag(ti.tag) + parsed = parse_type_ignore_tag(ti.tag) # type: ignore[attr-defined] if parsed is not None: self.type_ignores[ti.lineno] = parsed else: @@ -557,8 +557,14 @@ def get_type(self, extra_ignore = TYPE_IGNORE_PATTERN.match(comment) if extra_ignore: tag = cast(Any, extra_ignore).group(1) # type: Optional[str] - ignored = parse_type_ignore_tag(tag) - self.type_ignores[converter.line] = ignored + if not tag.strip().startswith('#'): + ignored = parse_type_ignore_tag(tag) + else: + ignored = [] + if ignored is None: + self.fail('Invalid "type: ignore" comment', converter.line, -1) + else: + self.type_ignores[converter.line] = ignored return typ return None diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 9c380b31fbe5..7fe7980b09f1 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -174,22 +174,43 @@ import nostub # type: ignore[foo] [bar] # E: Invalid "type: ignore" comment [s x = 0 # type: ignore[ # E: Invalid "type: ignore" comment [syntax] +def f(x, # type: int # type: ignore[ # E: Invalid "type: ignore" comment [syntax] + ): + # type: (...) -> None + pass + [case testErrorCodeBadIgnoreNoExtraComment] # Omit the E: ... comments, as they affect parsing import nostub # type: ignore xyz import nostub # type: ignore[xyz import nostub # type: ignore[xyz][xyz] x = 0 # type: ignore[ +def f(x, # type: int # type: ignore[ + ): + # type: (...) -> None + pass [out] main:2: error: Invalid "type: ignore" comment [syntax] main:3: error: Invalid "type: ignore" comment [syntax] main:4: error: Invalid "type: ignore" comment [syntax] main:5: error: Invalid "type: ignore" comment [syntax] +main:6: error: Invalid "type: ignore" comment [syntax] [case testErrorCodeBadIgnore_python2] -import nostub # type: ignore xyz # E: Invalid "type: ignore" comment [syntax] -import nostub # type: ignore[xyz # E: Invalid "type: ignore" comment [syntax] -import nostub # type: ignore[xyz][xyz] # E: Invalid "type: ignore" comment [syntax] +import nostub # type: ignore xyz +import nostub # type: ignore[xyz # Comment [x] +import nostub # type: ignore[xyz][xyz] +x = 0 # type: ignore[ +def f(x, # type: int # type: ignore[ + ): + # type: (...) -> None + pass +[out] +main:1: error: Invalid "type: ignore" comment [syntax] +main:2: error: Invalid "type: ignore" comment [syntax] +main:3: error: Invalid "type: ignore" comment [syntax] +main:4: error: Invalid "type: ignore" comment [syntax] +main:5: error: Invalid "type: ignore" comment [syntax] [case testErrorCodeArgKindAndCount] def f(x: int) -> None: pass # N: "f" defined here From 9967f96284eb192099e18ed3222c044d539a556e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 3 Sep 2019 17:31:13 +0100 Subject: [PATCH 04/10] Fix self check --- mypy/fastparse.py | 1 + mypy/fastparse2.py | 1 + 2 files changed, 2 insertions(+) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index e466ad99a256..854298102a70 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -208,6 +208,7 @@ def parse_type_comment(type_comment: str, if extra_ignore: # Typeshed has a non-optional return type for group! tag = cast(Any, extra_ignore).group(1) # type: Optional[str] + assert tag is not None if not tag.strip().startswith('#'): ignored = parse_type_ignore_tag(tag) # type: Optional[List[str]] else: diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index b7a0b99f438d..f533c6ca2bb3 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -557,6 +557,7 @@ def get_type(self, extra_ignore = TYPE_IGNORE_PATTERN.match(comment) if extra_ignore: tag = cast(Any, extra_ignore).group(1) # type: Optional[str] + assert tag is not None if not tag.strip().startswith('#'): ignored = parse_type_ignore_tag(tag) else: From 4c7c8bb9645fe11e0dd078efec85ab542de5e2f4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Sep 2019 15:39:20 +0100 Subject: [PATCH 05/10] Minor refactoring --- mypy/fastparse.py | 7 ++++--- mypy/fastparse2.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 854298102a70..6cce72e55a84 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -124,6 +124,8 @@ def ast3_parse(source: Union[str, bytes], filename: str, mode: str, TYPE_COMMENT_SYNTAX_ERROR = 'syntax error in type comment' # type: Final +INVALID_TYPE_IGNORE = 'Invalid "type: ignore" comment' # type: Final + TYPE_IGNORE_PATTERN = re.compile(r'[^#]*#\s*type:\s*ignore\s*\b(.*)') @@ -215,8 +217,7 @@ def parse_type_comment(type_comment: str, ignored = [] # Ignore everything if ignored is None: if errors is not None: - errors.report(line, column, 'Invalid "type: ignore" comment', - code=codes.SYNTAX) + errors.report(line, column, INVALID_TYPE_IGNORE, code=codes.SYNTAX) else: raise SyntaxError else: @@ -470,7 +471,7 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile: if parsed is not None: self.type_ignores[ti.lineno] = parsed else: - self.fail('Invalid "type: ignore" comment', ti.lineno, -1) + self.fail(INVALID_TYPE_IGNORE, ti.lineno, -1) body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True)) return MypyFile(body, self.imports, diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index f533c6ca2bb3..ba80de1ecb16 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -47,7 +47,7 @@ from mypy.errors import Errors from mypy.fastparse import ( TypeConverter, parse_type_comment, bytes_to_human_readable_repr, parse_type_ignore_tag, - TYPE_IGNORE_PATTERN + TYPE_IGNORE_PATTERN, INVALID_TYPE_IGNORE ) from mypy.options import Options from mypy.reachability import mark_block_unreachable @@ -348,7 +348,7 @@ def visit_Module(self, mod: ast27.Module) -> MypyFile: if parsed is not None: self.type_ignores[ti.lineno] = parsed else: - self.fail('Invalid "type: ignore" comment', ti.lineno, -1) + self.fail(INVALID_TYPE_IGNORE, ti.lineno, -1) body = self.fix_function_overloads(self.translate_stmt_list(mod.body)) return MypyFile(body, self.imports, @@ -563,7 +563,7 @@ def get_type(self, else: ignored = [] if ignored is None: - self.fail('Invalid "type: ignore" comment', converter.line, -1) + self.fail(INVALID_TYPE_IGNORE, converter.line, -1) else: self.type_ignores[converter.line] = ignored return typ From e183ae5a8f4e3e8816c99132a09d3e74cbfde122 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Sep 2019 15:44:21 +0100 Subject: [PATCH 06/10] Add test cases and simplify a bit --- mypy/fastparse.py | 2 +- test-data/unit/check-errorcodes.test | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 6cce72e55a84..1dc14011c18a 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -126,7 +126,7 @@ def ast3_parse(source: Union[str, bytes], filename: str, mode: str, INVALID_TYPE_IGNORE = 'Invalid "type: ignore" comment' # type: Final -TYPE_IGNORE_PATTERN = re.compile(r'[^#]*#\s*type:\s*ignore\s*\b(.*)') +TYPE_IGNORE_PATTERN = re.compile(r'[^#]*#\s*type:\s*ignore\s*(.*)') def parse(source: Union[str, bytes], diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 7fe7980b09f1..2cfaa9bdd837 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -642,3 +642,19 @@ class A: def g(self: A) -> None: pass A.f = g # E: Cannot assign to a method [assignment] + +[case testErrorCodeTypeIgnoreMisspelled1] +x = y # type: ignored[foo] +xx = y # type: ignored [foo] +[out] +main:1: error: Name 'ignored' is not defined [name-defined] +main:1: error: Name 'y' is not defined [name-defined] +main:2: error: Name 'ignored' is not defined [name-defined] +main:2: error: Name 'y' is not defined [name-defined] + +[case testErrorCodeTypeIgnoreMisspelled2] +x = y # type: int # type: ignored[foo] +x = y # type: int # type: ignored [foo] +[out] +main:1: error: syntax error in type comment 'int' [syntax] +main:2: error: syntax error in type comment 'int' [syntax] From f5e91d724605dc069f66bf3e1121e9b62b8a9995 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Sep 2019 15:45:55 +0100 Subject: [PATCH 07/10] Minor refactoring --- mypy/fastparse.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 1dc14011c18a..70571b4d36f1 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -173,15 +173,12 @@ def parse(source: Union[str, bytes], def parse_type_ignore_tag(tag: Optional[str]) -> Optional[List[str]]: - if not tag: + if not tag or tag.strip() == '': return [] m = re.match(r'\s*\[([^]#]*)\]\s*(#.*)?$', tag) if m is None: - if tag.strip() == '': - return [] - else: - # Invalid "# type: ignore" comment. - return None + # Invalid "# type: ignore" comment. + return None return [code.strip() for code in m.group(1).split(',')] From 95bdb2915f99713853d0ac0f11a27c90a735662c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Sep 2019 15:59:03 +0100 Subject: [PATCH 08/10] Fix and clean up --- mypy/fastparse.py | 8 ++------ mypy/fastparse2.py | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 70571b4d36f1..2861d04835bd 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -173,7 +173,7 @@ def parse(source: Union[str, bytes], def parse_type_ignore_tag(tag: Optional[str]) -> Optional[List[str]]: - if not tag or tag.strip() == '': + if not tag or tag.strip() == '' or tag.strip().startswith('#'): return [] m = re.match(r'\s*\[([^]#]*)\]\s*(#.*)?$', tag) if m is None: @@ -207,11 +207,7 @@ def parse_type_comment(type_comment: str, if extra_ignore: # Typeshed has a non-optional return type for group! tag = cast(Any, extra_ignore).group(1) # type: Optional[str] - assert tag is not None - if not tag.strip().startswith('#'): - ignored = parse_type_ignore_tag(tag) # type: Optional[List[str]] - else: - ignored = [] # Ignore everything + ignored = parse_type_ignore_tag(tag) # type: Optional[List[str]] if ignored is None: if errors is not None: errors.report(line, column, INVALID_TYPE_IGNORE, code=codes.SYNTAX) diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index ba80de1ecb16..2ef1cd745972 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -557,11 +557,7 @@ def get_type(self, extra_ignore = TYPE_IGNORE_PATTERN.match(comment) if extra_ignore: tag = cast(Any, extra_ignore).group(1) # type: Optional[str] - assert tag is not None - if not tag.strip().startswith('#'): - ignored = parse_type_ignore_tag(tag) - else: - ignored = [] + ignored = parse_type_ignore_tag(tag) if ignored is None: self.fail(INVALID_TYPE_IGNORE, converter.line, -1) else: From 4dee365633161b65ce60681008a19b5e8ee5c1d8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Sep 2019 16:06:01 +0100 Subject: [PATCH 09/10] Add comment --- mypy/fastparse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 2861d04835bd..b3a7e5f778ed 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -174,6 +174,7 @@ def parse(source: Union[str, bytes], def parse_type_ignore_tag(tag: Optional[str]) -> Optional[List[str]]: if not tag or tag.strip() == '' or tag.strip().startswith('#'): + # No tag -- ignore all errors. return [] m = re.match(r'\s*\[([^]#]*)\]\s*(#.*)?$', tag) if m is None: From dd11790df160bdc9237c3c8a6f81a5870edb7dbd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Sep 2019 16:07:56 +0100 Subject: [PATCH 10/10] Add docstring --- mypy/fastparse.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index b3a7e5f778ed..4a7b0e183600 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -173,6 +173,13 @@ def parse(source: Union[str, bytes], def parse_type_ignore_tag(tag: Optional[str]) -> Optional[List[str]]: + """Parse optional "[code, ...]" tag after "# type: ignore". + + Return: + * [] if no tag was found (ignore all errors) + * list of ignored error codes if a tag was found + * None if the tag was invalid. + """ if not tag or tag.strip() == '' or tag.strip().startswith('#'): # No tag -- ignore all errors. return [] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy