Skip to content

Commit

Permalink
Avoid expensive list/tuple multiplication operations (#2228) (#2229)
Browse files Browse the repository at this point in the history
(cherry picked from commit 1a318a0)

Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
  • Loading branch information
github-actions[bot] and jacobtylerwalls authored Jul 1, 2023
1 parent 3f6276d commit 2eb869e
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 4 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Release date: 2023-05-14

Closes pylint-dev/pylint#8749

* Avoid expensive list/tuple multiplication operations that would result in ``MemoryError``.

Closes pylint-dev/pylint#8748


What's New in astroid 2.15.5?
=============================
Expand Down
14 changes: 10 additions & 4 deletions astroid/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,19 @@ def const_infer_binary_op(
def _multiply_seq_by_int(
self: _TupleListNodeT,
opnode: nodes.AugAssign | nodes.BinOp,
other: nodes.Const,
value: int,
context: InferenceContext,
) -> _TupleListNodeT:
node = self.__class__(parent=opnode)
if value > 1e8:
node.elts = [util.Uninferable]
return node
filtered_elts = (
helpers.safe_infer(elt, context) or util.Uninferable
for elt in self.elts
if not isinstance(elt, util.UninferableBase)
)
node.elts = list(filtered_elts) * other.value
node.elts = list(filtered_elts) * value
return node


Expand Down Expand Up @@ -225,14 +228,17 @@ def tl_infer_binary_op(
if not isinstance(other.value, int):
yield not_implemented
return
yield _multiply_seq_by_int(self, opnode, other, context)
yield _multiply_seq_by_int(self, opnode, other.value, context)
elif isinstance(other, bases.Instance) and operator == "*":
# Verify if the instance supports __index__.
as_index = helpers.class_instance_as_index(other)
if not as_index:
yield util.Uninferable
elif not isinstance(as_index.value, int): # pragma: no cover
# already checked by class_instance_as_index() but faster than casting
raise AssertionError("Please open a bug report.")
else:
yield _multiply_seq_by_int(self, opnode, as_index, context)
yield _multiply_seq_by_int(self, opnode, as_index.value, context)
else:
yield not_implemented

Expand Down
7 changes: 7 additions & 0 deletions tests/test_protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ def test_uninferable_exponents() -> None:
parsed = extract_node("None ** 2")
assert parsed.inferred() == [Uninferable]

@staticmethod
def test_uninferable_list_multiplication() -> None:
"""Attempting to calculate the result is prohibitively expensive."""
parsed = extract_node("[0] * 123456789")
element = parsed.inferred()[0].elts[0]
assert element.value is Uninferable


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

0 comments on commit 2eb869e

Please sign in to comment.