diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 9c061bcd825..eaa4d482ce3 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -891,6 +891,9 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.8 Falls back to :meth:`__index__` if :meth:`__int__` is not defined. + .. versionchanged:: 3.11 + The delegation to :meth:`__trunc__` is deprecated. + .. function:: isinstance(object, classinfo) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 48c54d72942..a773b73ce32 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2760,6 +2760,9 @@ left undefined. The built-in function :func:`int` falls back to :meth:`__trunc__` if neither :meth:`__int__` nor :meth:`__index__` is defined. + .. versionchanged:: 3.11 + The delegation of :func:`int` to :meth:`__trunc__` is deprecated. + .. _context-managers: diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index acb21d3ccaf..18d4652fb42 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -458,6 +458,11 @@ Deprecated as deprecated, its docstring is now corrected). (Contributed by Hugo van Kemenade in :issue:`45837`.) +* The delegation of :func:`int` to :meth:`__trunc__` is now deprecated. Calling + ``int(a)`` when ``type(a)`` implements :meth:`__trunc__` but not + :meth:`__int__` or :meth:`__index__` now raises a :exc:`DeprecationWarning`. + (Contributed by Zackery Spytz in :issue:`44977`.) + * The following have been deprecated in :mod:`configparser` since Python 3.2. Their deprecation warnings have now been updated to note they will removed in Python 3.12: @@ -468,6 +473,7 @@ Deprecated (Contributed by Hugo van Kemenade in :issue:`45173`.) + Removed ======= diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 791cf714d46..a0942d6a85b 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2061,7 +2061,6 @@ order (MRO) for bases """ ("__format__", format, format_impl, set(), {}), ("__floor__", math.floor, zero, set(), {}), ("__trunc__", math.trunc, zero, set(), {}), - ("__trunc__", int, zero, set(), {}), ("__ceil__", math.ceil, zero, set(), {}), ("__dir__", dir, empty_seq, set(), {}), ("__round__", round, zero, set(), {}), diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index d6be64e7c18..a72699cc750 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -369,12 +369,14 @@ class IntTestCases(unittest.TestCase): 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 +387,8 @@ class IntTestCases(unittest.TestCase): 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 +397,8 @@ class IntTestCases(unittest.TestCase): 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 +409,8 @@ class IntTestCases(unittest.TestCase): 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 +428,8 @@ class IntTestCases(unittest.TestCase): 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 +523,16 @@ class IntTestCases(unittest.TestCase): 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) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index c7dd0b274d1..e68dfb4c542 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -392,7 +392,8 @@ class LongTest(unittest.TestCase): return 42 def __trunc__(self): return 1729 - self.assertEqual(int(LongTrunc()), 1729) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(LongTrunc()), 1729) def check_float_conversion(self, n): # Check that int -> float conversion behaviour matches diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-30-18-23-08.bpo-44977.BQV_zS.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-30-18-23-08.bpo-44977.BQV_zS.rst new file mode 100644 index 00000000000..84c1191bdbd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-30-18-23-08.bpo-44977.BQV_zS.rst @@ -0,0 +1,3 @@ +The delegation of :func:`int` to :meth:`__trunc__` is now deprecated. +Calling ``int(a)`` when ``type(a)`` implements :meth:`__trunc__` but not +:meth:`__int__` or :meth:`__index__` now raises a :exc:`DeprecationWarning`. diff --git a/Objects/abstract.c b/Objects/abstract.c index 6a2d5eda140..ed99fd6c5aa 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1564,6 +1564,11 @@ PyNumber_Long(PyObject *o) } trunc_func = _PyObject_LookupSpecial(o, &PyId___trunc__); if (trunc_func) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "The delegation of int() to __trunc__ is deprecated.", 1)) { + Py_DECREF(trunc_func); + return NULL; + } result = _PyObject_CallNoArgs(trunc_func); Py_DECREF(trunc_func); if (result == NULL || PyLong_CheckExact(result)) {