Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
534939e
gh-145633: deprecate float.__getformat__() class method
skirpichev Mar 25, 2026
aaf2b0a
workaround for missing ctypes
skirpichev Mar 25, 2026
907bfb1
+ use struct
skirpichev Mar 25, 2026
a8cbdbf
+news
skirpichev Mar 25, 2026
e38e308
Merge branch 'master' into deprecate-__getformat__/145633
skirpichev Apr 1, 2026
3a4c818
address review: restore test_getformat()
skirpichev Apr 1, 2026
b93f48c
address review: test_funcattrs.py
skirpichev Apr 1, 2026
04356f7
address review: document _have_ieee_doubles + a quick exit
skirpichev Apr 1, 2026
68a3264
address review: adjust news
skirpichev Apr 1, 2026
9fe8a22
+typo
skirpichev Apr 1, 2026
27d31f2
Merge branch 'master' into deprecate-__getformat__/145633
skirpichev Apr 17, 2026
291401b
address review: use assertWarns(DeprecationWarning)
skirpichev Apr 17, 2026
67fc46e
Apply suggestion from @skirpichev
skirpichev Apr 17, 2026
e4d5f18
+1
skirpichev Apr 17, 2026
3a0136e
Merge branch 'master' into deprecate-__getformat__/145633
skirpichev May 22, 2026
ec18531
Merge branch 'main' into deprecate-__getformat__/145633
skirpichev Jul 31, 2026
6dec973
Merge branch 'main' into deprecate-__getformat__/145633
skirpichev Aug 1, 2026
d330317
Merge branch 'main' into deprecate-__getformat__/145633
skirpichev Aug 3, 2026
40f9873
XXX partial reversion of 1cbe460eb6c
skirpichev Aug 3, 2026
a7f9a1a
Revert "XXX partial reversion of 1cbe460eb6c"
skirpichev Aug 3, 2026
1389d58
XXX
skirpichev Aug 3, 2026
2c55ab2
Merge branch 'main' into deprecate-__getformat__/145633
skirpichev Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/deprecations/pending-removal-in-3.21.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Pending removal in Python 3.21
------------------------------

* The ``__getformat__()`` class method of the :class:`float` is deprecated and
will be removed in Python 3.21. On CPython, ``float.__getformat__()`` always
return a string, prefixed with ``"IEEE"``: to build CPython, you need support
for IEEE 754 floating-point numbers since Python 3.11. (Contributed by
Sergey B Kirpichev in :gh:`85989`.)

* :mod:`abc`

* Soft-deprecated since Python 3.3 :class:`abc.abstractclassmethod`,
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,12 @@ Deprecated
New deprecations
----------------

* The ``__getformat__()`` class method of the :class:`float` is deprecated
and will be removed in Python 3.21. On CPython, ``float.__getformat__()``
always return a string, prefixed with ``"IEEE"``: to build CPython, you
need support for IEEE 754 floating-point numbers since Python 3.11.
(Contributed by Sergey B Kirpichev in :gh:`85989`.)

* :mod:`abc`

* Soft-deprecated since Python 3.3 :class:`abc.abstractclassmethod`,
Expand Down
6 changes: 0 additions & 6 deletions Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,6 @@ def collect_locale(info_add):
info_add('locale.getencoding', locale.getencoding())


def collect_builtins(info_add):
info_add('builtins.float.float_format', float.__getformat__("float"))
info_add('builtins.float.double_format', float.__getformat__("double"))
Comment thread
vstinner marked this conversation as resolved.


def collect_urandom(info_add):
import os

Expand Down Expand Up @@ -1333,7 +1328,6 @@ def collect_info(info):
# its state.
collect_urandom,

collect_builtins,
collect_cc,
collect_curses,
collect_datetime,
Expand Down
36 changes: 35 additions & 1 deletion Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,44 @@ def dec(*args, **kwargs):
# for a discussion of this number.
SOCK_MAX_SIZE = 16 * 1024 * 1024 + 1

# This helper exists for alternative Python implementations, that use
# the CPython test suite.
def _have_ieee_doubles():
Comment thread
skirpichev marked this conversation as resolved.
if sys.implementation.name == 'cpython':
return True
import math
import struct
# Check parameters for encoding of floats; a quick exit
# if they aren't same as for IEC 60559 doubles. Check
# also that subnormals are present.
if (struct.calcsize('d') != 8
or sys.float_info.radix != 2
or sys.float_info.mant_dig != 53
or sys.float_info.dig != 15
or sys.float_info.min_exp != -1021
or sys.float_info.min_10_exp != -307
or sys.float_info.max_exp != 1024
or sys.float_info.max_10_exp != 308
or not math.issubnormal(math.nextafter(0, 1))):
return False
# We attempt to determine if this machine is using IEC
# floating-point formats by peering at the bits of some
# carefully chosen value. Assume that integer and
# floating-point types have same endianness.
d = 9006104071832581.0
d_be_bytes = b"\x43\x3f\xff\x01\x02\x03\x04\x05"
d_packed = struct.pack('d', d)
if sys.byteorder == 'little':
return d_packed == bytes(reversed(d_be_bytes))
return d_packed == d_be_bytes

