Skip to content

Commit bedb3ed

Browse files
Promoted getattr() from FunctionDef to Lambda (#1472)
Co-authored-by: Daniël van Noord <[email protected]>
1 parent d74051d commit bedb3ed

File tree

4 files changed

+31
-22
lines changed

4 files changed

+31
-22
lines changed

ChangeLog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in astroid 2.11.1?
1212
=============================
1313
Release date: TBA
1414

15+
* Promoted ``getattr()`` from ``astroid.scoped_nodes.FunctionDef`` to its parent
16+
``astroid.scoped_nodes.Lambda``.
17+
1518

1619

1720
What's New in astroid 2.11.0?

astroid/nodes/scoped_nodes/scoped_nodes.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from astroid.interpreter.dunder_lookup import lookup
4040
from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel
4141
from astroid.manager import AstroidManager
42-
from astroid.nodes import Arguments, Const, node_classes
42+
from astroid.nodes import Arguments, Const, NodeNG, node_classes
4343
from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG
4444
from astroid.nodes.scoped_nodes.utils import builtin_lookup
4545
from astroid.nodes.utils import Position
@@ -1074,6 +1074,8 @@ class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG):
10741074
_other_other_fields = ("locals",)
10751075
name = "<lambda>"
10761076
is_lambda = True
1077+
special_attributes = FunctionModel()
1078+
"""The names of special attributes that this function has."""
10771079

10781080
def implicit_parameters(self):
10791081
return 0
@@ -1133,6 +1135,8 @@ def __init__(
11331135
:type: list(NodeNG)
11341136
"""
11351137

1138+
self.instance_attrs: Dict[str, List[NodeNG]] = {}
1139+
11361140
super().__init__(
11371141
lineno=lineno,
11381142
col_offset=col_offset,
@@ -1263,6 +1267,21 @@ def frame(self: T, *, future: Literal[None, True] = None) -> T:
12631267
"""
12641268
return self
12651269

1270+
def getattr(
1271+
self, name: str, context: Optional[InferenceContext] = None
1272+
) -> List[NodeNG]:
1273+
if not name:
1274+
raise AttributeInferenceError(target=self, attribute=name, context=context)
1275+
1276+
found_attrs = []
1277+
if name in self.instance_attrs:
1278+
found_attrs = self.instance_attrs[name]
1279+
if name in self.special_attributes:
1280+
found_attrs.append(self.special_attributes.lookup(name))
1281+
if found_attrs:
1282+
return found_attrs
1283+
raise AttributeInferenceError(target=self, attribute=name)
1284+
12661285

12671286
class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
12681287
"""Class representing an :class:`ast.FunctionDef`.
@@ -1281,11 +1300,7 @@ class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
12811300
returns = None
12821301
decorators: Optional[node_classes.Decorators] = None
12831302
"""The decorators that are applied to this method or function."""
1284-
special_attributes = FunctionModel()
1285-
"""The names of special attributes that this function has.
12861303

1287-
:type: objectmodel.FunctionModel
1288-
"""
12891304
is_function = True
12901305
"""Whether this node indicates a function.
12911306
@@ -1583,22 +1598,6 @@ def block_range(self, lineno):
15831598
"""
15841599
return self.fromlineno, self.tolineno
15851600

1586-
def getattr(self, name, context=None):
1587-
"""this method doesn't look in the instance_attrs dictionary since it's
1588-
done by an Instance proxy at inference time.
1589-
"""
1590-
if not name:
1591-
raise AttributeInferenceError(target=self, attribute=name, context=context)
1592-
1593-
found_attrs = []
1594-
if name in self.instance_attrs:
1595-
found_attrs = self.instance_attrs[name]
1596-
if name in self.special_attributes:
1597-
found_attrs.append(self.special_attributes.lookup(name))
1598-
if found_attrs:
1599-
return found_attrs
1600-
raise AttributeInferenceError(target=self, attribute=name)
1601-
16021601
def igetattr(self, name, context=None):
16031602
"""Inferred getattr, which returns an iterator of inferred statements."""
16041603
try:

tests/unittest_inference.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4388,7 +4388,8 @@ def test_lambda(self) -> None:
43884388
"""
43894389
)
43904390
inferred = next(node.infer())
4391-
self.assertEqual(inferred, util.Uninferable)
4391+
self.assertIsInstance(inferred, nodes.Const)
4392+
self.assertIs(inferred.value, False)
43924393

43934394

43944395
class BoolOpTest(unittest.TestCase):

tests/unittest_scoped_nodes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,12 @@ def test_lambda_qname(self) -> None:
484484
astroid = builder.parse("lmbd = lambda: None", __name__)
485485
self.assertEqual(f"{__name__}.<lambda>", astroid["lmbd"].parent.value.qname())
486486

487+
def test_lambda_getattr(self) -> None:
488+
astroid = builder.parse("lmbd = lambda: None")
489+
self.assertIsInstance(
490+
astroid["lmbd"].parent.value.getattr("__code__")[0], nodes.Unknown
491+
)
492+
487493
def test_is_method(self) -> None:
488494
data = """
489495
class A:

0 commit comments

Comments
 (0)