https://github.com/python/cpython/commit/f79ffc879b919604ed5de22ece83825006cf9a17
commit: f79ffc879b919604ed5de22ece83825006cf9a17
branch: main
author: Mark Dickinson <[email protected]>
committer: mdickinson <[email protected]>
date: 2024-06-02T10:16:49+01:00
summary:

gh-119740: Remove deprecated trunc delegation (#119743)

Remove the delegation of `int` to the `__trunc__` special method: `int` will 
now only delegate to `__int__` and `__index__` (in that order). `__trunc__` 
continues to exist, but its sole purpose is to support `math.trunc`.

---------

Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Serhiy Storchaka <[email protected]>

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-05-29-18-53-43.gh-issue-119740.zP2JNM.rst
M Doc/library/functions.rst
M Doc/reference/datamodel.rst
M Doc/whatsnew/3.14.rst
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_unicodeobject_generated.h
M Lib/test/test_int.py
M Lib/test/test_long.py
M Objects/abstract.c

diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 7291461c69acd2..4617767a71be18 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -1004,9 +1004,8 @@ are always available.  They are listed here in 
alphabetical order.
       115
 
    If the argument defines :meth:`~object.__int__`,
-   ``int(x)`` returns ``x.__int__()``.  If the argument defines 
:meth:`~object.__index__`,
-   it returns ``x.__index__()``.  If the argument defines 
:meth:`~object.__trunc__`,
-   it returns ``x.__trunc__()``.
+   ``int(x)`` returns ``x.__int__()``.  If the argument defines
+   :meth:`~object.__index__`, it returns ``x.__index__()``.
    For floating point numbers, this truncates towards zero.
 
    If the argument is not a number or if *base* is given, then it must be a 
string,
@@ -1044,9 +1043,6 @@ are always available.  They are listed here in 
alphabetical order.
    .. versionchanged:: 3.8
       Falls back to :meth:`~object.__index__` if :meth:`~object.__int__` is 
not defined.
 
-   .. versionchanged:: 3.11
-      The delegation to :meth:`~object.__trunc__` is deprecated.
-
    .. versionchanged:: 3.11
       :class:`int` string inputs and string representations can be limited to
       help avoid denial of service attacks. A :exc:`ValueError` is raised when
@@ -1055,6 +1051,9 @@ are always available.  They are listed here in 
alphabetical order.
       See the :ref:`integer string conversion length limitation
       <int_max_str_digits>` documentation.
 
+   .. versionchanged:: 3.14
+      :func:`int` no longer delegates to the :meth:`~object.__trunc__` method.
+
 .. function:: isinstance(object, classinfo)
 
    Return ``True`` if the *object* argument is an instance of the *classinfo*
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index 9110060a6177e5..af4c585e1c3e2f 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -3127,11 +3127,8 @@ left undefined.
    return the value of the object truncated to an :class:`~numbers.Integral`
    (typically an :class:`int`).
 
-   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.
+   .. versionchanged:: 3.14
+      :func:`int` no longer delegates to the :meth:`~object.__trunc__` method.
 
 
 .. _context-managers:
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 45ffb281fcc032..9f471d24909215 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -225,6 +225,11 @@ Others
   It had previously raised a :exc:`DeprecationWarning` since Python 3.9. 
(Contributed
   by Jelle Zijlstra in :gh:`118767`.)
 
+* The :func:`int` built-in no longer delegates to
+  :meth:`~object.__trunc__`. Classes that want to support conversion to
+  integer must implement either :meth:`~object.__int__` or
+  :meth:`~object.__index__`. (Contributed by Mark Dickinson in :gh:`119743`.)
+
 
 Porting to Python 3.14
 ======================
diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index a0f8fb71c1ff37..b9fae11dfaa85c 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -732,7 +732,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__));
-    _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__trunc__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__));
     _PyStaticObject_CheckRefcnt((PyObject 
*)&_Py_ID(__typing_is_unpacked_typevartuple__));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index 57d85020f14e05..aa66b20859a472 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -221,7 +221,6 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(__subclasscheck__)
         STRUCT_FOR_ID(__subclasshook__)
         STRUCT_FOR_ID(__truediv__)
