• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import sys
2import time
3
4import unittest
5from test import support
6from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
7                               INVALID_UNDERSCORE_LITERALS)
8
9L = [
10        ('0', 0),
11        ('1', 1),
12        ('9', 9),
13        ('10', 10),
14        ('99', 99),
15        ('100', 100),
16        ('314', 314),
17        (' 314', 314),
18        ('314 ', 314),
19        ('  \t\t  314  \t\t  ', 314),
20        (repr(sys.maxsize), sys.maxsize),
21        ('  1x', ValueError),
22        ('  1  ', 1),
23        ('  1\02  ', ValueError),
24        ('', ValueError),
25        (' ', ValueError),
26        ('  \t\t  ', ValueError),
27        ("\u0200", ValueError)
28]
29
30class IntSubclass(int):
31    pass
32
33class IntTestCases(unittest.TestCase):
34
35    def test_basic(self):
36        self.assertEqual(int(314), 314)
37        self.assertEqual(int(3.14), 3)
38        # Check that conversion from float truncates towards zero
39        self.assertEqual(int(-3.14), -3)
40        self.assertEqual(int(3.9), 3)
41        self.assertEqual(int(-3.9), -3)
42        self.assertEqual(int(3.5), 3)
43        self.assertEqual(int(-3.5), -3)
44        self.assertEqual(int("-3"), -3)
45        self.assertEqual(int(" -3 "), -3)
46        self.assertEqual(int("\N{EM SPACE}-3\N{EN SPACE}"), -3)
47        # Different base:
48        self.assertEqual(int("10",16), 16)
49        # Test conversion from strings and various anomalies
50        for s, v in L:
51            for sign in "", "+", "-":
52                for prefix in "", " ", "\t", "  \t\t  ":
53                    ss = prefix + sign + s
54                    vv = v
55                    if sign == "-" and v is not ValueError:
56                        vv = -v
57                    try:
58                        self.assertEqual(int(ss), vv)
59                    except ValueError:
60                        pass
61
62        s = repr(-1-sys.maxsize)
63        x = int(s)
64        self.assertEqual(x+1, -sys.maxsize)
65        self.assertIsInstance(x, int)
66        # should return int
67        self.assertEqual(int(s[1:]), sys.maxsize+1)
68
69        # should return int
70        x = int(1e100)
71        self.assertIsInstance(x, int)
72        x = int(-1e100)
73        self.assertIsInstance(x, int)
74
75
76        # SF bug 434186:  0x80000000/2 != 0x80000000>>1.
77        # Worked by accident in Windows release build, but failed in debug build.
78        # Failed in all Linux builds.
79        x = -1-sys.maxsize
80        self.assertEqual(x >> 1, x//2)
81
82        x = int('1' * 600)
83        self.assertIsInstance(x, int)
84
85
86        self.assertRaises(TypeError, int, 1, 12)
87
88        self.assertEqual(int('0o123', 0), 83)
89        self.assertEqual(int('0x123', 16), 291)
90
91        # Bug 1679: "0x" is not a valid hex literal
92        self.assertRaises(ValueError, int, "0x", 16)
93        self.assertRaises(ValueError, int, "0x", 0)
94
95        self.assertRaises(ValueError, int, "0o", 8)
96        self.assertRaises(ValueError, int, "0o", 0)
97
98        self.assertRaises(ValueError, int, "0b", 2)
99        self.assertRaises(ValueError, int, "0b", 0)
100
101        # SF bug 1334662: int(string, base) wrong answers
102        # Various representations of 2**32 evaluated to 0
103        # rather than 2**32 in previous versions
104
105        self.assertEqual(int('100000000000000000000000000000000', 2), 4294967296)
106        self.assertEqual(int('102002022201221111211', 3), 4294967296)
107        self.assertEqual(int('10000000000000000', 4), 4294967296)
108        self.assertEqual(int('32244002423141', 5), 4294967296)
109        self.assertEqual(int('1550104015504', 6), 4294967296)
110        self.assertEqual(int('211301422354', 7), 4294967296)
111        self.assertEqual(int('40000000000', 8), 4294967296)
112        self.assertEqual(int('12068657454', 9), 4294967296)
113        self.assertEqual(int('4294967296', 10), 4294967296)
114        self.assertEqual(int('1904440554', 11), 4294967296)
115        self.assertEqual(int('9ba461594', 12), 4294967296)
116        self.assertEqual(int('535a79889', 13), 4294967296)
117        self.assertEqual(int('2ca5b7464', 14), 4294967296)
118        self.assertEqual(int('1a20dcd81', 15), 4294967296)
119        self.assertEqual(int('100000000', 16), 4294967296)
120        self.assertEqual(int('a7ffda91', 17), 4294967296)
121        self.assertEqual(int('704he7g4', 18), 4294967296)
122        self.assertEqual(int('4f5aff66', 19), 4294967296)
123        self.assertEqual(int('3723ai4g', 20), 4294967296)
124        self.assertEqual(int('281d55i4', 21), 4294967296)
125        self.assertEqual(int('1fj8b184', 22), 4294967296)
126        self.assertEqual(int('1606k7ic', 23), 4294967296)
127        self.assertEqual(int('mb994ag', 24), 4294967296)
128        self.assertEqual(int('hek2mgl', 25), 4294967296)
129        self.assertEqual(int('dnchbnm', 26), 4294967296)
130        self.assertEqual(int('b28jpdm', 27), 4294967296)
131        self.assertEqual(int('8pfgih4', 28), 4294967296)
132        self.assertEqual(int('76beigg', 29), 4294967296)
133        self.assertEqual(int('5qmcpqg', 30), 4294967296)
134        self.assertEqual(int('4q0jto4', 31), 4294967296)
135        self.assertEqual(int('4000000', 32), 4294967296)
136        self.assertEqual(int('3aokq94', 33), 4294967296)
137        self.assertEqual(int('2qhxjli', 34), 4294967296)
138        self.assertEqual(int('2br45qb', 35), 4294967296)
139        self.assertEqual(int('1z141z4', 36), 4294967296)
140
141        # tests with base 0
142        # this fails on 3.0, but in 2.x the old octal syntax is allowed
143        self.assertEqual(int(' 0o123  ', 0), 83)
144        self.assertEqual(int(' 0o123  ', 0), 83)
145        self.assertEqual(int('000', 0), 0)
146        self.assertEqual(int('0o123', 0), 83)
147        self.assertEqual(int('0x123', 0), 291)
148        self.assertEqual(int('0b100', 0), 4)
149        self.assertEqual(int(' 0O123   ', 0), 83)
150        self.assertEqual(int(' 0X123  ', 0), 291)
151        self.assertEqual(int(' 0B100 ', 0), 4)
152
153        # without base still base 10
154        self.assertEqual(int('0123'), 123)
155        self.assertEqual(int('0123', 10), 123)
156
157        # tests with prefix and base != 0
158        self.assertEqual(int('0x123', 16), 291)
159        self.assertEqual(int('0o123', 8), 83)
160        self.assertEqual(int('0b100', 2), 4)
161        self.assertEqual(int('0X123', 16), 291)
162        self.assertEqual(int('0O123', 8), 83)
163        self.assertEqual(int('0B100', 2), 4)
164
165        # the code has special checks for the first character after the
166        #  type prefix
167        self.assertRaises(ValueError, int, '0b2', 2)
168        self.assertRaises(ValueError, int, '0b02', 2)
169        self.assertRaises(ValueError, int, '0B2', 2)
170        self.assertRaises(ValueError, int, '0B02', 2)
171        self.assertRaises(ValueError, int, '0o8', 8)
172        self.assertRaises(ValueError, int, '0o08', 8)
173        self.assertRaises(ValueError, int, '0O8', 8)
174        self.assertRaises(ValueError, int, '0O08', 8)
175        self.assertRaises(ValueError, int, '0xg', 16)
176        self.assertRaises(ValueError, int, '0x0g', 16)
177        self.assertRaises(ValueError, int, '0Xg', 16)
178        self.assertRaises(ValueError, int, '0X0g', 16)
179
180        # SF bug 1334662: int(string, base) wrong answers
181        # Checks for proper evaluation of 2**32 + 1
182        self.assertEqual(int('100000000000000000000000000000001', 2), 4294967297)
183        self.assertEqual(int('102002022201221111212', 3), 4294967297)
184        self.assertEqual(int('10000000000000001', 4), 4294967297)
185        self.assertEqual(int('32244002423142', 5), 4294967297)
186        self.assertEqual(int('1550104015505', 6), 4294967297)
187        self.assertEqual(int('211301422355', 7), 4294967297)
188        self.assertEqual(int('40000000001', 8), 4294967297)
189        self.assertEqual(int('12068657455', 9), 4294967297)
190        self.assertEqual(int('4294967297', 10), 4294967297)
191        self.assertEqual(int('1904440555', 11), 4294967297)
192        self.assertEqual(int('9ba461595', 12), 4294967297)
193        self.assertEqual(int('535a7988a', 13), 4294967297)
194        self.assertEqual(int('2ca5b7465', 14), 4294967297)
195        self.assertEqual(int('1a20dcd82', 15), 4294967297)
196        self.assertEqual(int('100000001', 16), 4294967297)
197        self.assertEqual(int('a7ffda92', 17), 4294967297)
198        self.assertEqual(int('704he7g5', 18), 4294967297)
199        self.assertEqual(int('4f5aff67', 19), 4294967297)
200        self.assertEqual(int('3723ai4h', 20), 4294967297)
201        self.assertEqual(int('281d55i5', 21), 4294967297)
202        self.assertEqual(int('1fj8b185', 22), 4294967297)
203        self.assertEqual(int('1606k7id', 23), 4294967297)
204        self.assertEqual(int('mb994ah', 24), 4294967297)
205        self.assertEqual(int('hek2mgm', 25), 4294967297)
206        self.assertEqual(int('dnchbnn', 26), 4294967297)
207        self.assertEqual(int('b28jpdn', 27), 4294967297)
208        self.assertEqual(int('8pfgih5', 28), 4294967297)
209        self.assertEqual(int('76beigh', 29), 4294967297)
210        self.assertEqual(int('5qmcpqh', 30), 4294967297)
211        self.assertEqual(int('4q0jto5', 31), 4294967297)
212        self.assertEqual(int('4000001', 32), 4294967297)
213        self.assertEqual(int('3aokq95', 33), 4294967297)
214        self.assertEqual(int('2qhxjlj', 34), 4294967297)
215        self.assertEqual(int('2br45qc', 35), 4294967297)
216        self.assertEqual(int('1z141z5', 36), 4294967297)
217
218    def test_underscores(self):
219        for lit in VALID_UNDERSCORE_LITERALS:
220            if any(ch in lit for ch in '.eEjJ'):
221                continue
222            self.assertEqual(int(lit, 0), eval(lit))
223            self.assertEqual(int(lit, 0), int(lit.replace('_', ''), 0))
224        for lit in INVALID_UNDERSCORE_LITERALS:
225            if any(ch in lit for ch in '.eEjJ'):
226                continue
227            self.assertRaises(ValueError, int, lit, 0)
228        # Additional test cases with bases != 0, only for the constructor:
229        self.assertEqual(int("1_00", 3), 9)
230        self.assertEqual(int("0_100"), 100)  # not valid as a literal!
231        self.assertEqual(int(b"1_00"), 100)  # byte underscore
232        self.assertRaises(ValueError, int, "_100")
233        self.assertRaises(ValueError, int, "+_100")
234        self.assertRaises(ValueError, int, "1__00")
235        self.assertRaises(ValueError, int, "100_")
236
237    @support.cpython_only
238    def test_small_ints(self):
239        # Bug #3236: Return small longs from PyLong_FromString
240        self.assertIs(int('10'), 10)
241        self.assertIs(int('-1'), -1)
242        self.assertIs(int(b'10'), 10)
243        self.assertIs(int(b'-1'), -1)
244
245    def test_no_args(self):
246        self.assertEqual(int(), 0)
247
248    def test_keyword_args(self):
249        # Test invoking int() using keyword arguments.
250        self.assertEqual(int('100', base=2), 4)
251        with self.assertRaisesRegex(TypeError, 'keyword argument'):
252            int(x=1.2)
253        with self.assertRaisesRegex(TypeError, 'keyword argument'):
254            int(x='100', base=2)
255        self.assertRaises(TypeError, int, base=10)
256        self.assertRaises(TypeError, int, base=0)
257
258    def test_int_base_limits(self):
259        """Testing the supported limits of the int() base parameter."""
260        self.assertEqual(int('0', 5), 0)
261        with self.assertRaises(ValueError):
262            int('0', 1)
263        with self.assertRaises(ValueError):
264            int('0', 37)
265        with self.assertRaises(ValueError):
266            int('0', -909)  # An old magic value base from Python 2.
267        with self.assertRaises(ValueError):
268            int('0', base=0-(2**234))
269        with self.assertRaises(ValueError):
270            int('0', base=2**234)
271        # Bases 2 through 36 are supported.
272        for base in range(2,37):
273            self.assertEqual(int('0', base=base), 0)
274
275    def test_int_base_bad_types(self):
276        """Not integer types are not valid bases; issue16772."""
277        with self.assertRaises(TypeError):
278            int('0', 5.5)
279        with self.assertRaises(TypeError):
280            int('0', 5.0)
281
282    def test_int_base_indexable(self):
283        class MyIndexable(object):
284            def __init__(self, value):
285                self.value = value
286            def __index__(self):
287                return self.value
288
289        # Check out of range bases.
290        for base in 2**100, -2**100, 1, 37:
291            with self.assertRaises(ValueError):
292                int('43', base)
293
294        # Check in-range bases.
295        self.assertEqual(int('101', base=MyIndexable(2)), 5)
296        self.assertEqual(int('101', base=MyIndexable(10)), 101)
297        self.assertEqual(int('101', base=MyIndexable(36)), 1 + 36**2)
298
299    def test_non_numeric_input_types(self):
300        # Test possible non-numeric types for the argument x, including
301        # subclasses of the explicitly documented accepted types.
302        class CustomStr(str): pass
303        class CustomBytes(bytes): pass
304        class CustomByteArray(bytearray): pass
305
306        factories = [
307            bytes,
308            bytearray,
309            lambda b: CustomStr(b.decode()),
310            CustomBytes,
311            CustomByteArray,
312            memoryview,
313        ]
314        try:
315            from array import array
316        except ImportError:
317            pass
318        else:
319            factories.append(lambda b: array('B', b))
320
321        for f in factories:
322            x = f(b'100')
323            with self.subTest(type(x)):
324                self.assertEqual(int(x), 100)
325                if isinstance(x, (str, bytes, bytearray)):
326                    self.assertEqual(int(x, 2), 4)
327                else:
328                    msg = "can't convert non-string"
329                    with self.assertRaisesRegex(TypeError, msg):
330                        int(x, 2)
331                with self.assertRaisesRegex(ValueError, 'invalid literal'):
332                    int(f(b'A' * 0x10))
333
334    def test_int_memoryview(self):
335        self.assertEqual(int(memoryview(b'123')[1:3]), 23)
336        self.assertEqual(int(memoryview(b'123\x00')[1:3]), 23)
337        self.assertEqual(int(memoryview(b'123 ')[1:3]), 23)
338        self.assertEqual(int(memoryview(b'123A')[1:3]), 23)
339        self.assertEqual(int(memoryview(b'1234')[1:3]), 23)
340
341    def test_string_float(self):
342        self.assertRaises(ValueError, int, '1.2')
343
344    def test_intconversion(self):
345        # Test __int__()
346        class ClassicMissingMethods:
347            pass
348        self.assertRaises(TypeError, int, ClassicMissingMethods())
349
350        class MissingMethods(object):
351            pass
352        self.assertRaises(TypeError, int, MissingMethods())
353
354        class Foo0:
355            def __int__(self):
356                return 42
357
358        self.assertEqual(int(Foo0()), 42)
359
360        class Classic:
361            pass
362        for base in (object, Classic):
363            class IntOverridesTrunc(base):
364                def __int__(self):
365                    return 42
366                def __trunc__(self):
367                    return -12
368            self.assertEqual(int(IntOverridesTrunc()), 42)
369
370            class JustTrunc(base):
371                def __trunc__(self):
372                    return 42
373            self.assertEqual(int(JustTrunc()), 42)
374
375            class ExceptionalTrunc(base):
376                def __trunc__(self):
377                    1 / 0
378            with self.assertRaises(ZeroDivisionError):
379                int(ExceptionalTrunc())
380
381            for trunc_result_base in (object, Classic):
382                class Index(trunc_result_base):
383                    def __index__(self):
384                        return 42
385
386                class TruncReturnsNonInt(base):
387                    def __trunc__(self):
388                        return Index()
389                self.assertEqual(int(TruncReturnsNonInt()), 42)
390
391                class Intable(trunc_result_base):
392                    def __int__(self):
393                        return 42
394
395                class TruncReturnsNonIndex(base):
396                    def __trunc__(self):
397                        return Intable()
398                self.assertEqual(int(TruncReturnsNonInt()), 42)
399
400                class NonIntegral(trunc_result_base):
401                    def __trunc__(self):
402                        # Check that we avoid infinite recursion.
403                        return NonIntegral()
404
405                class TruncReturnsNonIntegral(base):
406                    def __trunc__(self):
407                        return NonIntegral()
408                try:
409                    int(TruncReturnsNonIntegral())
410                except TypeError as e:
411                    self.assertEqual(str(e),
412                                      "__trunc__ returned non-Integral"
413                                      " (type NonIntegral)")
414                else:
415                    self.fail("Failed to raise TypeError with %s" %
416                              ((base, trunc_result_base),))
417
418                # Regression test for bugs.python.org/issue16060.
419                class BadInt(trunc_result_base):
420                    def __int__(self):
421                        return 42.0
422
423                class TruncReturnsBadInt(base):
424                    def __trunc__(self):
425                        return BadInt()
426
427                with self.assertRaises(TypeError):
428                    int(TruncReturnsBadInt())
429
430    def test_int_subclass_with_index(self):
431        class MyIndex(int):
432            def __index__(self):
433                return 42
434
435        class BadIndex(int):
436            def __index__(self):
437                return 42.0
438
439        my_int = MyIndex(7)
440        self.assertEqual(my_int, 7)
441        self.assertEqual(int(my_int), 7)
442
443        self.assertEqual(int(BadIndex()), 0)
444
445    def test_int_subclass_with_int(self):
446        class MyInt(int):
447            def __int__(self):
448                return 42
449
450        class BadInt(int):
451            def __int__(self):
452                return 42.0
453
454        my_int = MyInt(7)
455        self.assertEqual(my_int, 7)
456        self.assertEqual(int(my_int), 42)
457
458        my_int = BadInt(7)
459        self.assertEqual(my_int, 7)
460        self.assertRaises(TypeError, int, my_int)
461
462    def test_int_returns_int_subclass(self):
463        class BadIndex:
464            def __index__(self):
465                return True
466
467        class BadIndex2(int):
468            def __index__(self):
469                return True
470
471        class BadInt:
472            def __int__(self):
473                return True
474
475        class BadInt2(int):
476            def __int__(self):
477                return True
478
479        class TruncReturnsBadIndex:
480            def __trunc__(self):
481                return BadIndex()
482
483        class TruncReturnsBadInt:
484            def __trunc__(self):
485                return BadInt()
486
487        class TruncReturnsIntSubclass:
488            def __trunc__(self):
489                return True
490
491        bad_int = BadIndex()
492        with self.assertWarns(DeprecationWarning):
493            n = int(bad_int)
494        self.assertEqual(n, 1)
495        self.assertIs(type(n), int)
496
497        bad_int = BadIndex2()
498        n = int(bad_int)
499        self.assertEqual(n, 0)
500        self.assertIs(type(n), int)
501
502        bad_int = BadInt()
503        with self.assertWarns(DeprecationWarning):
504            n = int(bad_int)
505        self.assertEqual(n, 1)
506        self.assertIs(type(n), int)
507
508        bad_int = BadInt2()
509        with self.assertWarns(DeprecationWarning):
510            n = int(bad_int)
511        self.assertEqual(n, 1)
512        self.assertIs(type(n), int)
513
514        bad_int = TruncReturnsBadIndex()
515        with self.assertWarns(DeprecationWarning):
516            n = int(bad_int)
517        self.assertEqual(n, 1)
518        self.assertIs(type(n), int)
519
520        bad_int = TruncReturnsBadInt()
521        with self.assertWarns(DeprecationWarning):
522            n = int(bad_int)
523        self.assertEqual(n, 1)
524        self.assertIs(type(n), int)
525
526        good_int = TruncReturnsIntSubclass()
527        n = int(good_int)
528        self.assertEqual(n, 1)
529        self.assertIs(type(n), int)
530        n = IntSubclass(good_int)
531        self.assertEqual(n, 1)
532        self.assertIs(type(n), IntSubclass)
533
534    def test_error_message(self):
535        def check(s, base=None):
536            with self.assertRaises(ValueError,
537                                   msg="int(%r, %r)" % (s, base)) as cm:
538                if base is None:
539                    int(s)
540                else:
541                    int(s, base)
542            self.assertEqual(cm.exception.args[0],
543                "invalid literal for int() with base %d: %r" %
544                (10 if base is None else base, s))
545
546        check('\xbd')
547        check('123\xbd')
548        check('  123 456  ')
549
550        check('123\x00')
551        # SF bug 1545497: embedded NULs were not detected with explicit base
552        check('123\x00', 10)
553        check('123\x00 245', 20)
554        check('123\x00 245', 16)
555        check('123\x00245', 20)
556        check('123\x00245', 16)
557        # byte string with embedded NUL
558        check(b'123\x00')
559        check(b'123\x00', 10)
560        # non-UTF-8 byte string
561        check(b'123\xbd')
562        check(b'123\xbd', 10)
563        # lone surrogate in Unicode string
564        check('123\ud800')
565        check('123\ud800', 10)
566
567    def test_issue31619(self):
568        self.assertEqual(int('1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1_0_1', 2),
569                         0b1010101010101010101010101010101)
570        self.assertEqual(int('1_2_3_4_5_6_7_0_1_2_3', 8), 0o12345670123)
571        self.assertEqual(int('1_2_3_4_5_6_7_8_9', 16), 0x123456789)
572        self.assertEqual(int('1_2_3_4_5_6_7', 32), 1144132807)
573
574
575class IntStrDigitLimitsTests(unittest.TestCase):
576
577    int_class = int  # Override this in subclasses to reuse the suite.
578
579    def setUp(self):
580        super().setUp()
581        self._previous_limit = sys.get_int_max_str_digits()
582        sys.set_int_max_str_digits(2048)
583
584    def tearDown(self):
585        sys.set_int_max_str_digits(self._previous_limit)
586        super().tearDown()
587
588    def test_disabled_limit(self):
589        self.assertGreater(sys.get_int_max_str_digits(), 0)
590        self.assertLess(sys.get_int_max_str_digits(), 20_000)
591        with support.adjust_int_max_str_digits(0):
592            self.assertEqual(sys.get_int_max_str_digits(), 0)
593            i = self.int_class('1' * 20_000)
594            str(i)
595        self.assertGreater(sys.get_int_max_str_digits(), 0)
596
597    def test_max_str_digits_edge_cases(self):
598        """Ignore the +/- sign and space padding."""
599        int_class = self.int_class
600        maxdigits = sys.get_int_max_str_digits()
601
602        int_class('1' * maxdigits)
603        int_class(' ' + '1' * maxdigits)
604        int_class('1' * maxdigits + ' ')
605        int_class('+' + '1' * maxdigits)
606        int_class('-' + '1' * maxdigits)
607        self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits)
608
609    def check(self, i, base=None):
610        with self.assertRaises(ValueError):
611            if base is None:
612                self.int_class(i)
613            else:
614                self.int_class(i, base)
615
616    def test_max_str_digits(self):
617        maxdigits = sys.get_int_max_str_digits()
618
619        self.check('1' * (maxdigits + 1))
620        self.check(' ' + '1' * (maxdigits + 1))
621        self.check('1' * (maxdigits + 1) + ' ')
622        self.check('+' + '1' * (maxdigits + 1))
623        self.check('-' + '1' * (maxdigits + 1))
624        self.check('1' * (maxdigits + 1))
625
626        i = 10 ** maxdigits
627        with self.assertRaises(ValueError):
628            str(i)
629
630    def test_denial_of_service_prevented_int_to_str(self):
631        """Regression test: ensure we fail before performing O(N**2) work."""
632        maxdigits = sys.get_int_max_str_digits()
633        assert maxdigits < 50_000, maxdigits  # A test prerequisite.
634        get_time = time.process_time
635        if get_time() <= 0:  # some platforms like WASM lack process_time()
636            get_time = time.monotonic
637
638        huge_int = int(f'0x{"c"*65_000}', base=16)  # 78268 decimal digits.
639        digits = 78_268
640        with support.adjust_int_max_str_digits(digits):
641            start = get_time()
642            huge_decimal = str(huge_int)
643        seconds_to_convert = get_time() - start
644        self.assertEqual(len(huge_decimal), digits)
645        # Ensuring that we chose a slow enough conversion to measure.
646        # It takes 0.1 seconds on a Zen based cloud VM in an opt build.
647        if seconds_to_convert < 0.005:
648            raise unittest.SkipTest('"slow" conversion took only '
649                                    f'{seconds_to_convert} seconds.')
650
651        # We test with the limit almost at the size needed to check performance.
652        # The performant limit check is slightly fuzzy, give it a some room.
653        with support.adjust_int_max_str_digits(int(.995 * digits)):
654            with self.assertRaises(ValueError) as err:
655                start = get_time()
656                str(huge_int)
657            seconds_to_fail_huge = get_time() - start
658        self.assertIn('conversion', str(err.exception))
659        self.assertLess(seconds_to_fail_huge, seconds_to_convert/8)
660
661        # Now we test that a conversion that would take 30x as long also fails
662        # in a similarly fast fashion.
663        extra_huge_int = int(f'0x{"c"*500_000}', base=16)  # 602060 digits.
664        with self.assertRaises(ValueError) as err:
665            start = get_time()
666            # If not limited, 8 seconds said Zen based cloud VM.
667            str(extra_huge_int)
668        seconds_to_fail_extra_huge = get_time() - start
669        self.assertIn('conversion', str(err.exception))
670        self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8)
671
672    def test_denial_of_service_prevented_str_to_int(self):
673        """Regression test: ensure we fail before performing O(N**2) work."""
674        maxdigits = sys.get_int_max_str_digits()
675        assert maxdigits < 100_000, maxdigits  # A test prerequisite.
676        get_time = time.process_time
677        if get_time() <= 0:  # some platforms like WASM lack process_time()
678            get_time = time.monotonic
679
680        digits = 133700
681        huge = '8'*digits
682        with support.adjust_int_max_str_digits(digits):
683            start = get_time()
684            int(huge)
685        seconds_to_convert = get_time() - start
686        # Ensuring that we chose a slow enough conversion to measure.
687        # It takes 0.1 seconds on a Zen based cloud VM in an opt build.
688        if seconds_to_convert < 0.005:
689            raise unittest.SkipTest('"slow" conversion took only '
690                                    f'{seconds_to_convert} seconds.')
691
692        with support.adjust_int_max_str_digits(digits - 1):
693            with self.assertRaises(ValueError) as err:
694                start = get_time()
695                int(huge)
696            seconds_to_fail_huge = get_time() - start
697        self.assertIn('conversion', str(err.exception))
698        self.assertLess(seconds_to_fail_huge, seconds_to_convert/8)
699
700        # Now we test that a conversion that would take 30x as long also fails
701        # in a similarly fast fashion.
702        extra_huge = '7'*1_200_000
703        with self.assertRaises(ValueError) as err:
704            start = get_time()
705            # If not limited, 8 seconds in the Zen based cloud VM.
706            int(extra_huge)
707        seconds_to_fail_extra_huge = get_time() - start
708        self.assertIn('conversion', str(err.exception))
709        self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8)
710
711    def test_power_of_two_bases_unlimited(self):
712        """The limit does not apply to power of 2 bases."""
713        maxdigits = sys.get_int_max_str_digits()
714
715        for base in (2, 4, 8, 16, 32):
716            with self.subTest(base=base):
717                self.int_class('1' * (maxdigits + 1), base)
718                assert maxdigits < 100_000
719                self.int_class('1' * 100_000, base)
720
721    def test_underscores_ignored(self):
722        maxdigits = sys.get_int_max_str_digits()
723
724        triples = maxdigits // 3
725        s = '111' * triples
726        s_ = '1_11' * triples
727        self.int_class(s)  # succeeds
728        self.int_class(s_)  # succeeds
729        self.check(f'{s}111')
730        self.check(f'{s_}_111')
731
732    def test_sign_not_counted(self):
733        int_class = self.int_class
734        max_digits = sys.get_int_max_str_digits()
735        s = '5' * max_digits
736        i = int_class(s)
737        pos_i = int_class(f'+{s}')
738        assert i == pos_i
739        neg_i = int_class(f'-{s}')
740        assert -pos_i == neg_i
741        str(pos_i)
742        str(neg_i)
743
744    def _other_base_helper(self, base):
745        int_class = self.int_class
746        max_digits = sys.get_int_max_str_digits()
747        s = '2' * max_digits
748        i = int_class(s, base)
749        if base > 10:
750            with self.assertRaises(ValueError):
751                str(i)
752        elif base < 10:
753            str(i)
754        with self.assertRaises(ValueError) as err:
755            int_class(f'{s}1', base)
756
757    def test_int_from_other_bases(self):
758        base = 3
759        with self.subTest(base=base):
760            self._other_base_helper(base)
761        base = 36
762        with self.subTest(base=base):
763            self._other_base_helper(base)
764
765
766class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests):
767    int_class = IntSubclass
768
769
770if __name__ == "__main__":
771    unittest.main()
772