HAVE_IEEE_754 = _have_ieee_doubles()

# decorator for skipping tests on non-IEEE 754 platforms
requires_IEEE_754 = unittest.skipUnless(
float.__getformat__("double").startswith("IEEE"),
HAVE_IEEE_754,
"test requires IEEE 754 doubles")
del HAVE_IEEE_754

# detect evidence of double-rounding:
x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer
Expand Down
11 changes: 5 additions & 6 deletions Lib/test/test_capi/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from test.test_capi.test_getargs import (Float, FloatSubclass, FloatSubclass2,
BadIndex2, BadFloat2, Index, BadIndex,
BadFloat)
from test import support
from test.support import import_helper

_testcapi = import_helper.import_module('_testcapi')
Expand All @@ -23,7 +24,6 @@
8: 2.0 ** -53, # binary64
}

HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
INF = float("inf")
NAN = float("nan")

Expand Down Expand Up @@ -170,14 +170,13 @@ def test_unpack(self):
self.assertEqual(unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN),
1.5)

@support.requires_IEEE_754
def test_pack_unpack_roundtrip(self):
pack = _testcapi.float_pack
unpack = _testcapi.float_unpack

large = 2.0 ** 100
values = [1.0, 1.5, large, 1.0/7, math.pi]
if HAVE_IEEE_754:
values.extend((INF, NAN))
values = [1.0, 1.5, large, 1.0/7, math.pi, INF, NAN]
for value in values:
for size in (2, 4, 8,):
if size == 2 and value == large:
Expand All @@ -196,7 +195,7 @@ def test_pack_unpack_roundtrip(self):
else:
self.assertEqual(value2, value)

@unittest.skipUnless(HAVE_IEEE_754, "requires IEEE 754")
@support.requires_IEEE_754
def test_pack_unpack_roundtrip_for_nans(self):
pack = _testcapi.float_pack
unpack = _testcapi.float_unpack
Expand Down Expand Up @@ -228,7 +227,7 @@ def test_pack_unpack_roundtrip_for_nans(self):
self.assertTrue(math.isnan(value))
self.assertEqual(data1, data2)

@unittest.skipUnless(HAVE_IEEE_754, "requires IEEE 754")
@support.requires_IEEE_754
@unittest.skipUnless(sys.maxsize != 2147483647, "requires 64-bit mode")
def test_pack_unpack_nans_for_different_formats(self):
pack = _testcapi.float_pack
Expand Down
13 changes: 7 additions & 6 deletions Lib/test/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,12 +673,13 @@ def __neg__(self):
@unittest.skipUnless(hasattr(float, "__getformat__"), "requires __getformat__")
class FormatFunctionsTestCase(unittest.TestCase):
def test_getformat(self):
self.assertIn(float.__getformat__('double'),
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
self.assertIn(float.__getformat__('float'),
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
self.assertRaises(ValueError, float.__getformat__, 'chicken')
self.assertRaises(TypeError, float.__getformat__, 1)
with self.assertWarns(DeprecationWarning):
self.assertIn(float.__getformat__('double'),
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
self.assertIn(float.__getformat__('float'),
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
self.assertRaises(ValueError, float.__getformat__, 'chicken')
self.assertRaises(TypeError, float.__getformat__, 1)


BE_DOUBLE_INF = b'\x7f\xf0\x00\x00\x00\x00\x00\x00'
Expand Down
5 changes: 2 additions & 3 deletions Lib/test/test_funcattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,7 @@ def test_builtin__qualname__(self):

# builtin classmethod:
self.assertEqual(dict.fromkeys.__qualname__, 'dict.fromkeys')
self.assertEqual(float.__getformat__.__qualname__,
'float.__getformat__')
self.assertEqual(int.from_bytes.__qualname__, 'int.from_bytes')

# builtin staticmethod:
self.assertEqual(str.maketrans.__qualname__, 'str.maketrans')
Expand All @@ -509,7 +508,7 @@ def test_builtin__self__(self):

# builtin classmethod:
self.assertIs(dict.fromkeys.__self__, dict)
self.assertIs(float.__getformat__.__self__, float)
Comment thread
skirpichev marked this conversation as resolved.
self.assertIs(int.from_bytes.__self__, int)

# builtin staticmethod:
self.assertIsNone(str.maketrans.__self__)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The ``__getformat__()`` class method of the :class:`float` is deprecated. On
CPython, ``float.__getformat__()`` always return a string, prefixed with
``"IEEE"``: to build CPython, you need support for IEEE 754 floating-point
numbers since Python 3.11. Patch by Sergey B Kirpichev.
5 changes: 5 additions & 0 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,11 @@ static PyObject *
float___getformat___impl(PyTypeObject *type, const char *typestr)
/*[clinic end generated code: output=2bfb987228cc9628 input=eb1cf45e9bddab72]*/
{
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"float.__getformat__() is deprecated"))
{
return NULL;
}
if (strcmp(typestr, "double") != 0 && strcmp(typestr, "float") != 0) {
PyErr_SetString(PyExc_ValueError,
"__getformat__() argument 1 must be "
Expand Down
Loading