diff options
-rw-r--r-- | ChangeLog | 12 | ||||
-rw-r--r-- | astroid/node_classes.py | 4 | ||||
-rw-r--r-- | astroid/rebuilder.py | 16 | ||||
-rw-r--r-- | astroid/tests/unittest_nodes.py | 21 |
4 files changed, 45 insertions, 8 deletions
@@ -2,6 +2,18 @@ astroid's ChangeLog =================== +What's New in astroid 2.3.2? +============================ +Release Date: TBA + +* All type comments have as parent the corresponding `astroid` node + + Until now they had as parent the builtin `ast` node which meant + we were operating with primitive objects instead of our own. + + Close PyCQA/pylint#3174 + + What's New in astroid 2.3.1? ============================ Release Date: 2019-09-30 diff --git a/astroid/node_classes.py b/astroid/node_classes.py index b9af5983..994c96bd 100644 --- a/astroid/node_classes.py +++ b/astroid/node_classes.py @@ -496,7 +496,9 @@ class NodeNG: :returns: The first parent scope node. :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr """ - return self.parent.scope() + if self.parent: + return self.parent.scope() + return None def root(self): """Return the root node of the syntax tree. diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py index 09c9304b..fb78f7bb 100644 --- a/astroid/rebuilder.py +++ b/astroid/rebuilder.py @@ -218,7 +218,9 @@ class TreeRebuilder: self.visit(arg.annotation, newnode) if arg.annotation else None for arg in node.posonlyargs ] - type_comment_args = [self.check_type_comment(child) for child in node.args] + type_comment_args = [ + self.check_type_comment(child, parent=newnode) for child in node.args + ] newnode.postinit( args=args, @@ -250,7 +252,7 @@ class TreeRebuilder: newnode.postinit(self.visit(node.test, newnode), msg) return newnode - def check_type_comment(self, node): + def check_type_comment(self, node, parent): type_comment = getattr(node, "type_comment", None) if not type_comment: return None @@ -261,7 +263,7 @@ class TreeRebuilder: # Invalid type comment, just skip it. return None - type_object = self.visit(type_comment_ast.body[0], node) + type_object = self.visit(type_comment_ast.body[0], parent=parent) if not isinstance(type_object, nodes.Expr): return None @@ -289,8 +291,8 @@ class TreeRebuilder: def visit_assign(self, node, parent): """visit a Assign node by returning a fresh instance of it""" - type_annotation = self.check_type_comment(node) newnode = nodes.Assign(node.lineno, node.col_offset, parent) + type_annotation = self.check_type_comment(node, parent=newnode) newnode.postinit( targets=[self.visit(child, newnode) for child in node.targets], value=self.visit(node.value, newnode), @@ -550,7 +552,7 @@ class TreeRebuilder: def _visit_for(self, cls, node, parent): """visit a For node by returning a fresh instance of it""" newnode = cls(node.lineno, node.col_offset, parent) - type_annotation = self.check_type_comment(node) + type_annotation = self.check_type_comment(node, parent=newnode) newnode.postinit( target=self.visit(node.target, newnode), iter=self.visit(node.iter, newnode), @@ -912,7 +914,7 @@ class TreeRebuilder: else: optional_vars = None - type_annotation = self.check_type_comment(node) + type_annotation = self.check_type_comment(node, parent=newnode) newnode.postinit( items=[(expr, optional_vars)], body=[self.visit(child, newnode) for child in node.body], @@ -1026,7 +1028,7 @@ class TreeRebuilder3(TreeRebuilder): var = _visit_or_none(child, "optional_vars", self, newnode) return expr, var - type_annotation = self.check_type_comment(node) + type_annotation = self.check_type_comment(node, parent=newnode) newnode.postinit( items=[visit_child(child) for child in node.items], body=[self.visit(child, newnode) for child in node.body], diff --git a/astroid/tests/unittest_nodes.py b/astroid/tests/unittest_nodes.py index ff1cca07..79b9936e 100644 --- a/astroid/tests/unittest_nodes.py +++ b/astroid/tests/unittest_nodes.py @@ -1195,5 +1195,26 @@ def test_parse_fstring_debug_mode(): assert node.as_string() == "f'3={3}'" +@pytest.mark.skipif(not HAS_TYPED_AST, reason="requires typed_ast") +def test_parse_type_comments_with_proper_parent(): + code = """ + class D: #@ + @staticmethod + def g( + x # type: np.array + ): + pass + """ + node = astroid.extract_node(code) + func = node.getattr("g")[0] + type_comments = func.args.type_comment_args + assert len(type_comments) == 1 + + type_comment = type_comments[0] + assert isinstance(type_comment, astroid.Attribute) + assert isinstance(type_comment.parent, astroid.Expr) + assert isinstance(type_comment.parent.parent, astroid.Arguments) + + if __name__ == "__main__": unittest.main() |