Skip to content

Commit e9d054c

Browse files
authored
Fix inference of self in a list or tuple within bin. ops. (#1360)
1 parent b5e3e71 commit e9d054c

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ What's New in astroid 2.10.0?
66
=============================
77
Release date: TBA
88

9+
10+
* Fixed inference of ``self`` in binary operations in which ``self``
11+
is part of a list or tuple.
12+
13+
Closes PyCQA/pylint#4826
14+
915
* Fixed builtin inferenence on `property` calls not calling the `postinit` of the new node, which
1016
resulted in instance arguments missing on these nodes.
1117

astroid/protocols.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,22 @@ def _filter_uninferable_nodes(elts, context):
185185

186186

187187
@decorators.yes_if_nothing_inferred
188-
def tl_infer_binary_op(self, opnode, operator, other, context, method):
188+
def tl_infer_binary_op(
189+
self,
190+
opnode: nodes.BinOp,
191+
operator: str,
192+
other: nodes.NodeNG,
193+
context: InferenceContext,
194+
method: nodes.FunctionDef,
195+
) -> Generator[nodes.NodeNG, None, None]:
196+
"""Infer a binary operation on a tuple or list.
197+
198+
The instance on which the binary operation is performed is a tuple
199+
or list. This refers to the left-hand side of the operation, so:
200+
'tuple() + 1' or '[] + A()'
201+
"""
202+
# For tuples and list the boundnode is no longer the tuple or list instance
203+
context.boundnode = None
189204
not_implemented = nodes.Const(NotImplemented)
190205
if isinstance(other, self.__class__) and operator == "+":
191206
node = self.__class__(parent=opnode)

tests/unittest_inference.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3009,6 +3009,34 @@ def __radd__(self, other):
30093009
for node in ast_nodes:
30103010
self.assertEqual(next(node.infer()), util.Uninferable)
30113011

3012+
def test_binop_self_in_list(self) -> None:
3013+
"""If 'self' is referenced within a list it should not be bound by it.
3014+
3015+
Reported in https://github.com/PyCQA/pylint/issues/4826.
3016+
"""
3017+
ast_nodes = extract_node(
3018+
"""
3019+
class A:
3020+
def __init__(self):
3021+
for a in [self] + []:
3022+
print(a) #@
3023+
3024+
class B:
3025+
def __init__(self):
3026+
for b in [] + [self]:
3027+
print(b) #@
3028+
"""
3029+
)
3030+
inferred_a = list(ast_nodes[0].args[0].infer())
3031+
self.assertEqual(len(inferred_a), 1)
3032+
self.assertIsInstance(inferred_a[0], Instance)
3033+
self.assertEqual(inferred_a[0]._proxied.name, "A")
3034+
3035+
inferred_b = list(ast_nodes[1].args[0].infer())
3036+
self.assertEqual(len(inferred_b), 1)
3037+
self.assertIsInstance(inferred_b[0], Instance)
3038+
self.assertEqual(inferred_b[0]._proxied.name, "B")
3039+
30123040
def test_metaclass__getitem__(self) -> None:
30133041
ast_node = extract_node(
30143042
"""

0 commit comments

Comments
 (0)