From 431473af17dd765344c210d77f698e6a6129972c Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 13 Jul 2025 17:29:32 +0300 Subject: [PATCH 1/4] Update test_complex from 3.13.5 --- Lib/test/support/numbers.py | 80 +++++++ Lib/test/test_complex.py | 409 +++++++++++++++++++----------------- 2 files changed, 293 insertions(+), 196 deletions(-) create mode 100644 Lib/test/support/numbers.py diff --git a/Lib/test/support/numbers.py b/Lib/test/support/numbers.py new file mode 100644 index 00000000000..d5dbb41aceb --- /dev/null +++ b/Lib/test/support/numbers.py @@ -0,0 +1,80 @@ +# These are shared with test_tokenize and other test modules. +# +# Note: since several test cases filter out floats by looking for "e" and ".", +# don't add hexadecimal literals that contain "e" or "E". +VALID_UNDERSCORE_LITERALS = [ + '0_0_0', + '4_2', + '1_0000_0000', + '0b1001_0100', + '0xffff_ffff', + '0o5_7_7', + '1_00_00.5', + '1_00_00.5e5', + '1_00_00e5_1', + '1e1_0', + '.1_4', + '.1_4e1', + '0b_0', + '0x_f', + '0o_5', + '1_00_00j', + '1_00_00.5j', + '1_00_00e5_1j', + '.1_4j', + '(1_2.5+3_3j)', + '(.5_6j)', +] +INVALID_UNDERSCORE_LITERALS = [ + # Trailing underscores: + '0_', + '42_', + '1.4j_', + '0x_', + '0b1_', + '0xf_', + '0o5_', + '0 if 1_Else 1', + # Underscores in the base selector: + '0_b0', + '0_xf', + '0_o5', + # Old-style octal, still disallowed: + '0_7', + '09_99', + # Multiple consecutive underscores: + '4_______2', + '0.1__4', + '0.1__4j', + '0b1001__0100', + '0xffff__ffff', + '0x___', + '0o5__77', + '1e1__0', + '1e1__0j', + # Underscore right before a dot: + '1_.4', + '1_.4j', + # Underscore right after a dot: + '1._4', + '1._4j', + '._5', + '._5j', + # Underscore right after a sign: + '1.0e+_1', + '1.0e+_1j', + # Underscore right before j: + '1.4_j', + '1.4e5_j', + # Underscore right before e: + '1_e1', + '1.4_e1', + '1.4_e1j', + # Underscore right after e: + '1e_1', + '1.4e_1', + '1.4e_1j', + # Complex cases with parens: + '(1+1.5_j_)', + '(1+1.5_j)', +] diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index dd3c4f281a3..86d075de8ce 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -1,15 +1,19 @@ import unittest import sys from test import support -from test.test_grammar import (VALID_UNDERSCORE_LITERALS, - INVALID_UNDERSCORE_LITERALS) +from test.support.testcase import ComplexesAreIdenticalMixin +from test.support.numbers import ( + VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS, +) from random import random -from math import atan2, isnan, copysign +from math import isnan, copysign import operator INF = float("inf") NAN = float("nan") +DBL_MAX = sys.float_info.max # These tests ensure that complex math does the right thing ZERO_DIVISION = ( @@ -20,7 +24,28 @@ (1, 0+0j), ) -class ComplexTest(unittest.TestCase): +class WithIndex: + def __init__(self, value): + self.value = value + def __index__(self): + return self.value + +class WithFloat: + def __init__(self, value): + self.value = value + def __float__(self): + return self.value + +class ComplexSubclass(complex): + pass + +class WithComplex: + def __init__(self, value): + self.value = value + def __complex__(self): + return self.value + +class ComplexTest(ComplexesAreIdenticalMixin, unittest.TestCase): def assertAlmostEqual(self, a, b): if isinstance(a, complex): @@ -49,29 +74,6 @@ def assertCloseAbs(self, x, y, eps=1e-9): # check that relative difference < eps self.assertTrue(abs((x-y)/y) < eps) - def assertFloatsAreIdentical(self, x, y): - """assert that floats x and y are identical, in the sense that: - (1) both x and y are nans, or - (2) both x and y are infinities, with the same sign, or - (3) both x and y are zeros, with the same sign, or - (4) x and y are both finite and nonzero, and x == y - - """ - msg = 'floats {!r} and {!r} are not identical' - - if isnan(x) or isnan(y): - if isnan(x) and isnan(y): - return - elif x == y: - if x != 0.0: - return - # both zero; check that signs match - elif copysign(1.0, x) == copysign(1.0, y): - return - else: - msg += ': zeros have different signs' - self.fail(msg.format(x, y)) - def assertClose(self, x, y, eps=1e-9): """Return true iff complexes x and y "are close".""" self.assertCloseAbs(x.real, y.real, eps) @@ -303,6 +305,11 @@ def test_pow(self): except OverflowError: pass + # gh-113841: possible undefined division by 0 in _Py_c_pow() + x, y = 9j, 33j**3 + with self.assertRaises(OverflowError): + x**y + def test_pow_with_small_integer_exponents(self): # Check that small integer exponents are handled identically # regardless of their type. @@ -340,138 +347,93 @@ def test_boolcontext(self): def test_conjugate(self): self.assertClose(complex(5.3, 9.8).conjugate(), 5.3-9.8j) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_constructor(self): - class NS: - def __init__(self, value): self.value = value - def __complex__(self): return self.value - self.assertEqual(complex(NS(1+10j)), 1+10j) - self.assertRaises(TypeError, complex, NS(None)) - self.assertRaises(TypeError, complex, {}) - self.assertRaises(TypeError, complex, NS(1.5)) - self.assertRaises(TypeError, complex, NS(1)) - self.assertRaises(TypeError, complex, object()) - self.assertRaises(TypeError, complex, NS(4.25+0.5j), object()) - - self.assertAlmostEqual(complex("1+10j"), 1+10j) - self.assertAlmostEqual(complex(10), 10+0j) - self.assertAlmostEqual(complex(10.0), 10+0j) - self.assertAlmostEqual(complex(10), 10+0j) - self.assertAlmostEqual(complex(10+0j), 10+0j) - self.assertAlmostEqual(complex(1,10), 1+10j) - self.assertAlmostEqual(complex(1,10), 1+10j) - self.assertAlmostEqual(complex(1,10.0), 1+10j) - self.assertAlmostEqual(complex(1,10), 1+10j) - self.assertAlmostEqual(complex(1,10), 1+10j) - self.assertAlmostEqual(complex(1,10.0), 1+10j) - self.assertAlmostEqual(complex(1.0,10), 1+10j) - self.assertAlmostEqual(complex(1.0,10), 1+10j) - self.assertAlmostEqual(complex(1.0,10.0), 1+10j) - self.assertAlmostEqual(complex(3.14+0j), 3.14+0j) - self.assertAlmostEqual(complex(3.14), 3.14+0j) - self.assertAlmostEqual(complex(314), 314.0+0j) - self.assertAlmostEqual(complex(314), 314.0+0j) - self.assertAlmostEqual(complex(3.14+0j, 0j), 3.14+0j) - self.assertAlmostEqual(complex(3.14, 0.0), 3.14+0j) - self.assertAlmostEqual(complex(314, 0), 314.0+0j) - self.assertAlmostEqual(complex(314, 0), 314.0+0j) - self.assertAlmostEqual(complex(0j, 3.14j), -3.14+0j) - self.assertAlmostEqual(complex(0.0, 3.14j), -3.14+0j) - self.assertAlmostEqual(complex(0j, 3.14), 3.14j) - self.assertAlmostEqual(complex(0.0, 3.14), 3.14j) - self.assertAlmostEqual(complex("1"), 1+0j) - self.assertAlmostEqual(complex("1j"), 1j) - self.assertAlmostEqual(complex(), 0) - self.assertAlmostEqual(complex("-1"), -1) - self.assertAlmostEqual(complex("+1"), +1) - self.assertAlmostEqual(complex("(1+2j)"), 1+2j) - self.assertAlmostEqual(complex("(1.3+2.2j)"), 1.3+2.2j) - self.assertAlmostEqual(complex("3.14+1J"), 3.14+1j) - self.assertAlmostEqual(complex(" ( +3.14-6J )"), 3.14-6j) - self.assertAlmostEqual(complex(" ( +3.14-J )"), 3.14-1j) - self.assertAlmostEqual(complex(" ( +3.14+j )"), 3.14+1j) - self.assertAlmostEqual(complex("J"), 1j) - self.assertAlmostEqual(complex("( j )"), 1j) - self.assertAlmostEqual(complex("+J"), 1j) - self.assertAlmostEqual(complex("( -j)"), -1j) - self.assertAlmostEqual(complex('1e-500'), 0.0 + 0.0j) - self.assertAlmostEqual(complex('-1e-500j'), 0.0 - 0.0j) - self.assertAlmostEqual(complex('-1e-500+1e-500j'), -0.0 + 0.0j) - self.assertEqual(complex('1-1j'), 1.0 - 1j) - self.assertEqual(complex('1J'), 1j) - - class complex2(complex): pass - self.assertAlmostEqual(complex(complex2(1+1j)), 1+1j) - self.assertAlmostEqual(complex(real=17, imag=23), 17+23j) - self.assertAlmostEqual(complex(real=17+23j), 17+23j) - self.assertAlmostEqual(complex(real=17+23j, imag=23), 17+46j) - self.assertAlmostEqual(complex(real=1+2j, imag=3+4j), -3+5j) + def check(z, x, y): + self.assertIs(type(z), complex) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + + check(complex(), 0.0, 0.0) + check(complex(10), 10.0, 0.0) + check(complex(4.25), 4.25, 0.0) + check(complex(4.25+0j), 4.25, 0.0) + check(complex(4.25+0.5j), 4.25, 0.5) + check(complex(ComplexSubclass(4.25+0.5j)), 4.25, 0.5) + check(complex(WithComplex(4.25+0.5j)), 4.25, 0.5) + + check(complex(1, 10), 1.0, 10.0) + check(complex(1, 10.0), 1.0, 10.0) + check(complex(1, 4.25), 1.0, 4.25) + check(complex(1.0, 10), 1.0, 10.0) + check(complex(4.25, 10), 4.25, 10.0) + check(complex(1.0, 10.0), 1.0, 10.0) + check(complex(4.25, 0.5), 4.25, 0.5) + + check(complex(4.25+0j, 0), 4.25, 0.0) + check(complex(ComplexSubclass(4.25+0j), 0), 4.25, 0.0) + check(complex(WithComplex(4.25+0j), 0), 4.25, 0.0) + check(complex(4.25j, 0), 0.0, 4.25) + check(complex(0j, 4.25), 0.0, 4.25) + check(complex(0, 4.25+0j), 0.0, 4.25) + check(complex(0, ComplexSubclass(4.25+0j)), 0.0, 4.25) + with self.assertRaisesRegex(TypeError, + "second argument must be a number, not 'WithComplex'"): + complex(0, WithComplex(4.25+0j)) + check(complex(0.0, 4.25j), -4.25, 0.0) + check(complex(4.25+0j, 0j), 4.25, 0.0) + check(complex(4.25j, 0j), 0.0, 4.25) + check(complex(0j, 4.25+0j), 0.0, 4.25) + check(complex(0j, 4.25j), -4.25, 0.0) + + check(complex(real=4.25), 4.25, 0.0) + check(complex(real=4.25+0j), 4.25, 0.0) + check(complex(real=4.25+1.5j), 4.25, 1.5) + check(complex(imag=1.5), 0.0, 1.5) + check(complex(real=4.25, imag=1.5), 4.25, 1.5) + check(complex(4.25, imag=1.5), 4.25, 1.5) # check that the sign of a zero in the real or imaginary part - # is preserved when constructing from two floats. (These checks - # are harmless on systems without support for signed zeros.) - def split_zeros(x): - """Function that produces different results for 0. and -0.""" - return atan2(x, -1.) - - self.assertEqual(split_zeros(complex(1., 0.).imag), split_zeros(0.)) - self.assertEqual(split_zeros(complex(1., -0.).imag), split_zeros(-0.)) - self.assertEqual(split_zeros(complex(0., 1.).real), split_zeros(0.)) - self.assertEqual(split_zeros(complex(-0., 1.).real), split_zeros(-0.)) - - c = 3.14 + 1j - self.assertTrue(complex(c) is c) - del c - - self.assertRaises(TypeError, complex, "1", "1") - self.assertRaises(TypeError, complex, 1, "1") - - # SF bug 543840: complex(string) accepts strings with \0 - # Fixed in 2.3. - self.assertRaises(ValueError, complex, '1+1j\0j') - - self.assertRaises(TypeError, int, 5+3j) - self.assertRaises(TypeError, int, 5+3j) - self.assertRaises(TypeError, float, 5+3j) - self.assertRaises(ValueError, complex, "") - self.assertRaises(TypeError, complex, None) - self.assertRaisesRegex(TypeError, "not 'NoneType'", complex, None) - self.assertRaises(ValueError, complex, "\0") - self.assertRaises(ValueError, complex, "3\09") - self.assertRaises(TypeError, complex, "1", "2") - self.assertRaises(TypeError, complex, "1", 42) - self.assertRaises(TypeError, complex, 1, "2") - self.assertRaises(ValueError, complex, "1+") - self.assertRaises(ValueError, complex, "1+1j+1j") - self.assertRaises(ValueError, complex, "--") - self.assertRaises(ValueError, complex, "(1+2j") - self.assertRaises(ValueError, complex, "1+2j)") - self.assertRaises(ValueError, complex, "1+(2j)") - self.assertRaises(ValueError, complex, "(1+2j)123") - self.assertRaises(ValueError, complex, "x") - self.assertRaises(ValueError, complex, "1j+2") - self.assertRaises(ValueError, complex, "1e1ej") - self.assertRaises(ValueError, complex, "1e++1ej") - self.assertRaises(ValueError, complex, ")1+2j(") - self.assertRaisesRegex( - TypeError, + # is preserved when constructing from two floats. + for x in 1.0, -1.0: + for y in 0.0, -0.0: + check(complex(x, y), x, y) + check(complex(y, x), y, x) + + c = complex(4.25, 1.5) + self.assertIs(complex(c), c) + c2 = ComplexSubclass(c) + self.assertEqual(c2, c) + self.assertIs(type(c2), ComplexSubclass) + del c, c2 + + self.assertRaisesRegex(TypeError, "first argument must be a string or a number, not 'dict'", - complex, {1:2}, 1) - self.assertRaisesRegex( - TypeError, + complex, {}) + self.assertRaisesRegex(TypeError, + "first argument must be a string or a number, not 'NoneType'", + complex, None) + self.assertRaisesRegex(TypeError, + "first argument must be a string or a number, not 'dict'", + complex, {1:2}, 0) + self.assertRaisesRegex(TypeError, + "can't take second arg if first is a string", + complex, '1', 0) + self.assertRaisesRegex(TypeError, "second argument must be a number, not 'dict'", - complex, 1, {1:2}) - # the following three are accepted by Python 2.6 - self.assertRaises(ValueError, complex, "1..1j") - self.assertRaises(ValueError, complex, "1.11.1j") - self.assertRaises(ValueError, complex, "1e1.1j") - - # check that complex accepts long unicode strings - self.assertEqual(type(complex("1"*500)), complex) - # check whitespace processing - self.assertEqual(complex('\N{EM SPACE}(\N{EN SPACE}1+1j ) '), 1+1j) - # Invalid unicode string - # See bpo-34087 - self.assertRaises(ValueError, complex, '\u3053\u3093\u306b\u3061\u306f') + complex, 0, {1:2}) + self.assertRaisesRegex(TypeError, + "second arg can't be a string", + complex, 0, '1') + + self.assertRaises(TypeError, complex, WithComplex(1.5)) + self.assertRaises(TypeError, complex, WithComplex(1)) + self.assertRaises(TypeError, complex, WithComplex(None)) + self.assertRaises(TypeError, complex, WithComplex(4.25+0j), object()) + self.assertRaises(TypeError, complex, WithComplex(1.5), object()) + self.assertRaises(TypeError, complex, WithComplex(1), object()) + self.assertRaises(TypeError, complex, WithComplex(None), object()) class EvilExc(Exception): pass @@ -482,33 +444,33 @@ def __complex__(self): self.assertRaises(EvilExc, complex, evilcomplex()) - class float2: - def __init__(self, value): - self.value = value - def __float__(self): - return self.value - - self.assertAlmostEqual(complex(float2(42.)), 42) - self.assertAlmostEqual(complex(real=float2(17.), imag=float2(23.)), 17+23j) - self.assertRaises(TypeError, complex, float2(None)) - - class MyIndex: - def __init__(self, value): - self.value = value - def __index__(self): - return self.value - - self.assertAlmostEqual(complex(MyIndex(42)), 42.0+0.0j) - self.assertAlmostEqual(complex(123, MyIndex(42)), 123.0+42.0j) - self.assertRaises(OverflowError, complex, MyIndex(2**2000)) - self.assertRaises(OverflowError, complex, 123, MyIndex(2**2000)) + check(complex(WithFloat(4.25)), 4.25, 0.0) + check(complex(WithFloat(4.25), 1.5), 4.25, 1.5) + check(complex(1.5, WithFloat(4.25)), 1.5, 4.25) + self.assertRaises(TypeError, complex, WithFloat(42)) + self.assertRaises(TypeError, complex, WithFloat(42), 1.5) + self.assertRaises(TypeError, complex, 1.5, WithFloat(42)) + self.assertRaises(TypeError, complex, WithFloat(None)) + self.assertRaises(TypeError, complex, WithFloat(None), 1.5) + self.assertRaises(TypeError, complex, 1.5, WithFloat(None)) + + check(complex(WithIndex(42)), 42.0, 0.0) + check(complex(WithIndex(42), 1.5), 42.0, 1.5) + check(complex(1.5, WithIndex(42)), 1.5, 42.0) + self.assertRaises(OverflowError, complex, WithIndex(2**2000)) + self.assertRaises(OverflowError, complex, WithIndex(2**2000), 1.5) + self.assertRaises(OverflowError, complex, 1.5, WithIndex(2**2000)) + self.assertRaises(TypeError, complex, WithIndex(None)) + self.assertRaises(TypeError, complex, WithIndex(None), 1.5) + self.assertRaises(TypeError, complex, 1.5, WithIndex(None)) class MyInt: def __int__(self): return 42 self.assertRaises(TypeError, complex, MyInt()) - self.assertRaises(TypeError, complex, 123, MyInt()) + self.assertRaises(TypeError, complex, MyInt(), 1.5) + self.assertRaises(TypeError, complex, 1.5, MyInt()) class complex0(complex): """Test usage of __complex__() when inheriting from 'complex'""" @@ -528,9 +490,9 @@ class complex2(complex): def __complex__(self): return None - self.assertEqual(complex(complex0(1j)), 42j) + check(complex(complex0(1j)), 0.0, 42.0) with self.assertWarns(DeprecationWarning): - self.assertEqual(complex(complex1(1j)), 2j) + check(complex(complex1(1j)), 0.0, 2.0) self.assertRaises(TypeError, complex, complex2(1j)) def test___complex__(self): @@ -538,36 +500,93 @@ def test___complex__(self): self.assertEqual(z.__complex__(), z) self.assertEqual(type(z.__complex__()), complex) - class complex_subclass(complex): - pass - - z = complex_subclass(3 + 4j) + z = ComplexSubclass(3 + 4j) self.assertEqual(z.__complex__(), 3 + 4j) self.assertEqual(type(z.__complex__()), complex) @support.requires_IEEE_754 def test_constructor_special_numbers(self): - class complex2(complex): - pass for x in 0.0, -0.0, INF, -INF, NAN: for y in 0.0, -0.0, INF, -INF, NAN: with self.subTest(x=x, y=y): z = complex(x, y) self.assertFloatsAreIdentical(z.real, x) self.assertFloatsAreIdentical(z.imag, y) - z = complex2(x, y) - self.assertIs(type(z), complex2) + z = ComplexSubclass(x, y) + self.assertIs(type(z), ComplexSubclass) self.assertFloatsAreIdentical(z.real, x) self.assertFloatsAreIdentical(z.imag, y) - z = complex(complex2(x, y)) + z = complex(ComplexSubclass(x, y)) self.assertIs(type(z), complex) self.assertFloatsAreIdentical(z.real, x) self.assertFloatsAreIdentical(z.imag, y) - z = complex2(complex(x, y)) - self.assertIs(type(z), complex2) + z = ComplexSubclass(complex(x, y)) + self.assertIs(type(z), ComplexSubclass) self.assertFloatsAreIdentical(z.real, x) self.assertFloatsAreIdentical(z.imag, y) + def test_constructor_from_string(self): + def check(z, x, y): + self.assertIs(type(z), complex) + self.assertFloatsAreIdentical(z.real, x) + self.assertFloatsAreIdentical(z.imag, y) + + check(complex("1"), 1.0, 0.0) + check(complex("1j"), 0.0, 1.0) + check(complex("-1"), -1.0, 0.0) + check(complex("+1"), 1.0, 0.0) + check(complex("1+2j"), 1.0, 2.0) + check(complex("(1+2j)"), 1.0, 2.0) + check(complex("(1.5+4.25j)"), 1.5, 4.25) + check(complex("4.25+1J"), 4.25, 1.0) + check(complex(" ( +4.25-6J )"), 4.25, -6.0) + check(complex(" ( +4.25-J )"), 4.25, -1.0) + check(complex(" ( +4.25+j )"), 4.25, 1.0) + check(complex("J"), 0.0, 1.0) + check(complex("( j )"), 0.0, 1.0) + check(complex("+J"), 0.0, 1.0) + check(complex("( -j)"), 0.0, -1.0) + check(complex('1-1j'), 1.0, -1.0) + check(complex('1J'), 0.0, 1.0) + + check(complex('1e-500'), 0.0, 0.0) + check(complex('-1e-500j'), 0.0, -0.0) + check(complex('1e-500+1e-500j'), 0.0, 0.0) + check(complex('-1e-500+1e-500j'), -0.0, 0.0) + check(complex('1e-500-1e-500j'), 0.0, -0.0) + check(complex('-1e-500-1e-500j'), -0.0, -0.0) + + # SF bug 543840: complex(string) accepts strings with \0 + # Fixed in 2.3. + self.assertRaises(ValueError, complex, '1+1j\0j') + self.assertRaises(ValueError, complex, "") + self.assertRaises(ValueError, complex, "\0") + self.assertRaises(ValueError, complex, "3\09") + self.assertRaises(ValueError, complex, "1+") + self.assertRaises(ValueError, complex, "1+1j+1j") + self.assertRaises(ValueError, complex, "--") + self.assertRaises(ValueError, complex, "(1+2j") + self.assertRaises(ValueError, complex, "1+2j)") + self.assertRaises(ValueError, complex, "1+(2j)") + self.assertRaises(ValueError, complex, "(1+2j)123") + self.assertRaises(ValueError, complex, "x") + self.assertRaises(ValueError, complex, "1j+2") + self.assertRaises(ValueError, complex, "1e1ej") + self.assertRaises(ValueError, complex, "1e++1ej") + self.assertRaises(ValueError, complex, ")1+2j(") + # the following three are accepted by Python 2.6 + self.assertRaises(ValueError, complex, "1..1j") + self.assertRaises(ValueError, complex, "1.11.1j") + self.assertRaises(ValueError, complex, "1e1.1j") + + # check that complex accepts long unicode strings + self.assertIs(type(complex("1"*500)), complex) + # check whitespace processing + self.assertEqual(complex('\N{EM SPACE}(\N{EN SPACE}1+1j ) '), 1+1j) + # Invalid unicode string + # See bpo-34087 + self.assertRaises(ValueError, complex, '\u3053\u3093\u306b\u3061\u306f') + def test_constructor_negative_nans_from_string(self): self.assertEqual(copysign(1., complex("-nan").real), -1.) self.assertEqual(copysign(1., complex("-nanj").imag), -1.) @@ -589,7 +608,7 @@ def test_underscores(self): def test_hash(self): for x in range(-30, 30): self.assertEqual(hash(x), hash(complex(x, 0))) - x /= 3.0 # now check against floating point + x /= 3.0 # now check against floating-point self.assertEqual(hash(x), hash(complex(x, 0.))) self.assertNotEqual(hash(2000005 - 1j), -1) @@ -599,6 +618,8 @@ def test_abs(self): for num in nums: self.assertAlmostEqual((num.real**2 + num.imag**2) ** 0.5, abs(num)) + self.assertRaises(OverflowError, abs, complex(DBL_MAX, DBL_MAX)) + def test_repr_str(self): def test(v, expected, test_fn=self.assertEqual): test_fn(repr(v), expected) @@ -644,9 +665,6 @@ def test(v, expected, test_fn=self.assertEqual): test(complex(-0., -0.), "(-0-0j)") def test_pos(self): - class ComplexSubclass(complex): - pass - self.assertEqual(+(1+6j), 1+6j) self.assertEqual(+ComplexSubclass(1, 6), 1+6j) self.assertIs(type(+ComplexSubclass(1, 6)), complex) @@ -666,8 +684,8 @@ def test_getnewargs(self): def test_plus_minus_0j(self): # test that -0j and 0j literals are not identified z1, z2 = 0j, -0j - self.assertEqual(atan2(z1.imag, -1.), atan2(0., -1.)) - self.assertEqual(atan2(z2.imag, -1.), atan2(-0., -1.)) + self.assertFloatsAreIdentical(z1.imag, 0.0) + self.assertFloatsAreIdentical(z2.imag, -0.0) @support.requires_IEEE_754 def test_negated_imaginary_literal(self): @@ -702,8 +720,7 @@ def test_repr_roundtrip(self): for y in vals: z = complex(x, y) roundtrip = complex(repr(z)) - self.assertFloatsAreIdentical(z.real, roundtrip.real) - self.assertFloatsAreIdentical(z.imag, roundtrip.imag) + self.assertComplexesAreIdentical(z, roundtrip) # if we predefine some constants, then eval(repr(z)) should # also work, except that it might change the sign of zeros From 04721edd45422e4db3c1f85b915ff25337526a4c Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 13 Jul 2025 17:53:53 +0300 Subject: [PATCH 2/4] Update `test_float.py` from 3.13.5 --- Lib/test/test_float.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index d94a2bdadd8..2ccad19e03a 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -9,8 +9,10 @@ from test import support from test.support.testcase import FloatsAreIdenticalMixin -from test.test_grammar import (VALID_UNDERSCORE_LITERALS, - INVALID_UNDERSCORE_LITERALS) +from test.support.numbers import ( + VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS, +) from math import isinf, isnan, copysign, ldexp import math @@ -1513,4 +1515,4 @@ def __init__(self, value): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() From bace651ce69090a11d43b727db8d94577ea371a6 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 13 Jul 2025 18:32:25 +0300 Subject: [PATCH 3/4] Update `test_int.py` from CPython 3.13.5 --- Lib/test/support/__init__.py | 41 ++++ Lib/test/test_int.py | 391 +++++++++++++++++++++++++++++++++-- 2 files changed, 420 insertions(+), 12 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 948bad1ca80..ebc6bf529e2 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2579,6 +2579,47 @@ def sleeping_retry(timeout, err_msg=None, /, delay = min(delay * 2, max_delay) +# From CPython 3.13.5 +class CPUStopwatch: + """Context manager to roughly time a CPU-bound operation. + + Disables GC. Uses CPU time if it can (i.e. excludes sleeps & time of + other processes). + + N.B.: + - This *includes* time spent in other threads. + - Some systems only have a coarse resolution; check + stopwatch.clock_info.rseolution if. + + Usage: + + with ProcessStopwatch() as stopwatch: + ... + elapsed = stopwatch.seconds + resolution = stopwatch.clock_info.resolution + """ + def __enter__(self): + get_time = time.process_time + clock_info = time.get_clock_info('process_time') + if get_time() <= 0: # some platforms like WASM lack process_time() + get_time = time.monotonic + clock_info = time.get_clock_info('monotonic') + self.context = disable_gc() + self.context.__enter__() + self.get_time = get_time + self.clock_info = clock_info + self.start_time = get_time() + return self + + def __exit__(self, *exc): + try: + end_time = self.get_time() + finally: + result = self.context.__exit__(*exc) + self.seconds = end_time - self.start_time + return result + + @contextlib.contextmanager def adjust_int_max_str_digits(max_digits): """Temporarily change the integer string conversion length limit.""" diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 7ac83288f4c..464c0b69afe 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -1,9 +1,18 @@ import sys +import time import unittest +from unittest import mock from test import support -from test.test_grammar import (VALID_UNDERSCORE_LITERALS, - INVALID_UNDERSCORE_LITERALS) +from test.support.numbers import ( + VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS, +) + +try: + import _pylong +except ImportError: + _pylong = None L = [ ('0', 0), @@ -83,6 +92,7 @@ def test_basic(self): self.assertRaises(TypeError, int, 1, 12) + self.assertRaises(TypeError, int, "10", 2, 1) self.assertEqual(int('0o123', 0), 83) self.assertEqual(int('0x123', 16), 291) @@ -148,6 +158,8 @@ def test_basic(self): self.assertEqual(int(' 0O123 ', 0), 83) self.assertEqual(int(' 0X123 ', 0), 291) self.assertEqual(int(' 0B100 ', 0), 4) + with self.assertRaises(ValueError): + int('010', 0) # without base still base 10 self.assertEqual(int('0123'), 123) @@ -214,6 +226,26 @@ def test_basic(self): self.assertEqual(int('2br45qc', 35), 4294967297) self.assertEqual(int('1z141z5', 36), 4294967297) + def test_invalid_signs(self): + with self.assertRaises(ValueError): + int('+') + with self.assertRaises(ValueError): + int('-') + with self.assertRaises(ValueError): + int('- 1') + with self.assertRaises(ValueError): + int('+ 1') + with self.assertRaises(ValueError): + int(' + 1 ') + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_unicode(self): + self.assertEqual(int("१२३४५६७८९०1234567890"), 12345678901234567890) + self.assertEqual(int('١٢٣٤٥٦٧٨٩٠'), 1234567890) + self.assertEqual(int("१२३४५६७८९०1234567890", 0), 12345678901234567890) + self.assertEqual(int('١٢٣٤٥٦٧٨٩٠', 0), 1234567890) + def test_underscores(self): for lit in VALID_UNDERSCORE_LITERALS: if any(ch in lit for ch in '.eEjJ'): @@ -233,7 +265,7 @@ def test_underscores(self): self.assertRaises(ValueError, int, "1__00") self.assertRaises(ValueError, int, "100_") - # @support.cpython_only + @support.cpython_only def test_small_ints(self): # Bug #3236: Return small longs from PyLong_FromString self.assertIs(int('10'), 10) @@ -369,12 +401,14 @@ def __trunc__(self): class JustTrunc(base): def __trunc__(self): return 42 - self.assertEqual(int(JustTrunc()), 42) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(JustTrunc()), 42) class ExceptionalTrunc(base): def __trunc__(self): 1 / 0 - with self.assertRaises(ZeroDivisionError): + with self.assertRaises(ZeroDivisionError), \ + self.assertWarns(DeprecationWarning): int(ExceptionalTrunc()) for trunc_result_base in (object, Classic): @@ -385,7 +419,8 @@ def __index__(self): class TruncReturnsNonInt(base): def __trunc__(self): return Index() - self.assertEqual(int(TruncReturnsNonInt()), 42) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(TruncReturnsNonInt()), 42) class Intable(trunc_result_base): def __int__(self): @@ -394,7 +429,8 @@ def __int__(self): class TruncReturnsNonIndex(base): def __trunc__(self): return Intable() - self.assertEqual(int(TruncReturnsNonInt()), 42) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(TruncReturnsNonInt()), 42) class NonIntegral(trunc_result_base): def __trunc__(self): @@ -405,7 +441,8 @@ class TruncReturnsNonIntegral(base): def __trunc__(self): return NonIntegral() try: - int(TruncReturnsNonIntegral()) + with self.assertWarns(DeprecationWarning): + int(TruncReturnsNonIntegral()) except TypeError as e: self.assertEqual(str(e), "__trunc__ returned non-Integral" @@ -423,7 +460,8 @@ class TruncReturnsBadInt(base): def __trunc__(self): return BadInt() - with self.assertRaises(TypeError): + with self.assertRaises(TypeError), \ + self.assertWarns(DeprecationWarning): int(TruncReturnsBadInt()) def test_int_subclass_with_index(self): @@ -517,13 +555,16 @@ def __trunc__(self): self.assertIs(type(n), int) bad_int = TruncReturnsBadInt() - self.assertRaises(TypeError, int, bad_int) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, int, bad_int) good_int = TruncReturnsIntSubclass() - n = int(good_int) + with self.assertWarns(DeprecationWarning): + n = int(good_int) self.assertEqual(n, 1) self.assertIs(type(n), int) - n = IntSubclass(good_int) + with self.assertWarns(DeprecationWarning): + n = IntSubclass(good_int) self.assertEqual(n, 1) self.assertIs(type(n), IntSubclass) @@ -568,5 +609,331 @@ def test_issue31619(self): self.assertEqual(int('1_2_3_4_5_6_7', 32), 1144132807) +class IntStrDigitLimitsTests(unittest.TestCase): + + int_class = int # Override this in subclasses to reuse the suite. + + def setUp(self): + super().setUp() + self._previous_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(2048) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_limit) + super().tearDown() + + def test_disabled_limit(self): + self.assertGreater(sys.get_int_max_str_digits(), 0) + self.assertLess(sys.get_int_max_str_digits(), 20_000) + with support.adjust_int_max_str_digits(0): + self.assertEqual(sys.get_int_max_str_digits(), 0) + i = self.int_class('1' * 20_000) + str(i) + self.assertGreater(sys.get_int_max_str_digits(), 0) + + def test_max_str_digits_edge_cases(self): + """Ignore the +/- sign and space padding.""" + int_class = self.int_class + maxdigits = sys.get_int_max_str_digits() + + int_class('1' * maxdigits) + int_class(' ' + '1' * maxdigits) + int_class('1' * maxdigits + ' ') + int_class('+' + '1' * maxdigits) + int_class('-' + '1' * maxdigits) + self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits) + + def check(self, i, base=None): + with self.assertRaises(ValueError): + if base is None: + self.int_class(i) + else: + self.int_class(i, base) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_max_str_digits(self): + maxdigits = sys.get_int_max_str_digits() + + self.check('1' * (maxdigits + 1)) + self.check(' ' + '1' * (maxdigits + 1)) + self.check('1' * (maxdigits + 1) + ' ') + self.check('+' + '1' * (maxdigits + 1)) + self.check('-' + '1' * (maxdigits + 1)) + self.check('1' * (maxdigits + 1)) + + i = 10 ** maxdigits + with self.assertRaises(ValueError): + str(i) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_denial_of_service_prevented_int_to_str(self): + """Regression test: ensure we fail before performing O(N**2) work.""" + maxdigits = sys.get_int_max_str_digits() + assert maxdigits < 50_000, maxdigits # A test prerequisite. + + huge_int = int(f'0x{"c"*65_000}', base=16) # 78268 decimal digits. + digits = 78_268 + with ( + support.adjust_int_max_str_digits(digits), + support.CPUStopwatch() as sw_convert): + huge_decimal = str(huge_int) + self.assertEqual(len(huge_decimal), digits) + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. + # Some OSes have a low res 1/64s timer, skip if hard to measure. + if sw_convert.seconds < sw_convert.clock_info.resolution * 2: + raise unittest.SkipTest('"slow" conversion took only ' + f'{sw_convert.seconds} seconds.') + + # We test with the limit almost at the size needed to check performance. + # The performant limit check is slightly fuzzy, give it a some room. + with support.adjust_int_max_str_digits(int(.995 * digits)): + with ( + self.assertRaises(ValueError) as err, + support.CPUStopwatch() as sw_fail_huge): + str(huge_int) + self.assertIn('conversion', str(err.exception)) + self.assertLessEqual(sw_fail_huge.seconds, sw_convert.seconds/2) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. + extra_huge_int = int(f'0x{"c"*500_000}', base=16) # 602060 digits. + with ( + self.assertRaises(ValueError) as err, + support.CPUStopwatch() as sw_fail_extra_huge): + # If not limited, 8 seconds said Zen based cloud VM. + str(extra_huge_int) + self.assertIn('conversion', str(err.exception)) + self.assertLess(sw_fail_extra_huge.seconds, sw_convert.seconds/2) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_denial_of_service_prevented_str_to_int(self): + """Regression test: ensure we fail before performing O(N**2) work.""" + maxdigits = sys.get_int_max_str_digits() + assert maxdigits < 100_000, maxdigits # A test prerequisite. + + digits = 133700 + huge = '8'*digits + with ( + support.adjust_int_max_str_digits(digits), + support.CPUStopwatch() as sw_convert): + int(huge) + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. + # Some OSes have a low res 1/64s timer, skip if hard to measure. + if sw_convert.seconds < sw_convert.clock_info.resolution * 2: + raise unittest.SkipTest('"slow" conversion took only ' + f'{sw_convert.seconds} seconds.') + + with support.adjust_int_max_str_digits(digits - 1): + with ( + self.assertRaises(ValueError) as err, + support.CPUStopwatch() as sw_fail_huge): + int(huge) + self.assertIn('conversion', str(err.exception)) + self.assertLessEqual(sw_fail_huge.seconds, sw_convert.seconds/2) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. + extra_huge = '7'*1_200_000 + with ( + self.assertRaises(ValueError) as err, + support.CPUStopwatch() as sw_fail_extra_huge): + # If not limited, 8 seconds in the Zen based cloud VM. + int(extra_huge) + self.assertIn('conversion', str(err.exception)) + self.assertLessEqual(sw_fail_extra_huge.seconds, sw_convert.seconds/2) + + def test_power_of_two_bases_unlimited(self): + """The limit does not apply to power of 2 bases.""" + maxdigits = sys.get_int_max_str_digits() + + for base in (2, 4, 8, 16, 32): + with self.subTest(base=base): + self.int_class('1' * (maxdigits + 1), base) + assert maxdigits < 100_000 + self.int_class('1' * 100_000, base) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_underscores_ignored(self): + maxdigits = sys.get_int_max_str_digits() + + triples = maxdigits // 3 + s = '111' * triples + s_ = '1_11' * triples + self.int_class(s) # succeeds + self.int_class(s_) # succeeds + self.check(f'{s}111') + self.check(f'{s_}_111') + + def test_sign_not_counted(self): + int_class = self.int_class + max_digits = sys.get_int_max_str_digits() + s = '5' * max_digits + i = int_class(s) + pos_i = int_class(f'+{s}') + assert i == pos_i + neg_i = int_class(f'-{s}') + assert -pos_i == neg_i + str(pos_i) + str(neg_i) + + def _other_base_helper(self, base): + int_class = self.int_class + max_digits = sys.get_int_max_str_digits() + s = '2' * max_digits + i = int_class(s, base) + if base > 10: + with self.assertRaises(ValueError): + str(i) + elif base < 10: + str(i) + with self.assertRaises(ValueError) as err: + int_class(f'{s}1', base) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_int_from_other_bases(self): + base = 3 + with self.subTest(base=base): + self._other_base_helper(base) + base = 36 + with self.subTest(base=base): + self._other_base_helper(base) + + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_int_max_str_digits_is_per_interpreter(self): + # Changing the limit in one interpreter does not change others. + code = """if 1: + # Subinterpreters maintain and enforce their own limit + import sys + sys.set_int_max_str_digits(2323) + try: + int('3'*3333) + except ValueError: + pass + else: + raise AssertionError('Expected a int max str digits ValueError.') + """ + with support.adjust_int_max_str_digits(4000): + before_value = sys.get_int_max_str_digits() + self.assertEqual(support.run_in_subinterp(code), 0, + 'subinterp code failure, check stderr.') + after_value = sys.get_int_max_str_digits() + self.assertEqual(before_value, after_value) + + +class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests): + int_class = IntSubclass + + +class PyLongModuleTests(unittest.TestCase): + # Tests of the functions in _pylong.py. Those get used when the + # number of digits in the input values are large enough. + + def setUp(self): + super().setUp() + self._previous_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(0) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_limit) + super().tearDown() + + def _test_pylong_int_to_decimal(self, n, suffix): + s = str(n) + self.assertEqual(s[-10:], suffix) + s2 = str(-n) + self.assertEqual(s2, '-' + s) + s3 = '%d' % n + self.assertEqual(s3, s) + s4 = b'%d' % n + self.assertEqual(s4, s.encode('ascii')) + + def test_pylong_int_to_decimal(self): + self._test_pylong_int_to_decimal((1 << 100_000), '9883109376') + self._test_pylong_int_to_decimal((1 << 100_000) - 1, '9883109375') + self._test_pylong_int_to_decimal(10**30_000, '0000000000') + self._test_pylong_int_to_decimal(10**30_000 - 1, '9999999999') + self._test_pylong_int_to_decimal(3**60_000, '9313200001') + + @support.requires_resource('cpu') + def test_pylong_int_to_decimal_2(self): + self._test_pylong_int_to_decimal(2**1_000_000, '2747109376') + self._test_pylong_int_to_decimal(10**300_000, '0000000000') + self._test_pylong_int_to_decimal(3**600_000, '3132000001') + + def test_pylong_int_divmod(self): + n = (1 << 100_000) + a, b = divmod(n*3 + 1, n) + assert a == 3 and b == 1 + + def test_pylong_str_to_int(self): + v1 = 1 << 100_000 + s = str(v1) + v2 = int(s) + assert v1 == v2 + v3 = int(' -' + s) + assert -v1 == v3 + v4 = int(' +' + s + ' ') + assert v1 == v4 + with self.assertRaises(ValueError) as err: + int(s + 'z') + with self.assertRaises(ValueError) as err: + int(s + '_') + with self.assertRaises(ValueError) as err: + int('_' + s) + + @support.cpython_only # tests implementation details of CPython. + @unittest.skipUnless(_pylong, "_pylong module required") + @mock.patch.object(_pylong, "int_to_decimal_string") + def test_pylong_misbehavior_error_path_to_str( + self, mock_int_to_str): + with support.adjust_int_max_str_digits(20_000): + big_value = int('7'*19_999) + mock_int_to_str.return_value = None # not a str + with self.assertRaises(TypeError) as ctx: + str(big_value) + self.assertIn('_pylong.int_to_decimal_string did not', + str(ctx.exception)) + mock_int_to_str.side_effect = RuntimeError("testABC") + with self.assertRaises(RuntimeError): + str(big_value) + + @support.cpython_only # tests implementation details of CPython. + @unittest.skipUnless(_pylong, "_pylong module required") + @mock.patch.object(_pylong, "int_from_string") + def test_pylong_misbehavior_error_path_from_str( + self, mock_int_from_str): + big_value = '7'*19_999 + with support.adjust_int_max_str_digits(20_000): + mock_int_from_str.return_value = b'not an int' + with self.assertRaises(TypeError) as ctx: + int(big_value) + self.assertIn('_pylong.int_from_string did not', + str(ctx.exception)) + + mock_int_from_str.side_effect = RuntimeError("test123") + with self.assertRaises(RuntimeError): + int(big_value) + + def test_pylong_roundtrip(self): + from random import randrange, getrandbits + bits = 5000 + while bits <= 1_000_000: + bits += randrange(-100, 101) # break bitlength patterns + hibit = 1 << (bits - 1) + n = hibit | getrandbits(bits - 1) + assert n.bit_length() == bits + sn = str(n) + self.assertFalse(sn.startswith('0')) + self.assertEqual(n, int(sn)) + bits <<= 1 + if __name__ == "__main__": unittest.main() From 2b061f4e7d1cf0b9b07131f01b4b49fc74790bea Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 13 Jul 2025 18:36:24 +0300 Subject: [PATCH 4/4] Revert "Update `test_int.py` from CPython 3.13.5" This reverts commit bace651ce69090a11d43b727db8d94577ea371a6. --- Lib/test/support/__init__.py | 41 ---- Lib/test/test_int.py | 391 ++--------------------------------- 2 files changed, 12 insertions(+), 420 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index ebc6bf529e2..948bad1ca80 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2579,47 +2579,6 @@ def sleeping_retry(timeout, err_msg=None, /, delay = min(delay * 2, max_delay) -# From CPython 3.13.5 -class CPUStopwatch: - """Context manager to roughly time a CPU-bound operation. - - Disables GC. Uses CPU time if it can (i.e. excludes sleeps & time of - other processes). - - N.B.: - - This *includes* time spent in other threads. - - Some systems only have a coarse resolution; check - stopwatch.clock_info.rseolution if. - - Usage: - - with ProcessStopwatch() as stopwatch: - ... - elapsed = stopwatch.seconds - resolution = stopwatch.clock_info.resolution - """ - def __enter__(self): - get_time = time.process_time - clock_info = time.get_clock_info('process_time') - if get_time() <= 0: # some platforms like WASM lack process_time() - get_time = time.monotonic - clock_info = time.get_clock_info('monotonic') - self.context = disable_gc() - self.context.__enter__() - self.get_time = get_time - self.clock_info = clock_info - self.start_time = get_time() - return self - - def __exit__(self, *exc): - try: - end_time = self.get_time() - finally: - result = self.context.__exit__(*exc) - self.seconds = end_time - self.start_time - return result - - @contextlib.contextmanager def adjust_int_max_str_digits(max_digits): """Temporarily change the integer string conversion length limit.""" diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 464c0b69afe..7ac83288f4c 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -1,18 +1,9 @@ import sys -import time import unittest -from unittest import mock from test import support -from test.support.numbers import ( - VALID_UNDERSCORE_LITERALS, - INVALID_UNDERSCORE_LITERALS, -) - -try: - import _pylong -except ImportError: - _pylong = None +from test.test_grammar import (VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS) L = [ ('0', 0), @@ -92,7 +83,6 @@ def test_basic(self): self.assertRaises(TypeError, int, 1, 12) - self.assertRaises(TypeError, int, "10", 2, 1) self.assertEqual(int('0o123', 0), 83) self.assertEqual(int('0x123', 16), 291) @@ -158,8 +148,6 @@ def test_basic(self): self.assertEqual(int(' 0O123 ', 0), 83) self.assertEqual(int(' 0X123 ', 0), 291) self.assertEqual(int(' 0B100 ', 0), 4) - with self.assertRaises(ValueError): - int('010', 0) # without base still base 10 self.assertEqual(int('0123'), 123) @@ -226,26 +214,6 @@ def test_basic(self): self.assertEqual(int('2br45qc', 35), 4294967297) self.assertEqual(int('1z141z5', 36), 4294967297) - def test_invalid_signs(self): - with self.assertRaises(ValueError): - int('+') - with self.assertRaises(ValueError): - int('-') - with self.assertRaises(ValueError): - int('- 1') - with self.assertRaises(ValueError): - int('+ 1') - with self.assertRaises(ValueError): - int(' + 1 ') - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_unicode(self): - self.assertEqual(int("१२३४५६७८९०1234567890"), 12345678901234567890) - self.assertEqual(int('١٢٣٤٥٦٧٨٩٠'), 1234567890) - self.assertEqual(int("१२३४५६७८९०1234567890", 0), 12345678901234567890) - self.assertEqual(int('١٢٣٤٥٦٧٨٩٠', 0), 1234567890) - def test_underscores(self): for lit in VALID_UNDERSCORE_LITERALS: if any(ch in lit for ch in '.eEjJ'): @@ -265,7 +233,7 @@ def test_underscores(self): self.assertRaises(ValueError, int, "1__00") self.assertRaises(ValueError, int, "100_") - @support.cpython_only + # @support.cpython_only def test_small_ints(self): # Bug #3236: Return small longs from PyLong_FromString self.assertIs(int('10'), 10) @@ -401,14 +369,12 @@ def __trunc__(self): class JustTrunc(base): def __trunc__(self): return 42 - with self.assertWarns(DeprecationWarning): - self.assertEqual(int(JustTrunc()), 42) + self.assertEqual(int(JustTrunc()), 42) class ExceptionalTrunc(base): def __trunc__(self): 1 / 0 - with self.assertRaises(ZeroDivisionError), \ - self.assertWarns(DeprecationWarning): + with self.assertRaises(ZeroDivisionError): int(ExceptionalTrunc()) for trunc_result_base in (object, Classic): @@ -419,8 +385,7 @@ def __index__(self): class TruncReturnsNonInt(base): def __trunc__(self): return Index() - with self.assertWarns(DeprecationWarning): - self.assertEqual(int(TruncReturnsNonInt()), 42) + self.assertEqual(int(TruncReturnsNonInt()), 42) class Intable(trunc_result_base): def __int__(self): @@ -429,8 +394,7 @@ def __int__(self): class TruncReturnsNonIndex(base): def __trunc__(self): return Intable() - with self.assertWarns(DeprecationWarning): - self.assertEqual(int(TruncReturnsNonInt()), 42) + self.assertEqual(int(TruncReturnsNonInt()), 42) class NonIntegral(trunc_result_base): def __trunc__(self): @@ -441,8 +405,7 @@ class TruncReturnsNonIntegral(base): def __trunc__(self): return NonIntegral() try: - with self.assertWarns(DeprecationWarning): - int(TruncReturnsNonIntegral()) + int(TruncReturnsNonIntegral()) except TypeError as e: self.assertEqual(str(e), "__trunc__ returned non-Integral" @@ -460,8 +423,7 @@ class TruncReturnsBadInt(base): def __trunc__(self): return BadInt() - with self.assertRaises(TypeError), \ - self.assertWarns(DeprecationWarning): + with self.assertRaises(TypeError): int(TruncReturnsBadInt()) def test_int_subclass_with_index(self): @@ -555,16 +517,13 @@ def __trunc__(self): self.assertIs(type(n), int) bad_int = TruncReturnsBadInt() - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, int, bad_int) + self.assertRaises(TypeError, int, bad_int) good_int = TruncReturnsIntSubclass() - with self.assertWarns(DeprecationWarning): - n = int(good_int) + n = int(good_int) self.assertEqual(n, 1) self.assertIs(type(n), int) - with self.assertWarns(DeprecationWarning): - n = IntSubclass(good_int) + n = IntSubclass(good_int) self.assertEqual(n, 1) self.assertIs(type(n), IntSubclass) @@ -609,331 +568,5 @@ def test_issue31619(self): self.assertEqual(int('1_2_3_4_5_6_7', 32), 1144132807) -class IntStrDigitLimitsTests(unittest.TestCase): - - int_class = int # Override this in subclasses to reuse the suite. - - def setUp(self): - super().setUp() - self._previous_limit = sys.get_int_max_str_digits() - sys.set_int_max_str_digits(2048) - - def tearDown(self): - sys.set_int_max_str_digits(self._previous_limit) - super().tearDown() - - def test_disabled_limit(self): - self.assertGreater(sys.get_int_max_str_digits(), 0) - self.assertLess(sys.get_int_max_str_digits(), 20_000) - with support.adjust_int_max_str_digits(0): - self.assertEqual(sys.get_int_max_str_digits(), 0) - i = self.int_class('1' * 20_000) - str(i) - self.assertGreater(sys.get_int_max_str_digits(), 0) - - def test_max_str_digits_edge_cases(self): - """Ignore the +/- sign and space padding.""" - int_class = self.int_class - maxdigits = sys.get_int_max_str_digits() - - int_class('1' * maxdigits) - int_class(' ' + '1' * maxdigits) - int_class('1' * maxdigits + ' ') - int_class('+' + '1' * maxdigits) - int_class('-' + '1' * maxdigits) - self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits) - - def check(self, i, base=None): - with self.assertRaises(ValueError): - if base is None: - self.int_class(i) - else: - self.int_class(i, base) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_max_str_digits(self): - maxdigits = sys.get_int_max_str_digits() - - self.check('1' * (maxdigits + 1)) - self.check(' ' + '1' * (maxdigits + 1)) - self.check('1' * (maxdigits + 1) + ' ') - self.check('+' + '1' * (maxdigits + 1)) - self.check('-' + '1' * (maxdigits + 1)) - self.check('1' * (maxdigits + 1)) - - i = 10 ** maxdigits - with self.assertRaises(ValueError): - str(i) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_denial_of_service_prevented_int_to_str(self): - """Regression test: ensure we fail before performing O(N**2) work.""" - maxdigits = sys.get_int_max_str_digits() - assert maxdigits < 50_000, maxdigits # A test prerequisite. - - huge_int = int(f'0x{"c"*65_000}', base=16) # 78268 decimal digits. - digits = 78_268 - with ( - support.adjust_int_max_str_digits(digits), - support.CPUStopwatch() as sw_convert): - huge_decimal = str(huge_int) - self.assertEqual(len(huge_decimal), digits) - # Ensuring that we chose a slow enough conversion to measure. - # It takes 0.1 seconds on a Zen based cloud VM in an opt build. - # Some OSes have a low res 1/64s timer, skip if hard to measure. - if sw_convert.seconds < sw_convert.clock_info.resolution * 2: - raise unittest.SkipTest('"slow" conversion took only ' - f'{sw_convert.seconds} seconds.') - - # We test with the limit almost at the size needed to check performance. - # The performant limit check is slightly fuzzy, give it a some room. - with support.adjust_int_max_str_digits(int(.995 * digits)): - with ( - self.assertRaises(ValueError) as err, - support.CPUStopwatch() as sw_fail_huge): - str(huge_int) - self.assertIn('conversion', str(err.exception)) - self.assertLessEqual(sw_fail_huge.seconds, sw_convert.seconds/2) - - # Now we test that a conversion that would take 30x as long also fails - # in a similarly fast fashion. - extra_huge_int = int(f'0x{"c"*500_000}', base=16) # 602060 digits. - with ( - self.assertRaises(ValueError) as err, - support.CPUStopwatch() as sw_fail_extra_huge): - # If not limited, 8 seconds said Zen based cloud VM. - str(extra_huge_int) - self.assertIn('conversion', str(err.exception)) - self.assertLess(sw_fail_extra_huge.seconds, sw_convert.seconds/2) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_denial_of_service_prevented_str_to_int(self): - """Regression test: ensure we fail before performing O(N**2) work.""" - maxdigits = sys.get_int_max_str_digits() - assert maxdigits < 100_000, maxdigits # A test prerequisite. - - digits = 133700 - huge = '8'*digits - with ( - support.adjust_int_max_str_digits(digits), - support.CPUStopwatch() as sw_convert): - int(huge) - # Ensuring that we chose a slow enough conversion to measure. - # It takes 0.1 seconds on a Zen based cloud VM in an opt build. - # Some OSes have a low res 1/64s timer, skip if hard to measure. - if sw_convert.seconds < sw_convert.clock_info.resolution * 2: - raise unittest.SkipTest('"slow" conversion took only ' - f'{sw_convert.seconds} seconds.') - - with support.adjust_int_max_str_digits(digits - 1): - with ( - self.assertRaises(ValueError) as err, - support.CPUStopwatch() as sw_fail_huge): - int(huge) - self.assertIn('conversion', str(err.exception)) - self.assertLessEqual(sw_fail_huge.seconds, sw_convert.seconds/2) - - # Now we test that a conversion that would take 30x as long also fails - # in a similarly fast fashion. - extra_huge = '7'*1_200_000 - with ( - self.assertRaises(ValueError) as err, - support.CPUStopwatch() as sw_fail_extra_huge): - # If not limited, 8 seconds in the Zen based cloud VM. - int(extra_huge) - self.assertIn('conversion', str(err.exception)) - self.assertLessEqual(sw_fail_extra_huge.seconds, sw_convert.seconds/2) - - def test_power_of_two_bases_unlimited(self): - """The limit does not apply to power of 2 bases.""" - maxdigits = sys.get_int_max_str_digits() - - for base in (2, 4, 8, 16, 32): - with self.subTest(base=base): - self.int_class('1' * (maxdigits + 1), base) - assert maxdigits < 100_000 - self.int_class('1' * 100_000, base) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_underscores_ignored(self): - maxdigits = sys.get_int_max_str_digits() - - triples = maxdigits // 3 - s = '111' * triples - s_ = '1_11' * triples - self.int_class(s) # succeeds - self.int_class(s_) # succeeds - self.check(f'{s}111') - self.check(f'{s_}_111') - - def test_sign_not_counted(self): - int_class = self.int_class - max_digits = sys.get_int_max_str_digits() - s = '5' * max_digits - i = int_class(s) - pos_i = int_class(f'+{s}') - assert i == pos_i - neg_i = int_class(f'-{s}') - assert -pos_i == neg_i - str(pos_i) - str(neg_i) - - def _other_base_helper(self, base): - int_class = self.int_class - max_digits = sys.get_int_max_str_digits() - s = '2' * max_digits - i = int_class(s, base) - if base > 10: - with self.assertRaises(ValueError): - str(i) - elif base < 10: - str(i) - with self.assertRaises(ValueError) as err: - int_class(f'{s}1', base) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_int_from_other_bases(self): - base = 3 - with self.subTest(base=base): - self._other_base_helper(base) - base = 36 - with self.subTest(base=base): - self._other_base_helper(base) - - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_int_max_str_digits_is_per_interpreter(self): - # Changing the limit in one interpreter does not change others. - code = """if 1: - # Subinterpreters maintain and enforce their own limit - import sys - sys.set_int_max_str_digits(2323) - try: - int('3'*3333) - except ValueError: - pass - else: - raise AssertionError('Expected a int max str digits ValueError.') - """ - with support.adjust_int_max_str_digits(4000): - before_value = sys.get_int_max_str_digits() - self.assertEqual(support.run_in_subinterp(code), 0, - 'subinterp code failure, check stderr.') - after_value = sys.get_int_max_str_digits() - self.assertEqual(before_value, after_value) - - -class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests): - int_class = IntSubclass - - -class PyLongModuleTests(unittest.TestCase): - # Tests of the functions in _pylong.py. Those get used when the - # number of digits in the input values are large enough. - - def setUp(self): - super().setUp() - self._previous_limit = sys.get_int_max_str_digits() - sys.set_int_max_str_digits(0) - - def tearDown(self): - sys.set_int_max_str_digits(self._previous_limit) - super().tearDown() - - def _test_pylong_int_to_decimal(self, n, suffix): - s = str(n) - self.assertEqual(s[-10:], suffix) - s2 = str(-n) - self.assertEqual(s2, '-' + s) - s3 = '%d' % n - self.assertEqual(s3, s) - s4 = b'%d' % n - self.assertEqual(s4, s.encode('ascii')) - - def test_pylong_int_to_decimal(self): - self._test_pylong_int_to_decimal((1 << 100_000), '9883109376') - self._test_pylong_int_to_decimal((1 << 100_000) - 1, '9883109375') - self._test_pylong_int_to_decimal(10**30_000, '0000000000') - self._test_pylong_int_to_decimal(10**30_000 - 1, '9999999999') - self._test_pylong_int_to_decimal(3**60_000, '9313200001') - - @support.requires_resource('cpu') - def test_pylong_int_to_decimal_2(self): - self._test_pylong_int_to_decimal(2**1_000_000, '2747109376') - self._test_pylong_int_to_decimal(10**300_000, '0000000000') - self._test_pylong_int_to_decimal(3**600_000, '3132000001') - - def test_pylong_int_divmod(self): - n = (1 << 100_000) - a, b = divmod(n*3 + 1, n) - assert a == 3 and b == 1 - - def test_pylong_str_to_int(self): - v1 = 1 << 100_000 - s = str(v1) - v2 = int(s) - assert v1 == v2 - v3 = int(' -' + s) - assert -v1 == v3 - v4 = int(' +' + s + ' ') - assert v1 == v4 - with self.assertRaises(ValueError) as err: - int(s + 'z') - with self.assertRaises(ValueError) as err: - int(s + '_') - with self.assertRaises(ValueError) as err: - int('_' + s) - - @support.cpython_only # tests implementation details of CPython. - @unittest.skipUnless(_pylong, "_pylong module required") - @mock.patch.object(_pylong, "int_to_decimal_string") - def test_pylong_misbehavior_error_path_to_str( - self, mock_int_to_str): - with support.adjust_int_max_str_digits(20_000): - big_value = int('7'*19_999) - mock_int_to_str.return_value = None # not a str - with self.assertRaises(TypeError) as ctx: - str(big_value) - self.assertIn('_pylong.int_to_decimal_string did not', - str(ctx.exception)) - mock_int_to_str.side_effect = RuntimeError("testABC") - with self.assertRaises(RuntimeError): - str(big_value) - - @support.cpython_only # tests implementation details of CPython. - @unittest.skipUnless(_pylong, "_pylong module required") - @mock.patch.object(_pylong, "int_from_string") - def test_pylong_misbehavior_error_path_from_str( - self, mock_int_from_str): - big_value = '7'*19_999 - with support.adjust_int_max_str_digits(20_000): - mock_int_from_str.return_value = b'not an int' - with self.assertRaises(TypeError) as ctx: - int(big_value) - self.assertIn('_pylong.int_from_string did not', - str(ctx.exception)) - - mock_int_from_str.side_effect = RuntimeError("test123") - with self.assertRaises(RuntimeError): - int(big_value) - - def test_pylong_roundtrip(self): - from random import randrange, getrandbits - bits = 5000 - while bits <= 1_000_000: - bits += randrange(-100, 101) # break bitlength patterns - hibit = 1 << (bits - 1) - n = hibit | getrandbits(bits - 1) - assert n.bit_length() == bits - sn = str(n) - self.assertFalse(sn.startswith('0')) - self.assertEqual(n, int(sn)) - bits <<= 1 - if __name__ == "__main__": unittest.main()