Skip to content

gh-135487: fix reprlib.Repr.repr_int when given very large integers #135506

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Doc/library/reprlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ which format specific object types.


.. method:: Repr.repr_TYPE(obj, level)
:noindex:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this? repr_TYPE is not a real method name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was to make a link in the NEWS entry.


Formatting methods for specific types are implemented as methods with a name
based on the type name. In the method name, **TYPE** is replaced by
Expand Down
17 changes: 16 additions & 1 deletion Lib/reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,22 @@ def repr_str(self, x, level):
return s

def repr_int(self, x, level):
s = builtins.repr(x) # XXX Hope this isn't too slow...
try:
s = builtins.repr(x)
except ValueError as exc:
assert 'sys.set_int_max_str_digits()' in str(exc)
# Those imports must be deferred due to Python's build system
# where the reprlib module is imported before the math module.
import math, sys
# Integers with more than sys.get_int_max_str_digits() digits
# are rendered differently as their repr() raises a ValueError.
# See https://github.com/python/cpython/issues/135487.
k = 1 + int(math.log10(abs(x)))
# Note: math.log10(abs(x)) may be overestimated or underestimated,
# but for simplicity, we do not compute the exact number of digits.
max_digits = sys.get_int_max_str_digits()
return (f'<{x.__class__.__name__} instance with roughly {k} '
f'digits (limit at {max_digits}) at 0x{id(x):x}>')
if len(s) > self.maxlong:
i = max(0, (self.maxlong-3)//2)
j = max(0, self.maxlong-3-i)
Expand Down
40 changes: 32 additions & 8 deletions Lib/test/test_reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,38 @@ def test_frozenset(self):
eq(r(frozenset({1, 2, 3, 4, 5, 6, 7})), "frozenset({1, 2, 3, 4, 5, 6, ...})")

def test_numbers(self):
eq = self.assertEqual
eq(r(123), repr(123))
eq(r(123), repr(123))
eq(r(1.0/3), repr(1.0/3))

n = 10**100
expected = repr(n)[:18] + "..." + repr(n)[-19:]
eq(r(n), expected)
for x in [123, 1.0 / 3]:
self.assertEqual(r(x), repr(x))

max_digits = sys.get_int_max_str_digits()
for k in [100, max_digits - 1]:
with self.subTest(f'10 ** {k}', k=k):
n = 10 ** k
expected = repr(n)[:18] + "..." + repr(n)[-19:]
self.assertEqual(r(n), expected)

def re_msg(n, d):
return (rf'<{n.__class__.__name__} instance with roughly {d} '
rf'digits \(limit at {max_digits}\) at 0x[a-f0-9]+>')

k = max_digits
with self.subTest(f'10 ** {k}', k=k):
n = 10 ** k
self.assertRaises(ValueError, repr, n)
self.assertRegex(r(n), re_msg(n, k + 1))

for k in [max_digits + 1, 2 * max_digits]:
self.assertGreater(k, 100)
with self.subTest(f'10 ** {k}', k=k):
n = 10 ** k
self.assertRaises(ValueError, repr, n)
self.assertRegex(r(n), re_msg(n, k + 1))
with self.subTest(f'10 ** {k} - 1', k=k):
n = 10 ** k - 1
# Here, since math.log10(n) == math.log10(n-1),
# the number of digits of n - 1 is overestimated.
self.assertRaises(ValueError, repr, n)
self.assertRegex(r(n), re_msg(n, k + 1))

def test_instance(self):
eq = self.assertEqual
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :meth:`reprlib.Repr.repr_int <reprlib.Repr.repr_TYPE>` when given
integers with more than :func:`sys.get_int_max_str_digits` digits. Patch by
Bénédikt Tran.
Loading