Skip to content

bpo-37999: No longer use __int__ in implicit integer conversions. #15636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ New Features
Other Language Changes
======================

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


New Modules
Expand Down
17 changes: 0 additions & 17 deletions Include/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,23 +173,6 @@ PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
unsigned char* bytes, size_t n,
int little_endian, int is_signed);

/* _PyLong_FromNbInt: Convert the given object to a PyLongObject
using the nb_int slot, if available. Raise TypeError if either the
nb_int slot is not available or the result of the call to nb_int
returns something not of type int.
*/
PyAPI_FUNC(PyObject *) _PyLong_FromNbInt(PyObject *);

/* Convert the given object to a PyLongObject using the nb_index or
nb_int slots, if available (the latter is deprecated).
Raise TypeError if either nb_index and nb_int slots are not
available or the result of the call to nb_index or nb_int
returns something not of type int.
Should be replaced with PyNumber_Index after the end of the
deprecation period.
*/
PyAPI_FUNC(PyObject *) _PyLong_FromNbIndexOrNbInt(PyObject *);

/* _PyLong_Format: Convert the long to a string object with given base,
appending a base prefix of 0[box] if base is 2, 8 or 16. */
PyAPI_FUNC(PyObject *) _PyLong_Format(PyObject *obj, int base);
Expand Down
3 changes: 1 addition & 2 deletions Lib/ctypes/test/test_numbers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ def __index__(self):
for t in signed_types + unsigned_types:
self.assertRaises(TypeError, t, 3.14)
self.assertRaises(TypeError, t, f)
with self.assertWarns(DeprecationWarning):
self.assertEqual(t(d).value, 2)
self.assertRaises(TypeError, t, d)
self.assertEqual(t(i).value, 2)

def test_sizes(self):
Expand Down
51 changes: 10 additions & 41 deletions Lib/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import time as _time
import math as _math
import sys
from operator import index as _index

def _cmp(x, y):
return 0 if x == y else 1 if x > y else -1
Expand Down Expand Up @@ -380,42 +381,10 @@ def _check_utc_offset(name, offset):
"-timedelta(hours=24) and timedelta(hours=24)" %
(name, offset))

def _check_int_field(value):
if isinstance(value, int):
return value
if isinstance(value, float):
raise TypeError('integer argument expected, got float')
try:
value = value.__index__()
except AttributeError:
pass
else:
if not isinstance(value, int):
raise TypeError('__index__ returned non-int (type %s)' %
type(value).__name__)
return value
orig = value
try:
value = value.__int__()
except AttributeError:
pass
else:
if not isinstance(value, int):
raise TypeError('__int__ returned non-int (type %s)' %
type(value).__name__)
import warnings
warnings.warn("an integer is required (got type %s)" %
type(orig).__name__,
DeprecationWarning,
stacklevel=2)
return value
raise TypeError('an integer is required (got type %s)' %
type(value).__name__)

def _check_date_fields(year, month, day):
year = _check_int_field(year)
month = _check_int_field(month)
day = _check_int_field(day)
year = _index(year)
month = _index(month)
day = _index(day)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
if not 1 <= month <= 12:
Expand All @@ -426,10 +395,10 @@ def _check_date_fields(year, month, day):
return year, month, day

