Skip to content

bpo-40792: Make PyNumber_Index() always returning exact int. #20443

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
4 changes: 4 additions & 0 deletions Doc/c-api/number.rst
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ Number Protocol
Returns the *o* converted to a Python int on success or ``NULL`` with a
:exc:`TypeError` exception raised on failure.

.. versionchanged:: 3.10
The result always has exact type :class:`int`. Previously, the result
could have been an instance of a subclass of ``int``.


.. c:function:: PyObject* PyNumber_ToBase(PyObject *n, int base)

Expand Down
4 changes: 4 additions & 0 deletions Doc/library/operator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ The mathematical and bitwise operations are the most numerous:

Return *a* converted to an integer. Equivalent to ``a.__index__()``.

.. versionchanged:: 3.10
The result always has exact type :class:`int`. Previously, the result
could have been an instance of a subclass of ``int``.


.. function:: inv(obj)
invert(obj)
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ C API Changes
New Features
------------

The result of :c:func:`PyNumber_Index` now always has exact type :class:`int`.
Previously, the result could have been an instance of a subclass of ``int``.
(Contributed by Serhiy Storchaka in :issue:`40792`.)


Porting to Python 3.10
----------------------
Expand Down
3 changes: 3 additions & 0 deletions Include/cpython/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,9 @@ PyAPI_FUNC(void) _Py_add_one_to_index_C(int nd, Py_ssize_t *index,
/* Convert Python int to Py_ssize_t. Do nothing if the argument is None. */
PyAPI_FUNC(int) _Py_convert_optional_to_ssize_t(PyObject *, void *);

/* Same as PyNumber_Index but can return an instance of a subclass of int. */
PyAPI_FUNC(PyObject *) _PyNumber_Index(PyObject *o);

#ifdef __cplusplus
}
#endif
1 change: 1 addition & 0 deletions Lib/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def _deepcopy_atomic(x, memo):
d[str] = _deepcopy_atomic
d[types.CodeType] = _deepcopy_atomic
d[type] = _deepcopy_atomic
d[range] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
d[weakref.ref] = _deepcopy_atomic
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/clinic.test
Original file line number Diff line number Diff line change
Expand Up @@ -1332,7 +1332,7 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand All @@ -1347,7 +1347,7 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[1]);
PyObject *iobj = _PyNumber_Index(args[1]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand All @@ -1373,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=ea781bb7169b3436 input=3855f184bb3f299d]*/
/*[clinic end generated code: output=3bf73f9fdfeab468 input=3855f184bb3f299d]*/


