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