diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2019-11-26 09:17:10 +0100 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-11-26 09:17:10 +0100 |
commit | 2cb6818ad5db25fd0f32a193ab90425e7dfcbee0 (patch) | |
tree | 3d8666f65a776dbdd81c61b20b1c6404d09d0582 | |
parent | 5cdefb0d278dfe80a662156a942472794df5ca67 (diff) | |
download | astroid-git-2cb6818ad5db25fd0f32a193ab90425e7dfcbee0.tar.gz |
Retry parsing a module that has invalid type comments
It is possible for a module to use comments that might be interpreted
as type comments by the `ast` library. We do not want to completely crash on those
invalid type comments.
Close #708
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | astroid/_ast.py | 15 | ||||
-rw-r--r-- | astroid/builder.py | 18 | ||||
-rw-r--r-- | tests/unittest_builder.py | 25 |
4 files changed, 60 insertions, 6 deletions
@@ -12,6 +12,14 @@ Release Date: TBA * Allow inferring positional only arguments. +* Retry parsing a module that has invalid type comments + + It is possible for a module to use comments that might be interpreted + as type comments by the `ast` library. We do not want to completely crash on those + invalid type comments. + + Close #708 + * Scope the inference to the current bound node when inferring instances of classes When inferring instances of classes from arguments, such as ``self`` diff --git a/astroid/_ast.py b/astroid/_ast.py index 2e44c1f1..66c5cf25 100644 --- a/astroid/_ast.py +++ b/astroid/_ast.py @@ -21,7 +21,10 @@ if PY38: FunctionType = namedtuple("FunctionType", ["argtypes", "returns"]) -def _get_parser_module(parse_python_two: bool = False): +def _get_parser_module(parse_python_two=False, type_comments_support=True): + if not type_comments_support: + return ast + if parse_python_two: parser_module = _ast_py2 else: @@ -29,12 +32,14 @@ def _get_parser_module(parse_python_two: bool = False): return parser_module or ast -def _parse(string: str, parse_python_two: bool = False): - parse_module = _get_parser_module(parse_python_two=parse_python_two) +def _parse(string: str, parse_python_two=False, type_comments=True): + parse_module = _get_parser_module( + parse_python_two=parse_python_two, type_comments_support=type_comments + ) parse_func = parse_module.parse - if _ast_py3: + if parse_module is _ast_py3: if PY38: - parse_func = partial(parse_func, type_comments=True) + parse_func = partial(parse_func, type_comments=type_comments) if not parse_python_two: parse_func = partial(parse_func, feature_version=sys.version_info.minor) return parse_func(string) diff --git a/astroid/builder.py b/astroid/builder.py index 34bde0a1..7f30aaeb 100644 --- a/astroid/builder.py +++ b/astroid/builder.py @@ -165,7 +165,7 @@ class AstroidBuilder(raw_building.InspectBuilder): def _data_build(self, data, modname, path): """Build tree node from data and add some informations""" try: - node = _parse(data + "\n") + node = _parse_string(data) except (TypeError, ValueError, SyntaxError) as exc: raise exceptions.AstroidSyntaxError( "Parsing Python code failed:\n{error}", @@ -436,3 +436,19 @@ def extract_node(code, module_name=""): if len(extracted) == 1: return extracted[0] return extracted + + +MISPLACED_TYPE_ANNOTATION_ERROR = "misplaced type annotation" + + +def _parse_string(data, type_comments=True): + try: + node = _parse(data + "\n", type_comments=type_comments) + except SyntaxError as exc: + # If the type annotations are misplaced for some reason, we do not want + # to fail the entire parsing of the file, so we need to retry the parsing without + # type comment support. + if exc.args[0] != MISPLACED_TYPE_ANNOTATION_ERROR or not type_comments: + raise + node = _parse(data + "\n", type_comments=False) + return node diff --git a/tests/unittest_builder.py b/tests/unittest_builder.py index 0f1a470f..75557761 100644 --- a/tests/unittest_builder.py +++ b/tests/unittest_builder.py @@ -692,5 +692,30 @@ def test_module_build_dunder_file(): assert module.path[0] == collections.__file__ +def test_parse_module_with_invalid_type_comments_does_not_crash(): + node = builder.parse( + """ + # op { + # name: "AssignAddVariableOp" + # input_arg { + # name: "resource" + # type: DT_RESOURCE + # } + # input_arg { + # name: "value" + # type_attr: "dtype" + # } + # attr { + # name: "dtype" + # type: "type" + # } + # is_stateful: true + # } + a, b = 2 + """ + ) + assert isinstance(node, nodes.Module) + + if __name__ == "__main__": unittest.main() |