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