Skip to content

Commit 578c395

Browse files
bpo-37999: No longer use __int__ in implicit integer conversions. (GH-15636)
Only __index__ should be used to make integer conversions lossless.
1 parent 8ad0524 commit 578c395

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+226
-2937
lines changed

Doc/whatsnew/3.10.rst

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ New Features
7575
Other Language Changes
7676
======================
7777

78+
* Builtin and extension functions that take integer arguments no longer accept
79+
:class:`~decimal.Decimal`\ s, :class:`~fractions.Fraction`\ s and other
80+
objects that can be converted to integers only with a loss (e.g. that have
81+
the :meth:`~object.__int__` method but do not have the
82+
:meth:`~object.__index__` method).
83+
(Contributed by Serhiy Storchaka in :issue:`37999`.)
7884

7985

8086
New Modules

Include/longobject.h

-17
Original file line numberDiff line numberDiff line change
@@ -173,23 +173,6 @@ PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
173173
unsigned char* bytes, size_t n,
174174
int little_endian, int is_signed);
175175

176-
/* _PyLong_FromNbInt: Convert the given object to a PyLongObject
177-
using the nb_int slot, if available. Raise TypeError if either the
178-
nb_int slot is not available or the result of the call to nb_int
179-
returns something not of type int.
180-
*/
181-
PyAPI_FUNC(PyObject *) _PyLong_FromNbInt(PyObject *);
182-
183-
/* Convert the given object to a PyLongObject using the nb_index or
184-
nb_int slots, if available (the latter is deprecated).
185-
Raise TypeError if either nb_index and nb_int slots are not
186-
available or the result of the call to nb_index or nb_int
187-
returns something not of type int.
188-
Should be replaced with PyNumber_Index after the end of the
189-
deprecation period.
190-
*/
191-
PyAPI_FUNC(PyObject *) _PyLong_FromNbIndexOrNbInt(PyObject *);
192-
193176
/* _PyLong_Format: Convert the long to a string object with given base,
194177
appending a base prefix of 0[box] if base is 2, 8 or 16. */
195178
PyAPI_FUNC(PyObject *) _PyLong_Format(PyObject *obj, int base);

Lib/ctypes/test/test_numbers.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ def __index__(self):
134134
for t in signed_types + unsigned_types:
135135
self.assertRaises(TypeError, t, 3.14)
136136
self.assertRaises(TypeError, t, f)
137-
with self.assertWarns(DeprecationWarning):
138-
self.assertEqual(t(d).value, 2)
137+
self.assertRaises(TypeError, t, d)
139138
self.assertEqual(t(i).value, 2)
140139

141140
def test_sizes(self):

Lib/datetime.py

+10-41
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import time as _time
1212
import math as _math
1313
import sys
14+
from operator import index as _index
1415

1516
def _cmp(x, y):
1617
return 0 if x == y else 1 if x > y else -1
@@ -380,42 +381,10 @@ def _check_utc_offset(name, offset):
380381
"-timedelta(hours=24) and timedelta(hours=24)" %
381382
(name, offset))
382383

383-
def _check_int_field(value):
384-
if isinstance(value, int):
385-
return value
386-
if isinstance(value, float):
387-
raise TypeError('integer argument expected, got float')
388-
try:
389-
value = value.__index__()
390-
except AttributeError:
391-
pass
392-
else:
393-
if not isinstance(value, int):
394-
raise TypeError('__index__ returned non-int (type %s)' %
395-
type(value).__name__)
396-
return value
397-
orig = value
398-
try:
399-
value = value.__int__()
400-
except AttributeError:
401-
pass
402-
else:
403-
if not isinstance(value, int):
404-
raise TypeError('__int__ returned non-int (type %s)' %
405-
type(value).__name__)
406-
import warnings
407-
warnings.warn("an integer is required (got type %s)" %
408-
type(orig).__name__,
409-
DeprecationWarning,
410-
stacklevel=2)
411-
return value
412-
raise TypeError('an integer is required (got type %s)' %
413-
type(value).__name__)
414-
415384
def _check_date_fields(year, month, day):
416-
year = _check_int_field(year)
417-
month = _check_int_field(month)
418-
day = _check_int_field(day)
385+
year = _index(year)
386+
month = _index(month)
387+
day = _index(day)
419388
if not MINYEAR <= year <= MAXYEAR:
420389
raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
421390
if not 1 <= month <= 12:
@@ -426,10 +395,10 @@ def _check_date_fields(year, month, day):
426395
return year, month, day
427396

