Skip to content

Commit

Permalink
enforce a non-None parent in build_function
Browse files Browse the repository at this point in the history
We also remove `add_local_node` to avoid redundancy. Instead we do the
   attachment to the parent scope in the constructor of `FunctionDef`.

We append a node to the body of the frame when it is also the
   parent. If it's not a parent, then the node should belong to the
   "body" of the parent if it existed. An example is a definition
   within an "if", where the parent is the If node, but the frame is
   the whole module.

it's a part of the campaign to get rid of non-module roots
  • Loading branch information
temyurchenko committed Sep 11, 2024
1 parent c34ee25 commit f3242e8
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 13 deletions.
10 changes: 8 additions & 2 deletions astroid/nodes/scoped_nodes/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ def function_to_method(n, klass):
return n


def _attach_to_parent(node: NodeNG, name: str, parent: NodeNG):
frame = parent.frame()
frame.set_local(name, node)
if frame is parent:
frame._append_node(node)


class Module(LocalsDictNodeNG):
"""Class representing an :class:`ast.Module` node.
Expand Down Expand Up @@ -1167,8 +1174,7 @@ def __init__(
parent=parent,
)
if parent and not isinstance(parent, Unknown):
frame = parent.frame()
frame.set_local(name, self)
_attach_to_parent(self, name, parent)

def postinit(
self,
Expand Down
11 changes: 5 additions & 6 deletions astroid/raw_building.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ def build_class(

def build_function(
name: str,
parent: nodes.NodeNG,
args: list[str] | None = None,
posonlyargs: list[str] | None = None,
defaults: list[Any] | None = None,
Expand All @@ -142,7 +143,7 @@ def build_function(
name,
lineno=0,
col_offset=0,
parent=node_classes.Unknown(),
parent=parent,
end_col_offset=0,
end_lineno=0,
)
Expand Down Expand Up @@ -314,8 +315,9 @@ def object_build_function(
kwonly_defaults,
) = _get_args_info_from_callable(member)

func = build_function(
build_function(
localname,
node,
args,
posonlyargs,
defaults,
Expand All @@ -324,8 +326,6 @@ def object_build_function(
kwonlydefaults=kwonly_defaults,
)

node.add_local_node(func, localname)


def object_build_datadescriptor(
node: nodes.Module | nodes.ClassDef, member: type, name: str
Expand All @@ -341,8 +341,7 @@ def object_build_methoddescriptor(
) -> None:
"""create astroid for a living method descriptor object"""
# FIXME get arguments ?
func = build_function(localname, doc=member.__doc__)
node.add_local_node(func, localname)
func = build_function(localname, node, doc=member.__doc__)
_add_dunder_class(func, member)


Expand Down
12 changes: 7 additions & 5 deletions tests/test_raw_building.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
build_module,
)

DUMMY_MOD = build_module("DUMMY")


class RawBuildingTC(unittest.TestCase):
def test_attach_dummy_node(self) -> None:
Expand All @@ -53,28 +55,28 @@ def test_build_class(self) -> None:
self.assertEqual(node.doc_node, None)

def test_build_function(self) -> None:
node = build_function("MyFunction")
node = build_function("MyFunction", DUMMY_MOD)
self.assertEqual(node.name, "MyFunction")
self.assertEqual(node.doc_node, None)

def test_build_function_args(self) -> None:
args = ["myArgs1", "myArgs2"]
node = build_function("MyFunction", args)
node = build_function("MyFunction", DUMMY_MOD, args)
self.assertEqual("myArgs1", node.args.args[0].name)
self.assertEqual("myArgs2", node.args.args[1].name)
self.assertEqual(2, len(node.args.args))

def test_build_function_defaults(self) -> None:
defaults = ["defaults1", "defaults2"]
node = build_function(name="MyFunction", args=None, defaults=defaults)
node = build_function("MyFunction", DUMMY_MOD, args=None, defaults=defaults)
self.assertEqual(2, len(node.args.defaults))

def test_build_function_posonlyargs(self) -> None:
node = build_function(name="MyFunction", posonlyargs=["a", "b"])
node = build_function("MyFunction", DUMMY_MOD, posonlyargs=["a", "b"])
self.assertEqual(2, len(node.args.posonlyargs))

def test_build_function_kwonlyargs(self) -> None:
node = build_function(name="MyFunction", kwonlyargs=["a", "b"])
node = build_function("MyFunction", DUMMY_MOD, kwonlyargs=["a", "b"])
assert len(node.args.kwonlyargs) == 2
assert node.args.kwonlyargs[0].name == "a"
assert node.args.kwonlyargs[1].name == "b"
Expand Down

0 comments on commit f3242e8

Please sign in to comment.