summaryrefslogtreecommitdiff
path: root/astroid/nodes
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2023-04-17 17:13:31 +0200
committerGitHub <noreply@github.com>2023-04-17 17:13:31 +0200
commit1a14b5d2f6d7b16dcb8f50847b517798e8d1d759 (patch)
tree696f32917b1e03e369176f6989d52ecbd0c4ac8a /astroid/nodes
parentc40d6c0fcd83af530418f89749020352cc6f5b76 (diff)
downloadastroid-git-1a14b5d2f6d7b16dcb8f50847b517798e8d1d759.tar.gz
Decouple ``FunctionDef`` and ``Lambda`` (#2115)
As discussed in #2112 we really need to decouple this nodes.
Diffstat (limited to 'astroid/nodes')
-rw-r--r--astroid/nodes/scoped_nodes/scoped_nodes.py96
1 files changed, 93 insertions, 3 deletions
diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py
index 8c39129c..6afcc12b 100644
--- a/astroid/nodes/scoped_nodes/scoped_nodes.py
+++ b/astroid/nodes/scoped_nodes/scoped_nodes.py
@@ -1244,7 +1244,12 @@ class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG):
raise AttributeInferenceError(target=self, attribute=name)
-class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda):
+class FunctionDef(
+ _base_nodes.MultiLineBlockNode,
+ _base_nodes.FilterStmtsBaseNode,
+ _base_nodes.Statement,
+ LocalsDictNodeNG,
+):
"""Class representing an :class:`ast.FunctionDef`.
>>> import astroid
@@ -1291,6 +1296,13 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
)
_type = None
+ name = "<functiondef>"
+
+ is_lambda = True
+
+ special_attributes = FunctionModel()
+ """The names of special attributes that this function has."""
+
@decorators_mod.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead")
def __init__(
self,
@@ -1338,7 +1350,20 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
self.doc_node: Const | None = None
"""The doc node associated with this node."""
- self.instance_attrs = {}
+ self.locals = {}
+ """A map of the name of a local variable to the node defining it."""
+
+ self.args: Arguments
+ """The arguments that the function takes."""
+
+ self.body = []
+ """The contents of the function body.
+
+ :type: list(NodeNG)
+ """
+
+ self.instance_attrs: dict[str, list[NodeNG]] = {}
+
super().__init__(
lineno=lineno,
col_offset=col_offset,
@@ -1454,6 +1479,62 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
decorators.append(assign.value)
return decorators
+ def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]:
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ """
+ if "method" in self.type:
+ return "builtins.instancemethod"
+ return "builtins.function"
+
+ def display_type(self) -> str:
+ """A human readable type of this node.
+
+ :returns: The type of this node.
+ :rtype: str
+ """
+ if "method" in self.type:
+ return "Method"
+ return "Function"
+
+ def callable(self) -> Literal[True]:
+ return True
+
+ def argnames(self) -> list[str]:
+ """Get the names of each of the arguments, including that
+ of the collections of variable-length arguments ("args", "kwargs",
+ etc.), as well as positional-only and keyword-only arguments.
+
+ :returns: The names of the arguments.
+ :rtype: list(str)
+ """
+ if self.args.arguments: # maybe None with builtin functions
+ names = _rec_get_names(self.args.arguments)
+ else:
+ names = []
+ if self.args.vararg:
+ names.append(self.args.vararg)
+ names += [elt.name for elt in self.args.kwonlyargs]
+ if self.args.kwarg:
+ names.append(self.args.kwarg)
+ return names
+
+ def getattr(
+ self, name: str, context: InferenceContext | None = None
+ ) -> list[NodeNG]:
+ if not name:
+ raise AttributeInferenceError(target=self, attribute=name, context=context)
+
+ found_attrs = []
+ if name in self.instance_attrs:
+ found_attrs = self.instance_attrs[name]
+ if name in self.special_attributes:
+ found_attrs.append(self.special_attributes.lookup(name))
+ if found_attrs:
+ return found_attrs
+ raise AttributeInferenceError(target=self, attribute=name)
+
@cached_property
def type(self) -> str: # pylint: disable=too-many-return-statements # noqa: C901
"""The function type for this node.
@@ -1785,7 +1866,16 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
frame = self.parent.frame(future=True)
if isinstance(frame, ClassDef):
return self, [frame]
- return super().scope_lookup(node, name, offset)
+
+ if node in self.args.defaults or node in self.args.kw_defaults:
+ frame = self.parent.frame(future=True)
+ # line offset to avoid that def func(f=func) resolve the default
+ # value to the defined function
+ offset = -1
+ else:
+ # check this is not used in function decorators
+ frame = self
+ return frame._scope_lookup(node, name, offset)
def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
"""The node's frame node.