gh-84978: Add float.from_number() and complex.from_number() (GH-26827)

They are alternate constructors which only accept numbers
(including objects with special methods __float__, __complex__
and __index__), but not strings.
This commit is contained in:
Serhiy Storchaka 2024-07-15 19:07:00 +03:00 committed by GitHub
parent 8303d32ff5
commit 94bee45dee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 242 additions and 46 deletions

View file

@ -32,6 +32,28 @@ class FloatSubclass(float):
class OtherFloatSubclass(float):
pass
class MyIndex:
def __init__(self, value):
self.value = value
def __index__(self):
return self.value
class MyInt:
def __init__(self, value):
self.value = value
def __int__(self):
return self.value
class FloatLike:
def __init__(self, value):
self.value = value
def __float__(self):
return self.value
class GeneralFloatCases(unittest.TestCase):
def test_float(self):
@ -181,10 +203,6 @@ class GeneralFloatCases(unittest.TestCase):
def test_floatconversion(self):
# Make sure that calls to __float__() work properly
class Foo1(object):
def __float__(self):
return 42.
class Foo2(float):
def __float__(self):
return 42.
@ -206,45 +224,29 @@ class GeneralFloatCases(unittest.TestCase):
def __float__(self):
return float(str(self)) + 1
self.assertEqual(float(Foo1()), 42.)
self.assertEqual(float(FloatLike(42.)), 42.)
self.assertEqual(float(Foo2()), 42.)
with self.assertWarns(DeprecationWarning):
self.assertEqual(float(Foo3(21)), 42.)
self.assertRaises(TypeError, float, Foo4(42))
self.assertEqual(float(FooStr('8')), 9.)
class Foo5:
def __float__(self):
return ""
self.assertRaises(TypeError, time.sleep, Foo5())
self.assertRaises(TypeError, time.sleep, FloatLike(""))
# Issue #24731
class F:
def __float__(self):
return OtherFloatSubclass(42.)
f = FloatLike(OtherFloatSubclass(42.))
with self.assertWarns(DeprecationWarning):
self.assertEqual(float(F()), 42.)
self.assertEqual(float(f), 42.)
with self.assertWarns(DeprecationWarning):
self.assertIs(type(float(F())), float)
self.assertIs(type(float(f)), float)
with self.assertWarns(DeprecationWarning):
self.assertEqual(FloatSubclass(F()), 42.)
self.assertEqual(FloatSubclass(f), 42.)
with self.assertWarns(DeprecationWarning):
self.assertIs(type(FloatSubclass(F())), FloatSubclass)
class MyIndex:
def __init__(self, value):
self.value = value
def __index__(self):
return self.value
self.assertIs(type(FloatSubclass(f)), FloatSubclass)
self.assertEqual(float(MyIndex(42)), 42.0)
self.assertRaises(OverflowError, float, MyIndex(2**2000))
class MyInt:
def __int__(self):
return 42
self.assertRaises(TypeError, float, MyInt())
self.assertRaises(TypeError, float, MyInt(42))
def test_keyword_args(self):
with self.assertRaisesRegex(TypeError, 'keyword argument'):
@ -277,6 +279,37 @@ class GeneralFloatCases(unittest.TestCase):
self.assertEqual(float(u), 2.5)
self.assertEqual(u.newarg, 3)
def assertEqualAndType(self, actual, expected_value, expected_type):
self.assertEqual(actual, expected_value)
self.assertIs(type(actual), expected_type)
def test_from_number(self, cls=float):
def eq(actual, expected):
self.assertEqual(actual, expected)
self.assertIs(type(actual), cls)
eq(cls.from_number(3.14), 3.14)
eq(cls.from_number(314), 314.0)
eq(cls.from_number(OtherFloatSubclass(3.14)), 3.14)
eq(cls.from_number(FloatLike(3.14)), 3.14)
eq(cls.from_number(MyIndex(314)), 314.0)
x = cls.from_number(NAN)
self.assertTrue(x != x)
self.assertIs(type(x), cls)
if cls is float:
self.assertIs(cls.from_number(NAN), NAN)
self.assertRaises(TypeError, cls.from_number, '3.14')
self.assertRaises(TypeError, cls.from_number, b'3.14')
self.assertRaises(TypeError, cls.from_number, 3.14j)
self.assertRaises(TypeError, cls.from_number, MyInt(314))
self.assertRaises(TypeError, cls.from_number, {})
self.assertRaises(TypeError, cls.from_number)
def test_from_number_subclass(self):
self.test_from_number(FloatSubclass)
def test_is_integer(self):
self.assertFalse((1.1).is_integer())
self.assertTrue((1.).is_integer())