Fix MemoryError when inferring f-string with large width (#2972)
Fix MemoryError when inferring f-string with large format width
Catch MemoryError in FormattedValue._infer() so that f-strings with
huge format widths (e.g. f'{0:11111111111}') yield Uninferable instead
of crashing.
The test uses an OOMInt subclass whose __format__ raises MemoryError,
avoiding the slow/OOM-inducing actual allocation.
Closes #2762diff --git a/ChangeLog b/ChangeLog
index a46c96f..7d15bf0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -13,6 +13,11 @@
============================
Release date: TBA
+* Catch ``MemoryError`` when inferring f-strings with extremely large format
+ widths (e.g. ``f'{0:11111111111}'``) so that inference yields ``Uninferable``
+ instead of crashing.
+
+ Closes #2762
What's New in astroid 4.1.1?
diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py
index 88e62fc..22125b0 100644
--- a/astroid/nodes/node_classes.py
+++ b/astroid/nodes/node_classes.py
@@ -4756,8 +4756,9 @@
end_col_offset=self.end_col_offset,
)
continue
- except (ValueError, TypeError):
- # happens when format_spec.value is invalid
+ except (ValueError, TypeError, MemoryError):
+ # ValueError/TypeError: invalid format spec
+ # MemoryError: format spec with huge width (e.g. f'{0:11111111111}')
yield util.Uninferable
uninferable_already_generated = True
continue
diff --git a/tests/test_inference.py b/tests/test_inference.py
index 5f6478a..024ad85 100644
--- a/tests/test_inference.py
+++ b/tests/test_inference.py
@@ -5262,6 +5262,28 @@
assert value_node.value == result
+def test_fstring_large_width_no_memory_error() -> None:
+ """MemoryError should not crash inference for f-strings with huge width.
+
+ Regression test for https://github.com/pylint-dev/astroid/issues/2762
+ """
+
+ class OOMInt(int):
+ """An int whose __format__ raises MemoryError, simulating f'{0:>11111111111}'."""
+
+ def __format__(self, spec: str) -> str:
+ raise MemoryError
+
+ node = extract_node("f'{0:>9}'")
+ # Replace the Const value with our OOMInt so format() raises MemoryError
+ # without actually allocating a huge string.
+ fmt_value = node.values[0]
+ fmt_value.value.value = OOMInt(0)
+ inferred = node.inferred()
+ assert len(inferred) == 1
+ assert inferred[0] is util.Uninferable
+
+
def test_augassign_recursion() -> None:
"""Make sure inference doesn't throw a RecursionError.