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