Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6a44f6e

Browse filesBrowse files
bpo-36048: Use __index__() instead of __int__() for implicit conversion if available. (GH-11952)
Deprecate using the __int__() method in implicit conversions of Python numbers to C integers.
1 parent d90a141 commit 6a44f6e
Copy full SHA for 6a44f6e

File tree

Expand file treeCollapse file tree

18 files changed

+326
-55
lines changed
Filter options
Expand file treeCollapse file tree

18 files changed

+326
-55
lines changed

‎Doc/c-api/long.rst

Copy file name to clipboardExpand all lines: Doc/c-api/long.rst
+54-12Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,28 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
131131
single: OverflowError (built-in exception)
132132
133133
Return a C :c:type:`long` representation of *obj*. If *obj* is not an
134-
instance of :c:type:`PyLongObject`, first call its :meth:`__int__` method
135-
(if present) to convert it to a :c:type:`PyLongObject`.
134+
instance of :c:type:`PyLongObject`, first call its :meth:`__index__` or
135+
:meth:`__int__` method (if present) to convert it to a
136+
:c:type:`PyLongObject`.
136137
137138
Raise :exc:`OverflowError` if the value of *obj* is out of range for a
138139
:c:type:`long`.
139140
140141
Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
141142
143+
.. versionchanged:: 3.8
144+
Use :meth:`__index__` if available.
145+
146+
.. deprecated:: 3.8
147+
Using :meth:`__int__` is deprecated.
148+
142149
143150
.. c:function:: long PyLong_AsLongAndOverflow(PyObject *obj, int *overflow)
144151
145152
Return a C :c:type:`long` representation of *obj*. If *obj* is not an
146-
instance of :c:type:`PyLongObject`, first call its :meth:`__int__` method
147-
(if present) to convert it to a :c:type:`PyLongObject`.
153+
instance of :c:type:`PyLongObject`, first call its :meth:`__index__` or
154+
:meth:`__int__` method (if present) to convert it to a
155+
:c:type:`PyLongObject`.
148156
149157
If the value of *obj* is greater than :const:`LONG_MAX` or less than
150158
:const:`LONG_MIN`, set *\*overflow* to ``1`` or ``-1``, respectively, and
@@ -153,27 +161,41 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
153161
154162
Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
155163
164+
.. versionchanged:: 3.8
165+
Use :meth:`__index__` if available.
166+
167+
.. deprecated:: 3.8
168+
Using :meth:`__int__` is deprecated.
169+
156170
157171
.. c:function:: long long PyLong_AsLongLong(PyObject *obj)
158172
159173
.. index::
160174
single: OverflowError (built-in exception)
161175
162176
Return a C :c:type:`long long` representation of *obj*. If *obj* is not an
163-
instance of :c:type:`PyLongObject`, first call its :meth:`__int__` method
164-
(if present) to convert it to a :c:type:`PyLongObject`.
177+
instance of :c:type:`PyLongObject`, first call its :meth:`__index__` or
178+
:meth:`__int__` method (if present) to convert it to a
179+
:c:type:`PyLongObject`.
165180
166181
Raise :exc:`OverflowError` if the value of *obj* is out of range for a
167182
:c:type:`long`.
168183
169184
Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
170185
186+
.. versionchanged:: 3.8
187+
Use :meth:`__index__` if available.
188+
189+
.. deprecated:: 3.8
190+
Using :meth:`__int__` is deprecated.
191+
171192
172193
.. c:function:: long long PyLong_AsLongLongAndOverflow(PyObject *obj, int *overflow)
173194
174195
Return a C :c:type:`long long` representation of *obj*. If *obj* is not an
175-
instance of :c:type:`PyLongObject`, first call its :meth:`__int__` method
176-
(if present) to convert it to a :c:type:`PyLongObject`.
196+
instance of :c:type:`PyLongObject`, first call its :meth:`__index__` or
197+
:meth:`__int__` method (if present) to convert it to a
198+
:c:type:`PyLongObject`.
177199
178200
If the value of *obj* is greater than :const:`PY_LLONG_MAX` or less than
179201
:const:`PY_LLONG_MIN`, set *\*overflow* to ``1`` or ``-1``, respectively,
@@ -184,6 +206,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
184206
185207
.. versionadded:: 3.2
186208
209+
.. versionchanged:: 3.8
210+
Use :meth:`__index__` if available.
211+
212+
.. deprecated:: 3.8
213+
Using :meth:`__int__` is deprecated.
214+
187215
188216
.. c:function:: Py_ssize_t PyLong_AsSsize_t(PyObject *pylong)
189217
@@ -253,26 +281,40 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
253281
.. c:function:: unsigned long PyLong_AsUnsignedLongMask(PyObject *obj)
254282
255283
Return a C :c:type:`unsigned long` representation of *obj*. If *obj*
256-
is not an instance of :c:type:`PyLongObject`, first call its :meth:`__int__`
257-
method (if present) to convert it to a :c:type:`PyLongObject`.
284+
is not an instance of :c:type:`PyLongObject`, first call its
285+
:meth:`__index__` or :meth:`__int__` method (if present) to convert
286+
it to a :c:type:`PyLongObject`.
258287
259288
If the value of *obj* is out of range for an :c:type:`unsigned long`,
260289
return the reduction of that value modulo ``ULONG_MAX + 1``.
261290
262291
Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
263292
293+
.. versionchanged:: 3.8
294+
Use :meth:`__index__` if available.
295+
296+
.. deprecated:: 3.8
297+
Using :meth:`__int__` is deprecated.
298+
264299
265300
.. c:function:: unsigned long long PyLong_AsUnsignedLongLongMask(PyObject *obj)
266301
267302
Return a C :c:type:`unsigned long long` representation of *obj*. If *obj*
268-
is not an instance of :c:type:`PyLongObject`, first call its :meth:`__int__`
269-
method (if present) to convert it to a :c:type:`PyLongObject`.
303+
is not an instance of :c:type:`PyLongObject`, first call its
304+
:meth:`__index__` or :meth:`__int__` method (if present) to convert
305+
it to a :c:type:`PyLongObject`.
270306
271307
If the value of *obj* is out of range for an :c:type:`unsigned long long`,
272308
return the reduction of that value modulo ``PY_ULLONG_MAX + 1``.
273309
274310
Returns ``-1`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
275311
312+
.. versionchanged:: 3.8
313+
Use :meth:`__index__` if available.
314+
315+
.. deprecated:: 3.8
316+
Using :meth:`__int__` is deprecated.
317+
276318
277319
.. c:function:: double PyLong_AsDouble(PyObject *pylong)
278320

‎Doc/c-api/number.rst

Copy file name to clipboardExpand all lines: Doc/c-api/number.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ Number Protocol
1111
Returns ``1`` if the object *o* provides numeric protocols, and false otherwise.
1212
This function always succeeds.
1313
14+
.. versionchanged:: 3.8
15+
Returns ``1`` if *o* is an index integer.
16+
1417
1518
.. c:function:: PyObject* PyNumber_Add(PyObject *o1, PyObject *o2)
1619

‎Doc/whatsnew/3.8.rst

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.8.rst
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,17 @@ Build and C API Changes
458458

459459
(Contributed by Antoine Pitrou in :issue:`32430`.)
460460

461+
* Functions that convert Python number to C integer like
462+
:c:func:`PyLong_AsLong` and argument parsing functions like
463+
:c:func:`PyArg_ParseTuple` with integer converting format units like ``'i'``
464+
will now use the :meth:`~object.__index__` special method instead of
465+
:meth:`~object.__int__`, if available. The deprecation warning will be
466+
emitted for objects with the ``__int__()`` method but without the
467+
``__index__()`` method (like :class:`~decimal.Decimal` and
468+
:class:`~fractions.Fraction`). :c:func:`PyNumber_Check` will now return
469+
``1`` for objects implementing ``__index__()``.
470+
(Contributed by Serhiy Storchaka in :issue:`36048`.)
471+
461472

462473
Deprecated
463474
==========
@@ -508,6 +519,15 @@ Deprecated
508519
* The :meth:`~threading.Thread.isAlive()` method of :class:`threading.Thread` has been deprecated.
509520
(Contributed by Dong-hee Na in :issue:`35283`.)
510521

522+
* Many builtin and extension functions that take integer arguments will
523+
now emit a deprecation warning for :class:`~decimal.Decimal`\ s,
524+
:class:`~fractions.Fraction`\ s and any other objects that can be converted
525+
to integers only with a loss (e.g. that have the :meth:`~object.__int__`
526+
method but do not have the :meth:`~object.__index__` method). In future
527+
version they will be errors.
528+
(Contributed by Serhiy Storchaka in :issue:`36048`.)
529+
530+
511531
API and Feature Removals
512532
========================
513533

‎Include/longobject.h

Copy file name to clipboardExpand all lines: Include/longobject.h
+11-1Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,17 @@ PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
177177
nb_int slot is not available or the result of the call to nb_int
178178
returns something not of type int.
179179
*/
180-
PyAPI_FUNC(PyLongObject *)_PyLong_FromNbInt(PyObject *);
180+
PyAPI_FUNC(PyObject *) _PyLong_FromNbInt(PyObject *);
181+
182+
/* Convert the given object to a PyLongObject using the nb_index or
183+
nb_int slots, if available (the latter is deprecated).
184+
Raise TypeError if either nb_index and nb_int slots are not
185+
available or the result of the call to nb_index or nb_int
186+
returns something not of type int.
187+
Should be replaced with PyNumber_Index after the end of the
188+
deprecation period.
189+
*/
190+
PyAPI_FUNC(PyObject *) _PyLong_FromNbIndexOrNbInt(PyObject *);
181191

182192
/* _PyLong_Format: Convert the long to a string object with given base,
183193
appending a base prefix of 0[box] if base is 2, 8 or 16. */

‎Lib/ctypes/test/test_numbers.py

Copy file name to clipboardExpand all lines: Lib/ctypes/test/test_numbers.py
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,18 @@ def __float__(self):
124124
class IntLike(object):
125125
def __int__(self):
126126
return 2
127-
i = IntLike()
127+
d = IntLike()
128+
class IndexLike(object):
129+
def __index__(self):
130+
return 2
131+
i = IndexLike()
128132
# integers cannot be constructed from floats,
129133
# but from integer-like objects
130134
for t in signed_types + unsigned_types:
131135
self.assertRaises(TypeError, t, 3.14)
132136
self.assertRaises(TypeError, t, f)
137+
with self.assertWarns(DeprecationWarning):
138+
self.assertEqual(t(d).value, 2)
133139
self.assertEqual(t(i).value, 2)
134140

135141
def test_sizes(self):

‎Lib/datetime.py

Copy file name to clipboardExpand all lines: Lib/datetime.py
+26-11Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -379,19 +379,34 @@ def _check_utc_offset(name, offset):
379379
def _check_int_field(value):
380380
if isinstance(value, int):
381381
return value
382-
if not isinstance(value, float):
383-
try:
384-
value = value.__int__()
385-
except AttributeError:
386-
pass
387-
else:
388-
if isinstance(value, int):
389-
return value
382+
if isinstance(value, float):
383+
raise TypeError('integer argument expected, got float')
384+
try:
385+
value = value.__index__()
386+
except AttributeError:
387+
pass
388+
else:
389+
if not isinstance(value, int):
390+
raise TypeError('__index__ returned non-int (type %s)' %
391+
type(value).__name__)
392+
return value
393+
orig = value
394+
try:
395+
value = value.__int__()
396+
except AttributeError:
397+
pass
398+
else:
399+
if not isinstance(value, int):
390400
raise TypeError('__int__ returned non-int (type %s)' %
391401
type(value).__name__)
392-
raise TypeError('an integer is required (got type %s)' %
393-
type(value).__name__)
394-
raise TypeError('integer argument expected, got float')
402+
import warnings
403+
warnings.warn("an integer is required (got type %s)" %
404+
type(orig).__name__,
405+
DeprecationWarning,
406+
stacklevel=2)
407+
return value
408+
raise TypeError('an integer is required (got type %s)' %
409+
type(value).__name__)
395410

396411
def _check_date_fields(year, month, day):
397412
year = _check_int_field(year)

‎Lib/test/datetimetester.py

Copy file name to clipboardExpand all lines: Lib/test/datetimetester.py
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4918,16 +4918,17 @@ def __int__(self):
49184918
for xx in [decimal.Decimal(10),
49194919
decimal.Decimal('10.9'),
49204920
Number(10)]:
4921-
self.assertEqual(datetime(10, 10, 10, 10, 10, 10, 10),
4922-
datetime(xx, xx, xx, xx, xx, xx, xx))
4921+
with self.assertWarns(DeprecationWarning):
4922+
self.assertEqual(datetime(10, 10, 10, 10, 10, 10, 10),
4923+
datetime(xx, xx, xx, xx, xx, xx, xx))
49234924

49244925
with self.assertRaisesRegex(TypeError, '^an integer is required '
49254926
r'\(got type str\)$'):
49264927
datetime(10, 10, '10')
49274928

49284929
f10 = Number(10.9)
49294930
with self.assertRaisesRegex(TypeError, '^__int__ returned non-int '
4930-
r'\(type float\)$'):
4931+
r'\(type float\)$'):
49314932
datetime(10, 10, f10)
49324933

49334934
class Float(float):

‎Lib/test/test_array.py

Copy file name to clipboardExpand all lines: Lib/test/test_array.py
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,8 @@ def test_type_error(self):
12421242
class Intable:
12431243
def __init__(self, num):
12441244
self._num = num
1245+
def __index__(self):
1246+
return self._num
12451247
def __int__(self):
12461248
return self._num
12471249
def __sub__(self, other):

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.