/*[clinic input]
Expand Down
13 changes: 1 addition & 12 deletions Lib/test/test_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ def f():
pass
tests = [None, 42, 2**100, 3.14, True, False, 1j,
"hello", "hello\u1234", f.__code__,
NewStyle, Classic, max, property()]
NewStyle, range(10), Classic, max, property()]
for x in tests:
self.assertIs(copy.deepcopy(x), x)

Expand Down Expand Up @@ -579,17 +579,6 @@ class C:
self.assertIsNot(y, x)
self.assertIs(y.foo, y)

def test_deepcopy_range(self):
class I(int):
pass
x = range(I(10))
y = copy.deepcopy(x)
self.assertIsNot(y, x)
self.assertEqual(y, x)
self.assertIsNot(y.stop, x.stop)
self.assertEqual(y.stop, x.stop)
self.assertIsInstance(y.stop, I)

# _reconstruct()

def test_reconstruct_string(self):
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,11 +648,17 @@ def test_attributes(self):
self.assert_attrs(range(0, 10, 3), 0, 10, 3)
self.assert_attrs(range(10, 0, -1), 10, 0, -1)
self.assert_attrs(range(10, 0, -3), 10, 0, -3)
self.assert_attrs(range(True), 0, 1, 1)
self.assert_attrs(range(False, True), 0, 1, 1)
self.assert_attrs(range(False, True, True), 0, 1, 1)

def assert_attrs(self, rangeobj, start, stop, step):
self.assertEqual(rangeobj.start, start)
self.assertEqual(rangeobj.stop, stop)
self.assertEqual(rangeobj.step, step)
self.assertIs(type(rangeobj.start), int)
self.assertIs(type(rangeobj.stop), int)
self.assertIs(type(rangeobj.step), int)

with self.assertRaises(AttributeError):
rangeobj.start = 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The result of :c:func:`PyNumber_Index` now always has exact type :class:`int`.
Previously, the result could have been an instance of a subclass of ``int``.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Attributes ``start``, ``stop`` and ``step`` of the :class:`range` object now
always has exact type :class:`int`. Previously, they could have been an
instance of a subclass of ``int``.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The result of :func:`operator.index` now always has exact type :class:`int`.
Previously, the result could have been an instance of a subclass of ``int``.
2 changes: 1 addition & 1 deletion Modules/_io/_iomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ PyNumber_AsOff_t(PyObject *item, PyObject *err)
{
Py_off_t result;
PyObject *runerr;
PyObject *value = PyNumber_Index(item);
PyObject *value = _PyNumber_Index(item);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm missing something here: from my reading of the code, PyLong_AsOff_t is either PyLong_AsLong or PyLong_AsLongLong, and both of those already call _PyNumber_Index, so it looks as though we don't need this call.

Ah, I think I found what I'm missing: PyLong_AsOff_t could be PyLong_AsSsize_t, which doesn't call _PyNumber_Index. Is that right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is right.

It a separate question whether PyLong_AsSsize_t and others should call __index__ implicitly. It is not simple question, because such change may introduce bugs by making presumably atomic code non-atomic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It a separate question whether PyLong_AsSsize_t and others should call __index__ implicitly.

Agreed. It seems worth looking into, but not in this PR.

if (value == NULL)
return -1;

Expand Down
14 changes: 7 additions & 7 deletions Modules/_io/clinic/bufferedio.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ _io__Buffered_peek(buffered *self, PyObject *const *args, Py_ssize_t nargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -197,7 +197,7 @@ _io__Buffered_read1(buffered *self, PyObject *const *args, Py_ssize_t nargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -421,7 +421,7 @@ _io_BufferedReader___init__(PyObject *self, PyObject *args, PyObject *kwargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(fastargs[1]);
PyObject *iobj = _PyNumber_Index(fastargs[1]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -475,7 +475,7 @@ _io_BufferedWriter___init__(PyObject *self, PyObject *args, PyObject *kwargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(fastargs[1]);
PyObject *iobj = _PyNumber_Index(fastargs[1]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -567,7 +567,7 @@ _io_BufferedRWPair___init__(PyObject *self, PyObject *args, PyObject *kwargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(PyTuple_GET_ITEM(args, 2));
PyObject *iobj = _PyNumber_Index(PyTuple_GET_ITEM(args, 2));
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -621,7 +621,7 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(fastargs[1]);
PyObject *iobj = _PyNumber_Index(fastargs[1]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand All @@ -637,4 +637,4 @@ _io_BufferedRandom___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
/*[clinic end generated code: output=1882bb497ddc9375 input=a9049054013a1b77]*/
/*[clinic end generated code: output=98ccf7610c0e82ba input=a9049054013a1b77]*/
4 changes: 2 additions & 2 deletions Modules/_io/clinic/bytesio.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ _io_BytesIO_seek(bytesio *self, PyObject *const *args, Py_ssize_t nargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -505,4 +505,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
/*[clinic end generated code: output=ba0f302f16397741 input=a9049054013a1b77]*/
/*[clinic end generated code: output=49a32140eb8c5555 input=a9049054013a1b77]*/
4 changes: 2 additions & 2 deletions Modules/_io/clinic/iobase.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ _io__RawIOBase_read(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -310,4 +310,4 @@ _io__RawIOBase_readall(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return _io__RawIOBase_readall_impl(self);
}
/*[clinic end generated code: output=1f9ce590549593be input=a9049054013a1b77]*/
/*[clinic end generated code: output=83c1361a7a51ca84 input=a9049054013a1b77]*/
4 changes: 2 additions & 2 deletions Modules/_io/clinic/stringio.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ _io_StringIO_seek(stringio *self, PyObject *const *args, Py_ssize_t nargs)
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -338,4 +338,4 @@ _io_StringIO_seekable(stringio *self, PyObject *Py_UNUSED(ignored))
{
return _io_StringIO_seekable_impl(self);
}
/*[clinic end generated code: output=9c428b2942d54991 input=a9049054013a1b77]*/
/*[clinic end generated code: output=eea93dcab10d0a97 input=a9049054013a1b77]*/
4 changes: 2 additions & 2 deletions Modules/_io/clinic/textio.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ _io_TextIOWrapper_readline(textio *self, PyObject *const *args, Py_ssize_t nargs
}
{
Py_ssize_t ival = -1;
PyObject *iobj = PyNumber_Index(args[0]);
PyObject *iobj = _PyNumber_Index(args[0]);
if (iobj != NULL) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
Expand Down Expand Up @@ -671,4 +671,4 @@ _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored))
{
return _io_TextIOWrapper_close_impl(self);
}
/*[clinic end generated code: output=ea96ee1eb3a71f77 input=a9049054013a1b77]*/
/*[clinic end generated code: output=2604c8f3a45b9a03 input=a9049054013a1b77]*/
2 changes: 1 addition & 1 deletion Modules/_struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ get_pylong(PyObject *v)
if (!PyLong_Check(v)) {
/* Not an integer; try to use __index__ to convert. */
if (PyIndex_Check(v)) {
v = PyNumber_Index(v);
v = _PyNumber_Index(v);
if (v == NULL)
return NULL;
}
Expand Down
6 changes: 3 additions & 3 deletions Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
int do_decref = 0; /* if nb_int was called */

if (!PyLong_Check(v)) {
v = PyNumber_Index(v);
v = _PyNumber_Index(v);
if (NULL == v) {
return -1;
}
Expand Down Expand Up @@ -404,7 +404,7 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
int do_decref = 0; /* if nb_int was called */

if (!PyLong_Check(v)) {
v = PyNumber_Index(v);
v = _PyNumber_Index(v);
if (NULL == v) {
return -1;
}
Expand Down Expand Up @@ -457,7 +457,7 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
int do_decref = 0; /* if nb_int was called */

if (!PyLong_Check(v)) {
v = PyNumber_Index(v);
v = _PyNumber_Index(v);
if (NULL == v) {
return -1;
}
Expand Down
10 changes: 5 additions & 5 deletions Modules/clinic/_bisectmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Modules/clinic/_bz2module.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Modules/clinic/_collectionsmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Modules/clinic/_elementtree.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading