summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com>2021-12-29 14:05:20 +0530
committerGitHub <noreply@github.com>2021-12-29 09:35:20 +0100
commit12e2e8ce8a33b06c9b3d97007ca89120b529c215 (patch)
tree2ab95d0c484a4d6fe9286fe691995c82a36f5efe
parent6e58cdf9ea0ffee29ff2b6f9837c2ac2e4502095 (diff)
downloadastroid-git-12e2e8ce8a33b06c9b3d97007ca89120b529c215.tar.gz
Fix frame() error on inferred node (#1263)
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r--ChangeLog4
-rw-r--r--astroid/nodes/node_classes.py5
-rw-r--r--astroid/nodes/node_ng.py16
-rw-r--r--astroid/nodes/scoped_nodes/scoped_nodes.py8
-rw-r--r--tests/unittest_nodes.py8
5 files changed, 34 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index 5bfa2f68..76c15f0f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,10 @@ What's New in astroid 2.9.1?
============================
Release date: TBA
+* ``NodeNG.frame()`` and ``NodeNG.statement()`` will start raising ``ParentMissingError``
+ instead of ``AttributeError`` in astroid 3.0. This behaviour can already be triggered
+ by passing ``future=True`` to a ``frame()`` or ``statement()`` call.
+
* Prefer the module loader get_source() method in AstroidBuilder's
module_build() when possible to avoid assumptions about source
code being available on a filesystem. Otherwise the source cannot
diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py
index 76b06697..4e33bb5d 100644
--- a/astroid/nodes/node_classes.py
+++ b/astroid/nodes/node_classes.py
@@ -65,6 +65,7 @@ else:
from typing_extensions import Literal
if TYPE_CHECKING:
+ from astroid import nodes
from astroid.nodes import LocalsDictNodeNG
@@ -4795,7 +4796,9 @@ class NamedExpr(mixins.AssignTypeMixin, NodeNG):
See astroid/protocols.py for actual implementation.
"""
- def frame(self):
+ def frame(
+ self, *, future: Literal[None, True] = None
+ ) -> Union["nodes.FunctionDef", "nodes.Module", "nodes.ClassDef", "nodes.Lambda"]:
"""The first parent frame node.
A frame node is a :class:`Module`, :class:`FunctionDef`,
diff --git a/astroid/nodes/node_ng.py b/astroid/nodes/node_ng.py
index 5eb6d198..3c26f340 100644
--- a/astroid/nodes/node_ng.py
+++ b/astroid/nodes/node_ng.py
@@ -314,7 +314,7 @@ class NodeNG:
return self.parent.statement(future=future)
def frame(
- self,
+ self, *, future: Literal[None, True] = None
) -> Union["nodes.FunctionDef", "nodes.Module", "nodes.ClassDef", "nodes.Lambda"]:
"""The first parent frame node.
@@ -323,7 +323,19 @@ class NodeNG:
:returns: The first parent frame node.
"""
- return self.parent.frame()
+ if self.parent is None:
+ if future:
+ raise ParentMissingError(target=self)
+ warnings.warn(
+ "In astroid 3.0.0 NodeNG.frame() will return either a Frame node, "
+ "or raise ParentMissingError. AttributeError will no longer be raised. "
+ "This behaviour can already be triggered "
+ "by passing 'future=True' to a frame() call.",
+ DeprecationWarning,
+ )
+ raise AttributeError(f"{self} object has no attribute 'parent'")
+
+ return self.parent.frame(future=future)
def scope(self) -> "nodes.LocalsDictNodeNG":
"""The first parent node defining a new scope.
diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py
index c045c99e..f2720c4c 100644
--- a/astroid/nodes/scoped_nodes/scoped_nodes.py
+++ b/astroid/nodes/scoped_nodes/scoped_nodes.py
@@ -859,7 +859,7 @@ class Module(LocalsDictNodeNG):
def get_children(self):
yield from self.body
- def frame(self: T) -> T:
+ def frame(self: T, *, future: Literal[None, True] = None) -> T:
"""The node's frame node.
A frame node is a :class:`Module`, :class:`FunctionDef`,
@@ -1474,7 +1474,7 @@ class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG):
yield self.args
yield self.body
- def frame(self: T) -> T:
+ def frame(self: T, *, future: Literal[None, True] = None) -> T:
"""The node's frame node.
A frame node is a :class:`Module`, :class:`FunctionDef`,
@@ -2004,7 +2004,7 @@ class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
return self, [frame]
return super().scope_lookup(node, name, offset)
- def frame(self: T) -> T:
+ def frame(self: T, *, future: Literal[None, True] = None) -> T:
"""The node's frame node.
A frame node is a :class:`Module`, :class:`FunctionDef`,
@@ -3251,7 +3251,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement
)
return list(itertools.chain.from_iterable(children_assign_nodes))
- def frame(self: T) -> T:
+ def frame(self: T, *, future: Literal[None, True] = None) -> T:
"""The node's frame node.
A frame node is a :class:`Module`, :class:`FunctionDef`,
diff --git a/tests/unittest_nodes.py b/tests/unittest_nodes.py
index 68e2b335..aab8c5a1 100644
--- a/tests/unittest_nodes.py
+++ b/tests/unittest_nodes.py
@@ -55,6 +55,7 @@ from astroid.exceptions import (
AstroidBuildingError,
AstroidSyntaxError,
AttributeInferenceError,
+ ParentMissingError,
StatementMissing,
)
from astroid.nodes.node_classes import (
@@ -641,6 +642,13 @@ class ConstNodeTest(unittest.TestCase):
with self.assertRaises(StatementMissing):
node.statement(future=True)
+ with self.assertRaises(AttributeError):
+ with pytest.warns(DeprecationWarning) as records:
+ node.frame()
+ assert len(records) == 1
+ with self.assertRaises(ParentMissingError):
+ node.frame(future=True)
+
def test_none(self) -> None:
self._test(None)