Skip to content

Commit 5f235c0

Browse files
committed
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 pylint-dev/pylint#3174
1 parent 609f130 commit 5f235c0

File tree

4 files changed

+45
-8
lines changed

4 files changed

+45
-8
lines changed

ChangeLog

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
astroid's ChangeLog
33
===================
44

5+
What's New in astroid 2.3.2?
6+
============================
7+
Release Date: TBA
8+
9+
* All type comments have as parent the corresponding `astroid` node
10+
11+
Until now they had as parent the builtin `ast` node which meant
12+
we were operating with primitive objects instead of our own.
13+
14+
Close PyCQA/pylint#3174
15+
16+
517
What's New in astroid 2.3.1?
618
============================
719
Release Date: 2019-09-30

astroid/node_classes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,9 @@ def scope(self):
496496
:returns: The first parent scope node.
497497
:rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
498498
"""
499-
return self.parent.scope()
499+
if self.parent:
500+
return self.parent.scope()
501+
return None
500502

501503
def root(self):
502504
"""Return the root node of the syntax tree.

astroid/rebuilder.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,9 @@ def visit_arguments(self, node, parent):
218218
self.visit(arg.annotation, newnode) if arg.annotation else None
219219
for arg in node.posonlyargs
220220
]
221-
type_comment_args = [self.check_type_comment(child) for child in node.args]
221+
type_comment_args = [
222+
self.check_type_comment(child, parent=newnode) for child in node.args
223+
]
222224

223225
newnode.postinit(
224226
args=args,
@@ -250,7 +252,7 @@ def visit_assert(self, node, parent):
250252
newnode.postinit(self.visit(node.test, newnode), msg)
251253
return newnode
252254

253-
def check_type_comment(self, node):
255+
def check_type_comment(self, node, parent):
254256
type_comment = getattr(node, "type_comment", None)
255257
if not type_comment:
256258
return None
@@ -261,7 +263,7 @@ def check_type_comment(self, node):
261263
# Invalid type comment, just skip it.
262264
return None
263265

264-
type_object = self.visit(type_comment_ast.body[0], node)
266+
type_object = self.visit(type_comment_ast.body[0], parent=parent)
265267
if not isinstance(type_object, nodes.Expr):
266268
return None
267269

@@ -289,8 +291,8 @@ def check_function_type_comment(self, node):
289291

290292
def visit_assign(self, node, parent):
291293
"""visit a Assign node by returning a fresh instance of it"""
292-
type_annotation = self.check_type_comment(node)
293294
newnode = nodes.Assign(node.lineno, node.col_offset, parent)
295+
type_annotation = self.check_type_comment(node, parent=newnode)
294296
newnode.postinit(
295297
targets=[self.visit(child, newnode) for child in node.targets],
296298
value=self.visit(node.value, newnode),
@@ -550,7 +552,7 @@ def visit_extslice(self, node, parent):
550552
def _visit_for(self, cls, node, parent):
551553
"""visit a For node by returning a fresh instance of it"""
552554
newnode = cls(node.lineno, node.col_offset, parent)
553-
type_annotation = self.check_type_comment(node)
555+
type_annotation = self.check_type_comment(node, parent=newnode)
554556
newnode.postinit(
555557
target=self.visit(node.target, newnode),
556558
iter=self.visit(node.iter, newnode),
@@ -912,7 +914,7 @@ def visit_with(self, node, parent):
912914
else:
913915
optional_vars = None
914916

915-
type_annotation = self.check_type_comment(node)
917+
type_annotation = self.check_type_comment(node, parent=newnode)
916918
newnode.postinit(
917919
items=[(expr, optional_vars)],
918920
body=[self.visit(child, newnode) for child in node.body],
@@ -1026,7 +1028,7 @@ def visit_child(child):
10261028
var = _visit_or_none(child, "optional_vars", self, newnode)
10271029
return expr, var
10281030

1029-
type_annotation = self.check_type_comment(node)
1031+
type_annotation = self.check_type_comment(node, parent=newnode)
10301032
newnode.postinit(
10311033
items=[visit_child(child) for child in node.items],
10321034
body=[self.visit(child, newnode) for child in node.body],

astroid/tests/unittest_nodes.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,5 +1195,26 @@ def test_parse_fstring_debug_mode():
11951195
assert node.as_string() == "f'3={3}'"
11961196

11971197

1198+
@pytest.mark.skipif(not HAS_TYPED_AST, reason="requires typed_ast")
1199+
def test_parse_type_comments_with_proper_parent():
1200+
code = """
1201+
class D: #@
1202+
@staticmethod
1203+
def g(
1204+
x # type: np.array
1205+
):
1206+
pass
1207+
"""
1208+
node = astroid.extract_node(code)
1209+
func = node.getattr("g")[0]
1210+
type_comments = func.args.type_comment_args
1211+
assert len(type_comments) == 1
1212+
1213+
type_comment = type_comments[0]
1214+
assert isinstance(type_comment, astroid.Attribute)
1215+
assert isinstance(type_comment.parent, astroid.Expr)
1216+
assert isinstance(type_comment.parent.parent, astroid.Arguments)
1217+
1218+
11981219
if __name__ == "__main__":
11991220
unittest.main()

0 commit comments

Comments
 (0)