428397
def _check_time_fields(hour, minute, second, microsecond, fold):
429-
hour = _check_int_field(hour)
430-
minute = _check_int_field(minute)
431-
second = _check_int_field(second)
432-
microsecond = _check_int_field(microsecond)
398+
hour = _index(hour)
399+
minute = _index(minute)
400+
second = _index(second)
401+
microsecond = _index(microsecond)
433402
if not 0 <= hour <= 23:
434403
raise ValueError('hour must be in 0..23', hour)
435404
if not 0 <= minute <= 59:
@@ -2539,10 +2508,10 @@ def _name_from_offset(delta):
25392508
# Clean up unused names
25402509
del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y,
25412510
_DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time,
2542-
_check_date_fields, _check_int_field, _check_time_fields,
2511+
_check_date_fields, _check_time_fields,
25432512
_check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
25442513
_date_class, _days_before_month, _days_before_year, _days_in_month,
2545-
_format_time, _format_offset, _is_leap, _isoweek1monday, _math,
2514+
_format_time, _format_offset, _index, _is_leap, _isoweek1monday, _math,
25462515
_ord2ymd, _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord,
25472516
_divide_and_round, _parse_isoformat_date, _parse_isoformat_time,
25482517
_parse_hh_mm_ss_ff, _IsoCalendarDate)

Lib/test/clinic.test

