From 74005f2a1d039a480d52afdf446d9dce411dad4b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 12 Sep 2023 05:05:25 +0300 Subject: [PATCH 1/8] gh-109311: Remove support for non-complex/float types in __complex/float__ --- Lib/test/test_capi/test_complex.py | 12 +++------- Lib/test/test_capi/test_float.py | 6 +---- Lib/test/test_capi/test_getargs.py | 9 +++----- Lib/test/test_complex.py | 3 +-- Lib/test/test_float.py | 13 +++-------- ...-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst | 2 ++ Objects/abstract.c | 22 ++++--------------- Objects/complexobject.c | 22 +++++-------------- Objects/floatobject.c | 20 +++++------------ 9 files changed, 27 insertions(+), 82 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index d6fc1f077c40aa..33df31309e7f5c 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -83,8 +83,7 @@ def test_realasdouble(self): # Test types with __float__ dunder method self.assertEqual(realasdouble(Float()), 4.25) self.assertRaises(TypeError, realasdouble, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(realasdouble(BadFloat2()), 4.25) + self.assertRaises(TypeError, realasdouble, BadFloat2()) self.assertRaises(TypeError, realasdouble, object()) @@ -130,18 +129,13 @@ def test_asccomplex(self): # Test types with __complex__ dunder method self.assertEqual(asccomplex(Complex()), 4.25+0.5j) self.assertRaises(TypeError, asccomplex, BadComplex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(asccomplex(BadComplex2()), 4.25+0.5j) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - self.assertRaises(DeprecationWarning, asccomplex, BadComplex2()) + self.assertRaises(TypeError, asccomplex, BadComplex2()) self.assertRaises(RuntimeError, asccomplex, BadComplex3()) # Test types with __float__ dunder method self.assertEqual(asccomplex(Float()), 4.25+0.0j) self.assertRaises(TypeError, asccomplex, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(asccomplex(BadFloat2()), 4.25+0.0j) + self.assertRaises(TypeError, asccomplex, BadFloat2()) self.assertRaises(TypeError, asccomplex, object()) diff --git a/Lib/test/test_capi/test_float.py b/Lib/test/test_capi/test_float.py index cb94d562645916..af7332705b6210 100644 --- a/Lib/test/test_capi/test_float.py +++ b/Lib/test/test_capi/test_float.py @@ -99,11 +99,7 @@ def __float__(self): self.assertRaises(RuntimeError, asdouble, BadFloat3()) with self.assertWarns(DeprecationWarning): self.assertEqual(asdouble(BadIndex2()), 1.) - with self.assertWarns(DeprecationWarning): - self.assertEqual(asdouble(BadFloat2()), 4.25) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - self.assertRaises(DeprecationWarning, asdouble, BadFloat2()) + self.assertRaises(TypeError, asdouble, BadFloat2()) self.assertRaises(TypeError, asdouble, object()) self.assertRaises(TypeError, asdouble, NULL) diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 9b6aef27625ad0..56dcc3e927600e 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -444,8 +444,7 @@ def test_f(self): self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5) self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5) self.assertRaises(TypeError, getargs_f, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_f(BadFloat2()), 4.25) + self.assertRaises(TypeError, getargs_f, BadFloat2()) self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5) self.assertEqual(getargs_f(Index()), 99.0) self.assertRaises(TypeError, getargs_f, Int()) @@ -478,8 +477,7 @@ def test_d(self): self.assertEqual(getargs_d(FloatSubclass(7.5)), 7.5) self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5) self.assertRaises(TypeError, getargs_d, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_d(BadFloat2()), 4.25) + self.assertRaises(TypeError, getargs_d, BadFloat2()) self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5) self.assertEqual(getargs_d(Index()), 99.0) self.assertRaises(TypeError, getargs_d, Int()) @@ -502,8 +500,7 @@ def test_D(self): self.assertEqual(getargs_D(ComplexSubclass(7.5+0.25j)), 7.5+0.25j) self.assertEqual(getargs_D(ComplexSubclass2(7.5+0.25j)), 7.5+0.25j) self.assertRaises(TypeError, getargs_D, BadComplex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_D(BadComplex2()), 4.25+0.5j) + self.assertRaises(TypeError, getargs_D, BadComplex2()) self.assertEqual(getargs_D(BadComplex3(7.5+0.25j)), 7.5+0.25j) self.assertEqual(getargs_D(Index()), 99.0+0j) self.assertRaises(TypeError, getargs_D, Int()) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index b057121f285dc7..422ee507ba5bf9 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -527,8 +527,7 @@ def __complex__(self): return None self.assertEqual(complex(complex0(1j)), 42j) - with self.assertWarns(DeprecationWarning): - self.assertEqual(complex(complex1(1j)), 2j) + self.assertRaises(TypeError, complex, complex1(1j)) self.assertRaises(TypeError, complex, complex2(1j)) def test___complex__(self): diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 5bd640617d6874..7c60874b4d402f 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -208,8 +208,7 @@ def __float__(self): self.assertEqual(float(Foo1()), 42.) self.assertEqual(float(Foo2()), 42.) - with self.assertWarns(DeprecationWarning): - self.assertEqual(float(Foo3(21)), 42.) + self.assertRaises(TypeError, float, Foo3(21)) self.assertRaises(TypeError, float, Foo4(42)) self.assertEqual(float(FooStr('8')), 9.) @@ -222,14 +221,8 @@ def __float__(self): class F: def __float__(self): return OtherFloatSubclass(42.) - with self.assertWarns(DeprecationWarning): - self.assertEqual(float(F()), 42.) - with self.assertWarns(DeprecationWarning): - self.assertIs(type(float(F())), float) - with self.assertWarns(DeprecationWarning): - self.assertEqual(FloatSubclass(F()), 42.) - with self.assertWarns(DeprecationWarning): - self.assertIs(type(FloatSubclass(F())), FloatSubclass) + self.assertRaises(TypeError, float, F()) + self.assertRaises(TypeError, FloatSubclass, F()) class MyIndex: def __init__(self, value): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst new file mode 100644 index 00000000000000..d6ce5367ac472c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst @@ -0,0 +1,2 @@ +Remove deprecated in 3.7 support for returning non-complex/float types in +__complex__/__float__(). diff --git a/Objects/abstract.c b/Objects/abstract.c index 43842fbdd6aedd..d27e8d016d1a9c 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1703,25 +1703,11 @@ PyNumber_Float(PyObject *o) return res; } - if (!PyFloat_Check(res)) { - PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name); - Py_DECREF(res); - return NULL; - } - /* Issue #26983: warn if 'res' not of exact type float. */ - if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "%.50s.__float__ returned non-float (type %.50s). " - "The ability to return an instance of a strict subclass of float " - "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name)) { - Py_DECREF(res); - return NULL; - } - double val = PyFloat_AS_DOUBLE(res); + PyErr_Format(PyExc_TypeError, + "%.50s.__float__ returned non-float (type %.50s)", + Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name); Py_DECREF(res); - return PyFloat_FromDouble(val); + return NULL; } if (m && m->nb_index) { diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 0e96f54584677c..ac7d33ebc8f465 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -290,23 +290,11 @@ try_complex_special_method(PyObject *op) if (!res || PyComplex_CheckExact(res)) { return res; } - if (!PyComplex_Check(res)) { - PyErr_Format(PyExc_TypeError, - "__complex__ returned non-complex (type %.200s)", - Py_TYPE(res)->tp_name); - Py_DECREF(res); - return NULL; - } - /* Issue #29894: warn if 'res' not of exact type complex. */ - if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__complex__ returned non-complex (type %.200s). " - "The ability to return an instance of a strict subclass of complex " - "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(res)->tp_name)) { - Py_DECREF(res); - return NULL; - } - return res; + PyErr_Format(PyExc_TypeError, + "__complex__ returned non-complex (type %.200s)", + Py_TYPE(res)->tp_name); + Py_DECREF(res); + return NULL; } return NULL; } diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 364cf1553bb5d4..2e025a018c6313 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -323,21 +323,11 @@ PyFloat_AsDouble(PyObject *op) return -1; } if (!PyFloat_CheckExact(res)) { - if (!PyFloat_Check(res)) { - PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name); - Py_DECREF(res); - return -1; - } - if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "%.50s.__float__ returned non-float (type %.50s). " - "The ability to return an instance of a strict subclass of float " - "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name)) { - Py_DECREF(res); - return -1; - } + PyErr_Format(PyExc_TypeError, + "%.50s.__float__ returned non-float (type %.50s)", + Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name); + Py_DECREF(res); + return -1; } val = PyFloat_AS_DOUBLE(res); From 47a4622526e82b2e48b66cd8ba65b22ae3a4c26c Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 31 May 2024 09:12:01 +0300 Subject: [PATCH 2/8] + adjust more tests --- Lib/test/test_capi/test_complex.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_capi/test_complex.py b/Lib/test/test_capi/test_complex.py index 69b71708ef56a8..281e88dc22b9fd 100644 --- a/Lib/test/test_capi/test_complex.py +++ b/Lib/test/test_capi/test_complex.py @@ -80,11 +80,7 @@ def test_realasdouble(self): # Test types with __complex__ dunder method self.assertEqual(realasdouble(Complex()), 4.25) self.assertRaises(TypeError, realasdouble, BadComplex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(realasdouble(BadComplex2()), 4.25) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - self.assertRaises(DeprecationWarning, realasdouble, BadComplex2()) + self.assertRaises(TypeError, realasdouble, BadComplex2()) self.assertRaises(RuntimeError, realasdouble, BadComplex3()) # Test types with __float__ dunder method @@ -112,18 +108,13 @@ def test_imagasdouble(self): # Test types with __complex__ dunder method self.assertEqual(imagasdouble(Complex()), 0.5) self.assertRaises(TypeError, imagasdouble, BadComplex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(imagasdouble(BadComplex2()), 0.5) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - self.assertRaises(DeprecationWarning, imagasdouble, BadComplex2()) + self.assertRaises(TypeError, imagasdouble, BadComplex2()) self.assertRaises(RuntimeError, imagasdouble, BadComplex3()) # Test types with __float__ dunder method self.assertEqual(imagasdouble(Float()), 0.0) self.assertRaises(TypeError, imagasdouble, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(imagasdouble(BadFloat2()), 0.0) + self.assertRaises(TypeError, imagasdouble, BadFloat2()) self.assertRaises(TypeError, imagasdouble, object()) From f024115824fe6bab37b3a3bcb74a3403d459395e Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 11 May 2025 12:26:55 +0300 Subject: [PATCH 3/8] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst | 4 ++-- Objects/abstract.c | 4 ++-- Objects/complexobject.c | 4 ++-- Objects/floatobject.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst index d6ce5367ac472c..b0097c60d6c582 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst @@ -1,2 +1,2 @@ -Remove deprecated in 3.7 support for returning non-complex/float types in -__complex__/__float__(). +Remove support for returning non-:class:`complex` or non-:class:`float` +objects in :meth:`~object.__complex__` and :object:`~object.__float__`. diff --git a/Objects/abstract.c b/Objects/abstract.c index 7aad53484c527a..d676c13dea8deb 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1608,8 +1608,8 @@ PyNumber_Float(PyObject *o) } PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name); + "%T.__float__ returned non-float (type %T)", + o, res); Py_DECREF(res); return NULL; } diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 49706d0a2977c9..913a719a442189 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -498,8 +498,8 @@ try_complex_special_method(PyObject *op) return res; } PyErr_Format(PyExc_TypeError, - "__complex__ returned non-complex (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__complex__ returned non-complex (type %T)", + op, res); Py_DECREF(res); return NULL; } diff --git a/Objects/floatobject.c b/Objects/floatobject.c index ce572fc4307efa..a7104b242281de 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -287,8 +287,8 @@ PyFloat_AsDouble(PyObject *op) } if (!PyFloat_CheckExact(res)) { PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name); + "%T.__float__ returned non-float (type %T)", + op, res); Py_DECREF(res); return -1; } From 8d27a2b875e217d226bd1b04b153c779e71aaf01 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 11 May 2025 12:31:39 +0300 Subject: [PATCH 4/8] move news --- .../2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{Core and Builtins => Core_and_Builtins}/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst (100%) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst similarity index 100% rename from Misc/NEWS.d/next/Core and Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst rename to Misc/NEWS.d/next/Core_and_Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst From 9c36e32e6cd0c8586099dc689187f718ac9cf9fc Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 11 May 2025 12:41:30 +0300 Subject: [PATCH 5/8] address review: add to whatsnew --- Doc/whatsnew/3.15.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 5a9bf1ae3c97bf..a13a6c59f6890b 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -144,6 +144,18 @@ typing (Contributed by Bénédikt Tran in :gh:`133823`.) +Others +------ + +* Removed support for handling returned + non-:class:`float`/:class:`complex` types from + :meth:`~object.__float__` and :meth:`~object.__complex__`, + respectively. This had previously raised a + :exc:`DeprecationWarning` since Python 3.7. + (Contributed by Sergey B Kirpichev in :gh:`109311`.) + + + Porting to Python 3.15 ====================== From fedfd28baea1b993e016fdf49a3b6fd9f25faa24 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 11 May 2025 12:50:17 +0300 Subject: [PATCH 6/8] +1 --- .../2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst index b0097c60d6c582..c2a2f9701d8589 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2023-12-04-04-31-59.gh-issue-109311.G-qgQZ.rst @@ -1,2 +1,2 @@ Remove support for returning non-:class:`complex` or non-:class:`float` -objects in :meth:`~object.__complex__` and :object:`~object.__float__`. +objects in :meth:`~object.__complex__` and :meth:`~object.__float__`. From bbf976dc5597ebf5f5c11d715fc58e0ac5d1271c Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 11 May 2025 13:24:45 +0300 Subject: [PATCH 7/8] + fix test --- Lib/test/test_capi/test_number.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib/test/test_capi/test_number.py b/Lib/test/test_capi/test_number.py index bdd8868529f632..0c52a5e3d832c5 100644 --- a/Lib/test/test_capi/test_number.py +++ b/Lib/test/test_capi/test_number.py @@ -287,11 +287,7 @@ def test_float(self): self.assertEqual(float_(IndexLike.with_val(-1)), -1.0) self.assertRaises(TypeError, float_, FloatLike.with_val(687)) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - self.assertRaises(DeprecationWarning, float_, FloatLike.with_val(subclassof(float)(4.25))) - with self.assertWarns(DeprecationWarning): - self.assertEqual(float_(FloatLike.with_val(subclassof(float)(4.25))), 4.25) + self.assertRaises(TypeError, float_, FloatLike.with_val(subclassof(float)(4.25))) self.assertRaises(RuntimeError, float_, FloatLike.with_exc(RuntimeError)) self.assertRaises(TypeError, float_, IndexLike.with_val(1.25)) From f0b659620c8eac616b6a6cf9124df6d3942918cf Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 11 May 2025 16:39:08 +0300 Subject: [PATCH 8/8] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/whatsnew/3.15.rst | 1 - Objects/complexobject.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index d0c3aaa280f631..dfffd019a1a5bd 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -155,7 +155,6 @@ Others (Contributed by Sergey B Kirpichev in :gh:`109311`.) - Porting to Python 3.15 ====================== diff --git a/Objects/complexobject.c b/Objects/complexobject.c index 913a719a442189..662a7f80aae9d3 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -498,8 +498,8 @@ try_complex_special_method(PyObject *op) return res; } PyErr_Format(PyExc_TypeError, - "%T.__complex__ returned non-complex (type %T)", - op, res); + "%T.__complex__ returned non-complex (type %T)", + op, res); Py_DECREF(res); return NULL; }