From 77d9c8dcf0a334bc1cedee308d2ad143f25942b5 Mon Sep 17 00:00:00 2001 From: Andrey Maltsev Date: Sun, 2 Apr 2023 14:51:38 +0000 Subject: [PATCH] Update test_float.py from Cpython v3.11.2 --- Lib/test/test_float.py | 199 +++++++++++++++++++++++++---------------- 1 file changed, 123 insertions(+), 76 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 76c6f76c9a6..36af87873a9 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -8,18 +8,21 @@ import unittest from test import support +from test.support import import_helper from test.test_grammar import (VALID_UNDERSCORE_LITERALS, INVALID_UNDERSCORE_LITERALS) from math import isinf, isnan, copysign, ldexp +import math +try: + import _testcapi +except ImportError: + _testcapi = None + +HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE") INF = float("inf") NAN = float("nan") -have_getformat = hasattr(float, "__getformat__") -requires_getformat = unittest.skipUnless(have_getformat, - "requires __getformat__") -requires_setformat = unittest.skipUnless(hasattr(float, "__setformat__"), - "requires __setformat__") #locate file with float format test values test_dir = os.path.dirname(__file__) or os.curdir @@ -139,6 +142,10 @@ def check(s): check('123\xbd') check(' 123 456 ') check(b' 123 456 ') + # all whitespace (cf. https://github.com/python/cpython/issues/95605) + check('') + check(' ') + check('\t \n') # non-ascii digits (error came from non-digit '!') check('\u0663\u0661\u0664!') @@ -249,6 +256,35 @@ def test_keyword_args(self): with self.assertRaisesRegex(TypeError, 'keyword argument'): float(x='3.14') + # TODO: RUSTPYTHON + @unittest.expectedFailure + def test_keywords_in_subclass(self): + class subclass(float): + pass + u = subclass(2.5) + self.assertIs(type(u), subclass) + self.assertEqual(float(u), 2.5) + with self.assertRaises(TypeError): + subclass(x=0) + + class subclass_with_init(float): + def __init__(self, arg, newarg=None): + self.newarg = newarg + u = subclass_with_init(2.5, newarg=3) + self.assertIs(type(u), subclass_with_init) + self.assertEqual(float(u), 2.5) + self.assertEqual(u.newarg, 3) + + class subclass_with_new(float): + def __new__(cls, arg, newarg=None): + self = super().__new__(cls, arg) + self.newarg = newarg + return self + u = subclass_with_new(2.5, newarg=3) + self.assertIs(type(u), subclass_with_new) + self.assertEqual(float(u), 2.5) + self.assertEqual(u.newarg, 3) + def test_is_integer(self): self.assertFalse((1.1).is_integer()) self.assertTrue((1.).is_integer()) @@ -590,17 +626,8 @@ class F(float, H): self.assertEqual(hash(value), object.__hash__(value)) -@requires_setformat +@unittest.skipUnless(hasattr(float, "__getformat__"), "requires __getformat__") class FormatFunctionsTestCase(unittest.TestCase): - - def setUp(self): - self.save_formats = {'double':float.__getformat__('double'), - 'float':float.__getformat__('float')} - - def tearDown(self): - float.__setformat__('double', self.save_formats['double']) - float.__setformat__('float', self.save_formats['float']) - def test_getformat(self): self.assertIn(float.__getformat__('double'), ['unknown', 'IEEE, big-endian', 'IEEE, little-endian']) @@ -609,24 +636,6 @@ def test_getformat(self): self.assertRaises(ValueError, float.__getformat__, 'chicken') self.assertRaises(TypeError, float.__getformat__, 1) - def test_setformat(self): - for t in 'double', 'float': - float.__setformat__(t, 'unknown') - if self.save_formats[t] == 'IEEE, big-endian': - self.assertRaises(ValueError, float.__setformat__, - t, 'IEEE, little-endian') - elif self.save_formats[t] == 'IEEE, little-endian': - self.assertRaises(ValueError, float.__setformat__, - t, 'IEEE, big-endian') - else: - self.assertRaises(ValueError, float.__setformat__, - t, 'IEEE, big-endian') - self.assertRaises(ValueError, float.__setformat__, - t, 'IEEE, little-endian') - self.assertRaises(ValueError, float.__setformat__, - t, 'chicken') - self.assertRaises(ValueError, float.__setformat__, - 'chicken', 'unknown') BE_DOUBLE_INF = b'\x7f\xf0\x00\x00\x00\x00\x00\x00' LE_DOUBLE_INF = bytes(reversed(BE_DOUBLE_INF)) @@ -638,36 +647,6 @@ def test_setformat(self): BE_FLOAT_NAN = b'\x7f\xc0\x00\x00' LE_FLOAT_NAN = bytes(reversed(BE_FLOAT_NAN)) -# on non-IEEE platforms, attempting to unpack a bit pattern -# representing an infinity or a NaN should raise an exception. - -@requires_setformat -class UnknownFormatTestCase(unittest.TestCase): - def setUp(self): - self.save_formats = {'double':float.__getformat__('double'), - 'float':float.__getformat__('float')} - float.__setformat__('double', 'unknown') - float.__setformat__('float', 'unknown') - - def tearDown(self): - float.__setformat__('double', self.save_formats['double']) - float.__setformat__('float', self.save_formats['float']) - - def test_double_specials_dont_unpack(self): - for fmt, data in [('>d', BE_DOUBLE_INF), - ('>d', BE_DOUBLE_NAN), - ('f', BE_FLOAT_INF), - ('>f', BE_FLOAT_NAN), - ('\x00') + self.assertEqual(_testcapi.float_pack(4, 1.5, BIG_ENDIAN), + b'?\xc0\x00\x00') + self.assertEqual(_testcapi.float_pack(8, 1.5, BIG_ENDIAN), + b'?\xf8\x00\x00\x00\x00\x00\x00') + self.assertEqual(_testcapi.float_pack(2, 1.5, LITTLE_ENDIAN), + b'\x00>') + self.assertEqual(_testcapi.float_pack(4, 1.5, LITTLE_ENDIAN), + b'\x00\x00\xc0?') + self.assertEqual(_testcapi.float_pack(8, 1.5, LITTLE_ENDIAN), + b'\x00\x00\x00\x00\x00\x00\xf8?') + + def test_unpack(self): + self.assertEqual(_testcapi.float_unpack(b'>\x00', BIG_ENDIAN), + 1.5) + self.assertEqual(_testcapi.float_unpack(b'?\xc0\x00\x00', BIG_ENDIAN), + 1.5) + self.assertEqual(_testcapi.float_unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN), + 1.5) + self.assertEqual(_testcapi.float_unpack(b'\x00>', LITTLE_ENDIAN), + 1.5) + self.assertEqual(_testcapi.float_unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN), + 1.5) + self.assertEqual(_testcapi.float_unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN), + 1.5) + + def test_roundtrip(self): + large = 2.0 ** 100 + values = [1.0, 1.5, large, 1.0/7, math.pi] + if HAVE_IEEE_754: + values.extend((INF, NAN)) + for value in values: + for size in (2, 4, 8,): + if size == 2 and value == large: + # too large for 16-bit float + continue + rel_tol = EPSILON[size] + for endian in (BIG_ENDIAN, LITTLE_ENDIAN): + with self.subTest(value=value, size=size, endian=endian): + data = _testcapi.float_pack(size, value, endian) + value2 = _testcapi.float_unpack(data, endian) + if isnan(value): + self.assertTrue(isnan(value2), (value, value2)) + elif size < 8: + self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol), + (value, value2)) + else: + self.assertEqual(value2, value) + + if __name__ == '__main__': unittest.main()