-        STRUCT_FOR_ID(__trunc__)
         STRUCT_FOR_ID(__type_params__)
         STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__)
         STRUCT_FOR_ID(__typing_prepare_subst__)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index e62ebd659d30e8..b27720e9ff6ecf 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -730,7 +730,6 @@ extern "C" {
     INIT_ID(__subclasscheck__), \
     INIT_ID(__subclasshook__), \
     INIT_ID(__truediv__), \
-    INIT_ID(__trunc__), \
     INIT_ID(__type_params__), \
     INIT_ID(__typing_is_unpacked_typevartuple__), \
     INIT_ID(__typing_prepare_subst__), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index 892f580e8a6846..c61c556b758769 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -504,9 +504,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
     string = &_Py_ID(__truediv__);
     assert(_PyUnicode_CheckConsistency(string, 1));
     _PyUnicode_InternInPlace(interp, &string);
-    string = &_Py_ID(__trunc__);
-    assert(_PyUnicode_CheckConsistency(string, 1));
-    _PyUnicode_InternInPlace(interp, &string);
     string = &_Py_ID(__type_params__);
     assert(_PyUnicode_CheckConsistency(string, 1));
     _PyUnicode_InternInPlace(interp, &string);
diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py
index caeccbe1fed026..ce9febd741bba2 100644
--- a/Lib/test/test_int.py
+++ b/Lib/test/test_int.py
@@ -402,68 +402,8 @@ def __trunc__(self):
             class JustTrunc(base):
                 def __trunc__(self):
                     return 42
-            with self.assertWarns(DeprecationWarning):
-                self.assertEqual(int(JustTrunc()), 42)
-
-            class ExceptionalTrunc(base):
-                def __trunc__(self):
-                    1 / 0
-            with self.assertRaises(ZeroDivisionError), \
-                 self.assertWarns(DeprecationWarning):
-                int(ExceptionalTrunc())
-
-            for trunc_result_base in (object, Classic):
-                class Index(trunc_result_base):
-                    def __index__(self):
-                        return 42
-
-                class TruncReturnsNonInt(base):
-                    def __trunc__(self):
-                        return Index()
-                with self.assertWarns(DeprecationWarning):
-                    self.assertEqual(int(TruncReturnsNonInt()), 42)
-
-                class Intable(trunc_result_base):
-                    def __int__(self):
-                        return 42
-
-                class TruncReturnsNonIndex(base):
-                    def __trunc__(self):
-                        return Intable()
-                with self.assertWarns(DeprecationWarning):
-                    self.assertEqual(int(TruncReturnsNonInt()), 42)
-
-                class NonIntegral(trunc_result_base):
-                    def __trunc__(self):
-                        # Check that we avoid infinite recursion.
-                        return NonIntegral()
-
-                class TruncReturnsNonIntegral(base):
-                    def __trunc__(self):
-                        return NonIntegral()
-                try:
-                    with self.assertWarns(DeprecationWarning):
-                        int(TruncReturnsNonIntegral())
-                except TypeError as e:
-                    self.assertEqual(str(e),
-                                      "__trunc__ returned non-Integral"
-                                      " (type NonIntegral)")
-                else:
-                    self.fail("Failed to raise TypeError with %s" %
-                              ((base, trunc_result_base),))
-
-                # Regression test for bugs.python.org/issue16060.
-                class BadInt(trunc_result_base):
-                    def __int__(self):
-                        return 42.0
-
-                class TruncReturnsBadInt(base):
-                    def __trunc__(self):
-                        return BadInt()
-
-                with self.assertRaises(TypeError), \
-                     self.assertWarns(DeprecationWarning):
-                    int(TruncReturnsBadInt())
+            with self.assertRaises(TypeError):
+                int(JustTrunc())
 
     def test_int_subclass_with_index(self):
         class MyIndex(int):
@@ -514,18 +454,6 @@ class BadInt2(int):
             def __int__(self):
                 return True
 
-        class TruncReturnsBadIndex:
-            def __trunc__(self):
-                return BadIndex()
-
-        class TruncReturnsBadInt:
-            def __trunc__(self):
-                return BadInt()
-
-        class TruncReturnsIntSubclass:
-            def __trunc__(self):
-                return True
-
         bad_int = BadIndex()
         with self.assertWarns(DeprecationWarning):
             n = int(bad_int)
@@ -549,26 +477,6 @@ def __trunc__(self):
         self.assertEqual(n, 1)
         self.assertIs(type(n), int)
 
-        bad_int = TruncReturnsBadIndex()
-        with self.assertWarns(DeprecationWarning):
-            n = int(bad_int)
-        self.assertEqual(n, 1)
-        self.assertIs(type(n), int)
-
-        bad_int = TruncReturnsBadInt()
-        with self.assertWarns(DeprecationWarning):
-            self.assertRaises(TypeError, int, bad_int)
-
-        good_int = TruncReturnsIntSubclass()
-        with self.assertWarns(DeprecationWarning):
-            n = int(good_int)
-        self.assertEqual(n, 1)
-        self.assertIs(type(n), int)
-        with self.assertWarns(DeprecationWarning):
-            n = IntSubclass(good_int)
-        self.assertEqual(n, 1)
-        self.assertIs(type(n), IntSubclass)
-
     def test_error_message(self):
         def check(s, base=None):
             with self.assertRaises(ValueError,
diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index 41b973da2c7df0..3b2e7c4e71d10d 100644
--- a/Lib/test/test_long.py
+++ b/Lib/test/test_long.py
@@ -386,15 +386,6 @@ def __long__(self):
                 return 42
         self.assertRaises(TypeError, int, JustLong())
 
-        class LongTrunc:
-            # __long__ should be ignored in 3.x
-            def __long__(self):
-                return 42
-            def __trunc__(self):
-                return 1729
-        with self.assertWarns(DeprecationWarning):
-            self.assertEqual(int(LongTrunc()), 1729)
-
     def check_float_conversion(self, n):
         # Check that int -> float conversion behaviour matches
         # that of the pure Python version above.
diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2024-05-29-18-53-43.gh-issue-119740.zP2JNM.rst b/Misc/NEWS.d/next/Core 
and Builtins/2024-05-29-18-53-43.gh-issue-119740.zP2JNM.rst
new file mode 100644
index 00000000000000..111e096d262ea0
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2024-05-29-18-53-43.gh-issue-119740.zP2JNM.rst 
@@ -0,0 +1,2 @@
+Remove the previously-deprecated delegation of :func:`int` to
+:meth:`~object.__trunc__`.
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 8357175aa5591e..200817064e3cda 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -1521,7 +1521,6 @@ PyNumber_Long(PyObject *o)
 {
     PyObject *result;
     PyNumberMethods *m;
-    PyObject *trunc_func;
     Py_buffer view;
 
     if (o == NULL) {
@@ -1563,37 +1562,6 @@ PyNumber_Long(PyObject *o)
     if (m && m->nb_index) {
         return PyNumber_Index(o);
     }
-    trunc_func = _PyObject_LookupSpecial(o, &_Py_ID(__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)) {
-            return result;
-        }
-        if (PyLong_Check(result)) {
-            Py_SETREF(result, _PyLong_Copy((PyLongObject *)result));
-            return result;
-        }
-        /* __trunc__ is specified to return an Integral type,
-           but int() needs to return an int. */
-        if (!PyIndex_Check(result)) {
-            PyErr_Format(
-                PyExc_TypeError,
-                "__trunc__ returned non-Integral (type %.200s)",
-                Py_TYPE(result)->tp_name);
-            Py_DECREF(result);
-            return NULL;
-        }
-        Py_SETREF(result, PyNumber_Index(result));
-        return result;
-    }
-    if (PyErr_Occurred())
-        return NULL;
 
     if (PyUnicode_Check(o))
         /* The below check is done in PyLong_FromUnicodeObject(). */

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to