Skip to content

Commit 8bb4fb3

Browse files
mdickinsonpicnixzserhiy-storchaka
authored andcommitted
pythongh-119740: Remove deprecated trunc delegation (python#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]>
1 parent faa9224 commit 8bb4fb3

11 files changed

+16
-152
lines changed

Doc/library/functions.rst

+5-6
Original file line numberDiff line numberDiff line change
@@ -1004,9 +1004,8 @@ are always available. They are listed here in alphabetical order.
10041004
115
10051005

10061006
If the argument defines :meth:`~object.__int__`,
1007-
``int(x)`` returns ``x.__int__()``. If the argument defines :meth:`~object.__index__`,
1008-
it returns ``x.__index__()``. If the argument defines :meth:`~object.__trunc__`,
1009-
it returns ``x.__trunc__()``.
1007+
``int(x)`` returns ``x.__int__()``. If the argument defines
1008+
:meth:`~object.__index__`, it returns ``x.__index__()``.
10101009
For floating point numbers, this truncates towards zero.
10111010

10121011
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.
10441043
.. versionchanged:: 3.8
10451044
Falls back to :meth:`~object.__index__` if :meth:`~object.__int__` is not defined.
10461045

1047-
.. versionchanged:: 3.11
1048-
The delegation to :meth:`~object.__trunc__` is deprecated.
1049-
10501046
.. versionchanged:: 3.11
10511047
:class:`int` string inputs and string representations can be limited to
10521048
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.
10551051
See the :ref:`integer string conversion length limitation
10561052
<int_max_str_digits>` documentation.
10571053

1054+
.. versionchanged:: 3.14
1055+
:func:`int` no longer delegates to the :meth:`~object.__trunc__` method.
1056+
10581057
.. function:: isinstance(object, classinfo)
10591058

10601059
Return ``True`` if the *object* argument is an instance of the *classinfo*

Doc/reference/datamodel.rst

+2-5
Original file line numberDiff line numberDiff line change
@@ -3127,11 +3127,8 @@ left undefined.
31273127
return the value of the object truncated to an :class:`~numbers.Integral`
31283128
(typically an :class:`int`).
31293129

3130-
The built-in function :func:`int` falls back to :meth:`__trunc__` if neither
3131-
:meth:`__int__` nor :meth:`__index__` is defined.
3132-
3133-
.. versionchanged:: 3.11
3134-
The delegation of :func:`int` to :meth:`__trunc__` is deprecated.
3130+
.. versionchanged:: 3.14
3131+
:func:`int` no longer delegates to the :meth:`~object.__trunc__` method.
31353132

31363133

31373134
.. _context-managers:

Doc/whatsnew/3.14.rst

+5
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ Others
225225
It had previously raised a :exc:`DeprecationWarning` since Python 3.9. (Contributed
226226
by Jelle Zijlstra in :gh:`118767`.)
227227

228+
* The :func:`int` built-in no longer delegates to
229+
:meth:`~object.__trunc__`. Classes that want to support conversion to
230+
integer must implement either :meth:`~object.__int__` or
231+
:meth:`~object.__index__`. (Contributed by Mark Dickinson in :gh:`119743`.)
232+
228233

229234
Porting to Python 3.14
230235
======================

Include/internal/pycore_global_objects_fini_generated.h

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ struct _Py_global_strings {
221221
STRUCT_FOR_ID(__subclasscheck__)
222222
STRUCT_FOR_ID(__subclasshook__)
223223
STRUCT_FOR_ID(__truediv__)
224-
STRUCT_FOR_ID(__trunc__)
225224
STRUCT_FOR_ID(__type_params__)
226225
STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__)
227226
STRUCT_FOR_ID(__typing_prepare_subst__)

Include/internal/pycore_runtime_init_generated.h

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_int.py

+2-94
Original file line numberDiff line numberDiff line change
@@ -402,68 +402,8 @@ def __trunc__(self):
402402
class JustTrunc(base):
403403
def __trunc__(self):
404404
return 42
405-
with self.assertWarns(DeprecationWarning):
406-
self.assertEqual(int(JustTrunc()), 42)
407-
408-
class ExceptionalTrunc(base):
409-
def __trunc__(self):
410-
1 / 0
411-
with self.assertRaises(ZeroDivisionError), \
412-
self.assertWarns(DeprecationWarning):
413-
int(ExceptionalTrunc())
414-
415-
for trunc_result_base in (object, Classic):
416-
class Index(trunc_result_base):
417-
def __index__(self):
418-
return 42
419-
420-
class TruncReturnsNonInt(base):
421-
def __trunc__(self):
422-
return Index()
423-
with self.assertWarns(DeprecationWarning):
424-
self.assertEqual(int(TruncReturnsNonInt()), 42)
425-
426-
class Intable(trunc_result_base):
427-
def __int__(self):
428-
return 42
429-
430-
class TruncReturnsNonIndex(base):
431-
def __trunc__(self):
432-
return Intable()
433-
with self.assertWarns(DeprecationWarning):
434-
self.assertEqual(int(TruncReturnsNonInt()), 42)
435-
436-
class NonIntegral(trunc_result_base):
437-
def __trunc__(self):
438-
# Check that we avoid infinite recursion.
439-
return NonIntegral()
440-
441-
class TruncReturnsNonIntegral(base):
442-
def __trunc__(self):
443-
return NonIntegral()
444-
try:
445-
with self.assertWarns(DeprecationWarning):
446-
int(TruncReturnsNonIntegral())
447-
except TypeError as e:
448-
self.assertEqual(str(e),
449-
"__trunc__ returned non-Integral"
450-
" (type NonIntegral)")
451-
else:
452-
self.fail("Failed to raise TypeError with %s" %
453-
((base, trunc_result_base),))
454-
455-
# Regression test for bugs.python.org/issue16060.
456-
class BadInt(trunc_result_base):
457-
def __int__(self):
458-
return 42.0
459-
460-
class TruncReturnsBadInt(base):
461-
def __trunc__(self):
462-
return BadInt()
463-
464-
with self.assertRaises(TypeError), \
465-
self.assertWarns(DeprecationWarning):
466-
int(TruncReturnsBadInt())
405+
with self.assertRaises(TypeError):
406+
int(JustTrunc())
467407

468408
def test_int_subclass_with_index(self):
469409
class MyIndex(int):
@@ -514,18 +454,6 @@ class BadInt2(int):
514454
def __int__(self):
515455
return True
516456

517-
class TruncReturnsBadIndex:
518-
def __trunc__(self):
519-
return BadIndex()
520-
521-
class TruncReturnsBadInt:
522-
def __trunc__(self):
523-
return BadInt()
524-
525-
class TruncReturnsIntSubclass:
526-
def __trunc__(self):
527-
return True
528-
529457
bad_int = BadIndex()
530458
with self.assertWarns(DeprecationWarning):
531459
n = int(bad_int)
@@ -549,26 +477,6 @@ def __trunc__(self):
549477
self.assertEqual(n, 1)
550478
self.assertIs(type(n), int)
551479

552-
bad_int = TruncReturnsBadIndex()
553-
with self.assertWarns(DeprecationWarning):
554-
n = int(bad_int)
555-
self.assertEqual(n, 1)
556-
self.assertIs(type(n), int)
557-
558-
bad_int = TruncReturnsBadInt()
559-
with self.assertWarns(DeprecationWarning):
560-
self.assertRaises(TypeError, int, bad_int)
561-
562-
good_int = TruncReturnsIntSubclass()
563-
with self.assertWarns(DeprecationWarning):
564-
n = int(good_int)
565-
self.assertEqual(n, 1)
566-
self.assertIs(type(n), int)
567-
with self.assertWarns(DeprecationWarning):
568-
n = IntSubclass(good_int)
569-
self.assertEqual(n, 1)
570-
self.assertIs(type(n), IntSubclass)
571-
572480
def test_error_message(self):
573481
def check(s, base=None):
574482
with self.assertRaises(ValueError,

Lib/test/test_long.py

-9
Original file line numberDiff line numberDiff line change
@@ -386,15 +386,6 @@ def __long__(self):
386386
return 42
387387
self.assertRaises(TypeError, int, JustLong())
388388

389-
class LongTrunc:
390-
# __long__ should be ignored in 3.x
391-
def __long__(self):
392-
return 42
393-
def __trunc__(self):
394-
return 1729
395-
with self.assertWarns(DeprecationWarning):
396-
self.assertEqual(int(LongTrunc()), 1729)
397-
398389
def check_float_conversion(self, n):
399390
# Check that int -> float conversion behaviour matches
400391
# that of the pure Python version above.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Remove the previously-deprecated delegation of :func:`int` to
2+
:meth:`~object.__trunc__`.

Objects/abstract.c

-32
Original file line numberDiff line numberDiff line change
@@ -1521,7 +1521,6 @@ PyNumber_Long(PyObject *o)
15211521
{
15221522
PyObject *result;
15231523
PyNumberMethods *m;
1524-
PyObject *trunc_func;
15251524
Py_buffer view;
15261525

15271526
if (o == NULL) {
@@ -1563,37 +1562,6 @@ PyNumber_Long(PyObject *o)
15631562
if (m && m->nb_index) {
15641563
return PyNumber_Index(o);
15651564
}
1566-
trunc_func = _PyObject_LookupSpecial(o, &_Py_ID(__trunc__));
1567-
if (trunc_func) {
1568-
if (PyErr_WarnEx(PyExc_DeprecationWarning,
1569-
"The delegation of int() to __trunc__ is deprecated.", 1)) {
1570-
Py_DECREF(trunc_func);
1571-
return NULL;
1572-
}
1573-
result = _PyObject_CallNoArgs(trunc_func);
1574-
Py_DECREF(trunc_func);
1575-
if (result == NULL || PyLong_CheckExact(result)) {
1576-
return result;
1577-
}
1578-
if (PyLong_Check(result)) {
1579-
Py_SETREF(result, _PyLong_Copy((PyLongObject *)result));
1580-
return result;
1581-
}
1582-
/* __trunc__ is specified to return an Integral type,
1583-
but int() needs to return an int. */
1584-
if (!PyIndex_Check(result)) {
1585-
PyErr_Format(
1586-
PyExc_TypeError,
1587-
"__trunc__ returned non-Integral (type %.200s)",
1588-
Py_TYPE(result)->tp_name);
1589-
Py_DECREF(result);
1590-
return NULL;
1591-
}
1592-
Py_SETREF(result, PyNumber_Index(result));
1593-
return result;
1594-
}
1595-
if (PyErr_Occurred())
1596-
return NULL;
15971565

15981566
if (PyUnicode_Check(o))
15991567
/* The below check is done in PyLong_FromUnicodeObject(). */

0 commit comments

Comments
 (0)