Skip to content

Commit

Permalink
bpo-37999: No longer use __int__ in implicit integer conversions. (GH…
Browse files Browse the repository at this point in the history
…-15636)

Only __index__ should be used to make integer conversions lossless.
  • Loading branch information
serhiy-storchaka authored May 26, 2020
1 parent 8ad0524 commit 578c395
Show file tree
Hide file tree
Showing 87 changed files with 226 additions and 2,937 deletions.
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

0 comments on commit 578c395

Please sign in to comment.