+11-81
Original file line numberDiff line numberDiff line change
@@ -418,11 +418,6 @@ test_bool_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
418418
if (nargs < 3) {
419419
goto skip_optional;
420420
}
421-
if (PyFloat_Check(args[2])) {
422-
PyErr_SetString(PyExc_TypeError,
423-
"integer argument expected, got float" );
424-
goto exit;
425-
}
426421
c = _PyLong_AsInt(args[2]);
427422
if (c == -1 && PyErr_Occurred()) {
428423
goto exit;
@@ -436,7 +431,7 @@ exit:
436431

437432
static PyObject *
438433
test_bool_converter_impl(PyObject *module, int a, int b, int c)
439-
/*[clinic end generated code: output=25f20963894256a1 input=939854fa9f248c60]*/
434+
/*[clinic end generated code: output=b5ec6409d942e0f9 input=939854fa9f248c60]*/
440435

441436

442437
/*[clinic input]
@@ -729,11 +724,6 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t
729724
if (nargs < 1) {
730725
goto skip_optional;
731726
}
732-
if (PyFloat_Check(args[0])) {
733-
PyErr_SetString(PyExc_TypeError,
734-
"integer argument expected, got float" );
735-
goto exit;
736-
}
737727
{
738728
long ival = PyLong_AsLong(args[0]);
739729
if (ival == -1 && PyErr_Occurred()) {
@@ -756,11 +746,6 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t
756746
if (nargs < 2) {
757747
goto skip_optional;
758748
}
759-
if (PyFloat_Check(args[1])) {
760-
PyErr_SetString(PyExc_TypeError,
761-
"integer argument expected, got float" );
762-
goto exit;
763-
}
764749
{
765750
long ival = PyLong_AsLong(args[1]);
766751
if (ival == -1 && PyErr_Occurred()) {
@@ -783,14 +768,9 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t
783768
if (nargs < 3) {
784769
goto skip_optional;
785770
}
786-
if (PyFloat_Check(args[2])) {
787-
PyErr_SetString(PyExc_TypeError,
788-
"integer argument expected, got float" );
789-
goto exit;
790-
}
791771
{
792-
long ival = PyLong_AsUnsignedLongMask(args[2]);
793-
if (ival == -1 && PyErr_Occurred()) {
772+
unsigned long ival = PyLong_AsUnsignedLongMask(args[2]);
773+
if (ival == (unsigned long)-1 && PyErr_Occurred()) {
794774
goto exit;
795775
}
796776
else {
@@ -807,7 +787,7 @@ exit:
807787
static PyObject *
808788
test_unsigned_char_converter_impl(PyObject *module, unsigned char a,
809789
unsigned char b, unsigned char c)
810-
/*[clinic end generated code: output=ebf905c5c9414762 input=021414060993e289]*/
790+
/*[clinic end generated code: output=c0a6ab3144481466 input=021414060993e289]*/
811791

812792

813793
/*[clinic input]
@@ -841,11 +821,6 @@ test_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
841821
if (nargs < 1) {
842822
goto skip_optional;
843823
}
844-
if (PyFloat_Check(args[0])) {
845-
PyErr_SetString(PyExc_TypeError,
846-
"integer argument expected, got float" );
847-
goto exit;
848-
}
849824
{
850825
long ival = PyLong_AsLong(args[0]);
851826
if (ival == -1 && PyErr_Occurred()) {
@@ -874,7 +849,7 @@ exit:
874849

875850
static PyObject *
876851
test_short_converter_impl(PyObject *module, short a)
877-
/*[clinic end generated code: output=86fe1a1496a7ff20 input=6a8a7a509a498ff4]*/
852+
/*[clinic end generated code: output=3ccda4bd08b6e4b4 input=6a8a7a509a498ff4]*/
878853

879854

880855
/*[clinic input]
@@ -925,11 +900,6 @@ test_unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_
925900
if (nargs < 3) {
926901
goto skip_optional;
927902
}
928-
if (PyFloat_Check(args[2])) {
929-
PyErr_SetString(PyExc_TypeError,
930-
"integer argument expected, got float" );
931-
goto exit;
932-
}
933903
c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]);
934904
if (c == (unsigned short)-1 && PyErr_Occurred()) {
935905
goto exit;
@@ -944,7 +914,7 @@ exit:
944914
static PyObject *
945915
test_unsigned_short_converter_impl(PyObject *module, unsigned short a,
946916
unsigned short b, unsigned short c)
947-
/*[clinic end generated code: output=3779fe104319e3ae input=cdfd8eff3d9176b4]*/
917+
/*[clinic end generated code: output=576b5ce48424f351 input=cdfd8eff3d9176b4]*/
948918

949919

950920
/*[clinic input]
@@ -984,23 +954,13 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
984954
if (nargs < 1) {
985955
goto skip_optional;
986956
}
987-
if (PyFloat_Check(args[0])) {
988-
PyErr_SetString(PyExc_TypeError,
989-
"integer argument expected, got float" );
990-
goto exit;
991-
}
992957
a = _PyLong_AsInt(args[0]);
993958
if (a == -1 && PyErr_Occurred()) {
994959
goto exit;
995960
}
996961
if (nargs < 2) {
997962
goto skip_optional;
998963
}
999-
if (PyFloat_Check(args[1])) {
1000-
PyErr_SetString(PyExc_TypeError,
1001-
"integer argument expected, got float" );
1002-
goto exit;
1003-
}
1004964
b = _PyLong_AsInt(args[1]);
1005965
if (b == -1 && PyErr_Occurred()) {
1006966
goto exit;
@@ -1023,11 +983,6 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
1023983
if (nargs < 4) {
1024984
goto skip_optional;
1025985
}
1026-
if (PyFloat_Check(args[3])) {
1027-
PyErr_SetString(PyExc_TypeError,
1028-
"integer argument expected, got float" );
1029-
goto exit;
1030-
}
1031986
d = _PyLong_AsInt(args[3]);
1032987
if (d == -1 && PyErr_Occurred()) {
1033988
goto exit;
@@ -1041,7 +996,7 @@ exit:
1041996

1042997
static PyObject *
1043998
test_int_converter_impl(PyObject *module, int a, int b, int c, myenum d)
1044-
/*[clinic end generated code: output=10a2e48a34af5d7a input=d20541fc1ca0553e]*/
999+
/*[clinic end generated code: output=8a1a7b02ebe9eeac input=d20541fc1ca0553e]*/
10451000

10461001

10471002
/*[clinic input]
@@ -1092,11 +1047,6 @@ test_unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t
10921047
if (nargs < 3) {
10931048
goto skip_optional;
10941049
}
1095-
if (PyFloat_Check(args[2])) {
1096-
PyErr_SetString(PyExc_TypeError,
1097-
"integer argument expected, got float" );
1098-
goto exit;
1099-
}
11001050
c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]);
11011051
if (c == (unsigned int)-1 && PyErr_Occurred()) {
11021052
goto exit;
@@ -1111,7 +1061,7 @@ exit:
11111061
static PyObject *
11121062
test_unsigned_int_converter_impl(PyObject *module, unsigned int a,
11131063
unsigned int b, unsigned int c)
1114-
/*[clinic end generated code: output=189176ce67c7d2e7 input=5533534828b62fc0]*/
1064+
/*[clinic end generated code: output=4f53904bfa1a0250 input=5533534828b62fc0]*/
11151065

11161066

11171067
/*[clinic input]
@@ -1145,11 +1095,6 @@ test_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
11451095
if (nargs < 1) {
11461096
goto skip_optional;
11471097
}
1148-
if (PyFloat_Check(args[0])) {
1149-
PyErr_SetString(PyExc_TypeError,
1150-
"integer argument expected, got float" );
1151-
goto exit;
1152-
}
11531098
a = PyLong_AsLong(args[0]);
11541099
if (a == -1 && PyErr_Occurred()) {
11551100
goto exit;
@@ -1163,7 +1108,7 @@ exit:
11631108

11641109
static PyObject *
11651110
test_long_converter_impl(PyObject *module, long a)
1166-
/*[clinic end generated code: output=44cd8823f59d116b input=d2179e3c9cdcde89]*/
1111+
/*[clinic end generated code: output=e5e7883fddcf4218 input=d2179e3c9cdcde89]*/
11671112

11681113

11691114
/*[clinic input]
@@ -1263,11 +1208,6 @@ test_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nar
12631208
if (nargs < 1) {
12641209
goto skip_optional;
12651210
}
1266-
if (PyFloat_Check(args[0])) {
1267-
PyErr_SetString(PyExc_TypeError,
1268-
"integer argument expected, got float" );
1269-
goto exit;
1270-
}
12711211
a = PyLong_AsLongLong(args[0]);
12721212
if (a == -1 && PyErr_Occurred()) {
12731213
goto exit;
@@ -1281,7 +1221,7 @@ exit:
12811221

12821222
static PyObject *
12831223
test_long_long_converter_impl(PyObject *module, long long a)
1284-
/*[clinic end generated code: output=7143b585d7e433e8 input=d5fc81577ff4dd02]*/
1224+
/*[clinic end generated code: output=0488ac9e8c1d77bb input=d5fc81577ff4dd02]*/
12851225

12861226

12871227
/*[clinic input]
@@ -1390,11 +1330,6 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
13901330
if (nargs < 1) {
13911331
goto skip_optional;
13921332
}
1393-
if (PyFloat_Check(args[0])) {
1394-
PyErr_SetString(PyExc_TypeError,
1395-
"integer argument expected, got float" );
1396-
goto exit;
1397-
}
13981333
{
13991334
Py_ssize_t ival = -1;
14001335
PyObject *iobj = PyNumber_Index(args[0]);
@@ -1410,11 +1345,6 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
14101345
if (nargs < 2) {
14111346
goto skip_optional;
14121347
}
1413-
if (PyFloat_Check(args[1])) {
1414-
PyErr_SetString(PyExc_TypeError,
1415-
"integer argument expected, got float" );
1416-
goto exit;
1417-
}
14181348
{
14191349
Py_ssize_t ival = -1;
14201350
PyObject *iobj = PyNumber_Index(args[1]);
@@ -1443,7 +1373,7 @@ exit:
14431373
static PyObject *
14441374
test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b,
14451375
Py_ssize_t c)
1446-
/*[clinic end generated code: output=a46d2aaf40c10398 input=3855f184bb3f299d]*/
1376+
/*[clinic end generated code: output=ea781bb7169b3436 input=3855f184bb3f299d]*/
14471377

14481378

14491379
/*[clinic input]

0 commit comments

Comments
 (0)