Skip to content

Commit 2eb869e

Browse files
Avoid expensive list/tuple multiplication operations (#2228) (#2229)
(cherry picked from commit 1a318a0) Co-authored-by: Jacob Walls <[email protected]>
1 parent 3f6276d commit 2eb869e

File tree

3 files changed

+21
-4
lines changed

3 files changed

+21
-4
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Release date: 2023-05-14
2727

2828
Closes pylint-dev/pylint#8749
2929

30+
* Avoid expensive list/tuple multiplication operations that would result in ``MemoryError``.
31+
32+
Closes pylint-dev/pylint#8748
33+
3034

3135
What's New in astroid 2.15.5?
3236
=============================

astroid/protocols.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,19 @@ def const_infer_binary_op(
167167
def _multiply_seq_by_int(
168168
self: _TupleListNodeT,
169169
opnode: nodes.AugAssign | nodes.BinOp,
170-
other: nodes.Const,
170+
value: int,
171171
context: InferenceContext,
172172
) -> _TupleListNodeT:
173173
node = self.__class__(parent=opnode)
174+
if value > 1e8:
175+
node.elts = [util.Uninferable]
176+
return node
174177
filtered_elts = (
175178
helpers.safe_infer(elt, context) or util.Uninferable
176179
for elt in self.elts
177180
if not isinstance(elt, util.UninferableBase)
178181
)
179-
node.elts = list(filtered_elts) * other.value
182+
node.elts = list(filtered_elts) * value
180183
return node
181184

182185

@@ -225,14 +228,17 @@ def tl_infer_binary_op(
225228
if not isinstance(other.value, int):
226229
yield not_implemented
227230
return
228-
yield _multiply_seq_by_int(self, opnode, other, context)
231+
yield _multiply_seq_by_int(self, opnode, other.value, context)
229232
elif isinstance(other, bases.Instance) and operator == "*":
230233
# Verify if the instance supports __index__.
231234
as_index = helpers.class_instance_as_index(other)
232235
if not as_index:
233236
yield util.Uninferable
237+
elif not isinstance(as_index.value, int): # pragma: no cover
238+
# already checked by class_instance_as_index() but faster than casting
239+
raise AssertionError("Please open a bug report.")
234240
else:
235-
yield _multiply_seq_by_int(self, opnode, as_index, context)
241+
yield _multiply_seq_by_int(self, opnode, as_index.value, context)
236242
else:
237243
yield not_implemented
238244

tests/test_protocols.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ def test_uninferable_exponents() -> None:
279279
parsed = extract_node("None ** 2")
280280
assert parsed.inferred() == [Uninferable]
281281

282+
@staticmethod
283+
def test_uninferable_list_multiplication() -> None:
284+
"""Attempting to calculate the result is prohibitively expensive."""
285+
parsed = extract_node("[0] * 123456789")
286+
element = parsed.inferred()[0].elts[0]
287+
assert element.value is Uninferable
288+
282289

283290
@pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions")
284291
def test_named_expr_inference() -> None:

0 commit comments

Comments
 (0)