mirror of https://github.com/python/cpython
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 <10796600+picnixz@users.noreply.github.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
4aed319a8e
commit
f79ffc879b
|
@ -1004,9 +1004,8 @@ are always available. They are listed here in alphabetical order.
|
||||||
115
|
115
|
||||||
|
|
||||||
If the argument defines :meth:`~object.__int__`,
|
If the argument defines :meth:`~object.__int__`,
|
||||||
``int(x)`` returns ``x.__int__()``. If the argument defines :meth:`~object.__index__`,
|
``int(x)`` returns ``x.__int__()``. If the argument defines
|
||||||
it returns ``x.__index__()``. If the argument defines :meth:`~object.__trunc__`,
|
:meth:`~object.__index__`, it returns ``x.__index__()``.
|
||||||
it returns ``x.__trunc__()``.
|
|
||||||
For floating point numbers, this truncates towards zero.
|
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,
|
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
|
.. versionchanged:: 3.8
|
||||||
Falls back to :meth:`~object.__index__` if :meth:`~object.__int__` is not defined.
|
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
|
.. versionchanged:: 3.11
|
||||||
:class:`int` string inputs and string representations can be limited to
|
:class:`int` string inputs and string representations can be limited to
|
||||||
help avoid denial of service attacks. A :exc:`ValueError` is raised when
|
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
|
See the :ref:`integer string conversion length limitation
|
||||||
<int_max_str_digits>` documentation.
|
<int_max_str_digits>` documentation.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.14
|
||||||
|
:func:`int` no longer delegates to the :meth:`~object.__trunc__` method.
|
||||||
|
|
||||||
.. function:: isinstance(object, classinfo)
|
.. function:: isinstance(object, classinfo)
|
||||||
|
|
||||||
Return ``True`` if the *object* argument is an instance of the *classinfo*
|
Return ``True`` if the *object* argument is an instance of the *classinfo*
|
||||||
|
|
|
@ -3127,11 +3127,8 @@ left undefined.
|
||||||
return the value of the object truncated to an :class:`~numbers.Integral`
|
return the value of the object truncated to an :class:`~numbers.Integral`
|
||||||
(typically an :class:`int`).
|
(typically an :class:`int`).
|
||||||
|
|
||||||
The built-in function :func:`int` falls back to :meth:`__trunc__` if neither
|
.. versionchanged:: 3.14
|
||||||
:meth:`__int__` nor :meth:`__index__` is defined.
|
:func:`int` no longer delegates to the :meth:`~object.__trunc__` method.
|
||||||
|
|
||||||
.. versionchanged:: 3.11
|
|
||||||
The delegation of :func:`int` to :meth:`__trunc__` is deprecated.
|
|
||||||
|
|
||||||
|
|
||||||
.. _context-managers:
|
.. _context-managers:
|
||||||
|
|
|
@ -225,6 +225,11 @@ Others
|
||||||
It had previously raised a :exc:`DeprecationWarning` since Python 3.9. (Contributed
|
It had previously raised a :exc:`DeprecationWarning` since Python 3.9. (Contributed
|
||||||
by Jelle Zijlstra in :gh:`118767`.)
|
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
|
Porting to Python 3.14
|
||||||
======================
|
======================
|
||||||
|
|
|
@ -732,7 +732,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__trunc__));
|
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_is_unpacked_typevartuple__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_is_unpacked_typevartuple__));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__));
|
||||||
|
|
|
@ -221,7 +221,6 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(__subclasscheck__)
|
STRUCT_FOR_ID(__subclasscheck__)
|
||||||
STRUCT_FOR_ID(__subclasshook__)
|
STRUCT_FOR_ID(__subclasshook__)
|
||||||
STRUCT_FOR_ID(__truediv__)
|
STRUCT_FOR_ID(__truediv__)
|
||||||
STRUCT_FOR_ID(__trunc__)
|
|
||||||
STRUCT_FOR_ID(__type_params__)
|
STRUCT_FOR_ID(__type_params__)
|
||||||
STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__)
|
STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__)
|
||||||
STRUCT_FOR_ID(__typing_prepare_subst__)
|
STRUCT_FOR_ID(__typing_prepare_subst__)
|
||||||
|
|
|
@ -730,7 +730,6 @@ extern "C" {
|
||||||
INIT_ID(__subclasscheck__), \
|
INIT_ID(__subclasscheck__), \
|
||||||
INIT_ID(__subclasshook__), \
|
INIT_ID(__subclasshook__), \
|
||||||
INIT_ID(__truediv__), \
|
INIT_ID(__truediv__), \
|
||||||
INIT_ID(__trunc__), \
|
|
||||||
INIT_ID(__type_params__), \
|
INIT_ID(__type_params__), \
|
||||||
INIT_ID(__typing_is_unpacked_typevartuple__), \
|
INIT_ID(__typing_is_unpacked_typevartuple__), \
|
||||||
INIT_ID(__typing_prepare_subst__), \
|
INIT_ID(__typing_prepare_subst__), \
|
||||||
|
|
|
@ -504,9 +504,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(__truediv__);
|
string = &_Py_ID(__truediv__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(__trunc__);
|
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
|
||||||
string = &_Py_ID(__type_params__);
|
string = &_Py_ID(__type_params__);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
|
|
@ -402,68 +402,8 @@ class IntTestCases(unittest.TestCase):
|
||||||
class JustTrunc(base):
|
class JustTrunc(base):
|
||||||
def __trunc__(self):
|
def __trunc__(self):
|
||||||
return 42
|
return 42
|
||||||
with self.assertWarns(DeprecationWarning):
|
with self.assertRaises(TypeError):
|
||||||
self.assertEqual(int(JustTrunc()), 42)
|
int(JustTrunc())
|
||||||
|
|
||||||
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())
|
|
||||||
|
|
||||||
def test_int_subclass_with_index(self):
|
def test_int_subclass_with_index(self):
|
||||||
class MyIndex(int):
|
class MyIndex(int):
|
||||||
|
@ -514,18 +454,6 @@ class IntTestCases(unittest.TestCase):
|
||||||
def __int__(self):
|
def __int__(self):
|
||||||
return True
|
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()
|
bad_int = BadIndex()
|
||||||
with self.assertWarns(DeprecationWarning):
|
with self.assertWarns(DeprecationWarning):
|
||||||
n = int(bad_int)
|
n = int(bad_int)
|
||||||
|
@ -549,26 +477,6 @@ class IntTestCases(unittest.TestCase):
|
||||||
self.assertEqual(n, 1)
|
self.assertEqual(n, 1)
|
||||||
self.assertIs(type(n), int)
|
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 test_error_message(self):
|
||||||
def check(s, base=None):
|
def check(s, base=None):
|
||||||
with self.assertRaises(ValueError,
|
with self.assertRaises(ValueError,
|
||||||
|
|
|
@ -386,15 +386,6 @@ class LongTest(unittest.TestCase):
|
||||||
return 42
|
return 42
|
||||||
self.assertRaises(TypeError, int, JustLong())
|
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):
|
def check_float_conversion(self, n):
|
||||||
# Check that int -> float conversion behaviour matches
|
# Check that int -> float conversion behaviour matches
|
||||||
# that of the pure Python version above.
|
# that of the pure Python version above.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Remove the previously-deprecated delegation of :func:`int` to
|
||||||
|
:meth:`~object.__trunc__`.
|
|
@ -1521,7 +1521,6 @@ PyNumber_Long(PyObject *o)
|
||||||
{
|
{
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
PyNumberMethods *m;
|
PyNumberMethods *m;
|
||||||
PyObject *trunc_func;
|
|
||||||
Py_buffer view;
|
Py_buffer view;
|
||||||
|
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
|
@ -1563,37 +1562,6 @@ PyNumber_Long(PyObject *o)
|
||||||
if (m && m->nb_index) {
|
if (m && m->nb_index) {
|
||||||
return PyNumber_Index(o);
|
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))
|
if (PyUnicode_Check(o))
|
||||||
/* The below check is done in PyLong_FromUnicodeObject(). */
|
/* The below check is done in PyLong_FromUnicodeObject(). */
|
||||||
|
|
Loading…
Reference in New Issue