def _check_time_fields(hour, minute, second, microsecond, fold):
hour = _check_int_field(hour)
minute = _check_int_field(minute)
second = _check_int_field(second)
microsecond = _check_int_field(microsecond)
hour = _index(hour)
minute = _index(minute)
second = _index(second)
microsecond = _index(microsecond)
if not 0 <= hour <= 23:
raise ValueError('hour must be in 0..23', hour)
if not 0 <= minute <= 59:
Expand Down Expand Up @@ -2539,10 +2508,10 @@ def _name_from_offset(delta):
# Clean up unused names
del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y,
_DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time,
_check_date_fields, _check_int_field, _check_time_fields,
_check_date_fields, _check_time_fields,
_check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
_date_class, _days_before_month, _days_before_year, _days_in_month,
_format_time, _format_offset, _is_leap, _isoweek1monday, _math,
_format_time, _format_offset, _index, _is_leap, _isoweek1monday, _math,
_ord2ymd, _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord,
_divide_and_round, _parse_isoformat_date, _parse_isoformat_time,
_parse_hh_mm_ss_ff, _IsoCalendarDate)
Expand Down
92 changes: 11 additions & 81 deletions Lib/test/clinic.test
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,6 @@ test_bool_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 3) {
goto skip_optional;
}
if (PyFloat_Check(args[2])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
c = _PyLong_AsInt(args[2]);
if (c == -1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -436,7 +431,7 @@ exit:

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


/*[clinic input]
Expand Down Expand Up @@ -729,11 +724,6 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t
if (nargs < 1) {
goto skip_optional;
}
if (PyFloat_Check(args[0])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
{
long ival = PyLong_AsLong(args[0]);
if (ival == -1 && PyErr_Occurred()) {
Expand All @@ -756,11 +746,6 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t
if (nargs < 2) {
goto skip_optional;
}
if (PyFloat_Check(args[1])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
{
long ival = PyLong_AsLong(args[1]);
if (ival == -1 && PyErr_Occurred()) {
Expand All @@ -783,14 +768,9 @@ test_unsigned_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t
if (nargs < 3) {
goto skip_optional;
}
if (PyFloat_Check(args[2])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
{
long ival = PyLong_AsUnsignedLongMask(args[2]);
if (ival == -1 && PyErr_Occurred()) {
unsigned long ival = PyLong_AsUnsignedLongMask(args[2]);
if (ival == (unsigned long)-1 && PyErr_Occurred()) {
goto exit;
}
else {
Expand All @@ -807,7 +787,7 @@ exit:
static PyObject *
test_unsigned_char_converter_impl(PyObject *module, unsigned char a,
unsigned char b, unsigned char c)
/*[clinic end generated code: output=ebf905c5c9414762 input=021414060993e289]*/
/*[clinic end generated code: output=c0a6ab3144481466 input=021414060993e289]*/


/*[clinic input]
Expand Down Expand Up @@ -841,11 +821,6 @@ test_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 1) {
goto skip_optional;
}
if (PyFloat_Check(args[0])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
{
long ival = PyLong_AsLong(args[0]);
if (ival == -1 && PyErr_Occurred()) {
Expand Down Expand Up @@ -874,7 +849,7 @@ exit:

static PyObject *
test_short_converter_impl(PyObject *module, short a)
/*[clinic end generated code: output=86fe1a1496a7ff20 input=6a8a7a509a498ff4]*/
/*[clinic end generated code: output=3ccda4bd08b6e4b4 input=6a8a7a509a498ff4]*/


/*[clinic input]
Expand Down Expand Up @@ -925,11 +900,6 @@ test_unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_
if (nargs < 3) {
goto skip_optional;
}
if (PyFloat_Check(args[2])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
c = (unsigned short)PyLong_AsUnsignedLongMask(args[2]);
if (c == (unsigned short)-1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -944,7 +914,7 @@ exit:
static PyObject *
test_unsigned_short_converter_impl(PyObject *module, unsigned short a,
unsigned short b, unsigned short c)
/*[clinic end generated code: output=3779fe104319e3ae input=cdfd8eff3d9176b4]*/
/*[clinic end generated code: output=576b5ce48424f351 input=cdfd8eff3d9176b4]*/


/*[clinic input]
Expand Down Expand Up @@ -984,23 +954,13 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 1) {
goto skip_optional;
}
if (PyFloat_Check(args[0])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
a = _PyLong_AsInt(args[0]);
if (a == -1 && PyErr_Occurred()) {
goto exit;
}
if (nargs < 2) {
goto skip_optional;
}
if (PyFloat_Check(args[1])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
b = _PyLong_AsInt(args[1]);
if (b == -1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -1023,11 +983,6 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 4) {
goto skip_optional;
}
if (PyFloat_Check(args[3])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
d = _PyLong_AsInt(args[3]);
if (d == -1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -1041,7 +996,7 @@ exit:

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


/*[clinic input]
Expand Down Expand Up @@ -1092,11 +1047,6 @@ test_unsigned_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t
if (nargs < 3) {
goto skip_optional;
}
if (PyFloat_Check(args[2])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
c = (unsigned int)PyLong_AsUnsignedLongMask(args[2]);
if (c == (unsigned int)-1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -1111,7 +1061,7 @@ exit:
static PyObject *
test_unsigned_int_converter_impl(PyObject *module, unsigned int a,
unsigned int b, unsigned int c)
/*[clinic end generated code: output=189176ce67c7d2e7 input=5533534828b62fc0]*/
/*[clinic end generated code: output=4f53904bfa1a0250 input=5533534828b62fc0]*/


/*[clinic input]
Expand Down Expand Up @@ -1145,11 +1095,6 @@ test_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
if (nargs < 1) {
goto skip_optional;
}
if (PyFloat_Check(args[0])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
a = PyLong_AsLong(args[0]);
if (a == -1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -1163,7 +1108,7 @@ exit:

static PyObject *
test_long_converter_impl(PyObject *module, long a)
/*[clinic end generated code: output=44cd8823f59d116b input=d2179e3c9cdcde89]*/
/*[clinic end generated code: output=e5e7883fddcf4218 input=d2179e3c9cdcde89]*/


/*[clinic input]
Expand Down Expand Up @@ -1263,11 +1208,6 @@ test_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t nar
if (nargs < 1) {
goto skip_optional;
}
if (PyFloat_Check(args[0])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
a = PyLong_AsLongLong(args[0]);
if (a == -1 && PyErr_Occurred()) {
goto exit;
Expand All @@ -1281,7 +1221,7 @@ exit:

static PyObject *
test_long_long_converter_impl(PyObject *module, long long a)
/*[clinic end generated code: output=7143b585d7e433e8 input=d5fc81577ff4dd02]*/
/*[clinic end generated code: output=0488ac9e8c1d77bb input=d5fc81577ff4dd02]*/


/*[clinic input]
Expand Down Expand Up @@ -1390,11 +1330,6 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
if (nargs < 1) {
goto skip_optional;
}
if (PyFloat_Check(args[0])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
Expand All @@ -1410,11 +1345,6 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
if (nargs < 2) {
goto skip_optional;
}
if (PyFloat_Check(args[1])) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float" );
goto exit;
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[1]);
Expand Down Expand Up @@ -1443,7 +1373,7 @@ exit:
static PyObject *
test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b,
Py_ssize_t c)
/*[clinic end generated code: output=a46d2aaf40c10398 input=3855f184bb3f299d]*/
/*[clinic end generated code: output=ea781bb7169b3436 input=3855f184bb3f299d]*/


/*[clinic input]
Expand Down
Loading