-
-
Notifications
You must be signed in to change notification settings - Fork 32.9k
gh-78857: Always return a 2-tuple from divmod() and PyNumber_Divmod(). #9301
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
base: main
Are you sure you want to change the base?
Changes from all commits
4a699eb
2142008
70132f8
eb6947e
c011052
5ec28de
2d5c3cc
f8e551f
159a37b
60adab4
eb0be0e
148ac2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
It is now guaranteed that :func:`divmod` and :c:func:`PyNumber_Divmod` always | ||
return a 2-tuple on success. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1794,29 +1794,6 @@ delta_to_microseconds(PyDateTime_Delta *self) | |
return result; | ||
} | ||
|
||
static PyObject * | ||
checked_divmod(PyObject *a, PyObject *b) | ||
{ | ||
PyObject *result = PyNumber_Divmod(a, b); | ||
if (result != NULL) { | ||
if (!PyTuple_Check(result)) { | ||
PyErr_Format(PyExc_TypeError, | ||
"divmod() returned non-tuple (type %.200s)", | ||
Py_TYPE(result)->tp_name); | ||
Py_DECREF(result); | ||
return NULL; | ||
} | ||
if (PyTuple_GET_SIZE(result) != 2) { | ||
PyErr_Format(PyExc_TypeError, | ||
"divmod() returned a tuple of size %zd", | ||
PyTuple_GET_SIZE(result)); | ||
Py_DECREF(result); | ||
return NULL; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/* Convert a number of us (as a Python int) to a timedelta. | ||
*/ | ||
static PyObject * | ||
|
@@ -1830,7 +1807,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) | |
PyObject *num = NULL; | ||
PyObject *result = NULL; | ||
|
||
tuple = checked_divmod(pyus, us_per_second); | ||
tuple = PyNumber_Divmod(pyus, us_per_second); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't looked closely at any of the other stuff, but +1 on the |
||
if (tuple == NULL) { | ||
goto Done; | ||
} | ||
|
@@ -1849,7 +1826,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) | |
Py_INCREF(num); | ||
Py_DECREF(tuple); | ||
|
||
tuple = checked_divmod(num, seconds_per_day); | ||
tuple = PyNumber_Divmod(num, seconds_per_day); | ||
if (tuple == NULL) | ||
goto Done; | ||
Py_DECREF(num); | ||
|
@@ -2301,7 +2278,7 @@ delta_divmod(PyObject *left, PyObject *right) | |
return NULL; | ||
} | ||
|
||
divmod = checked_divmod(pyus_left, pyus_right); | ||
divmod = PyNumber_Divmod(pyus_left, pyus_right); | ||
Py_DECREF(pyus_left); | ||
Py_DECREF(pyus_right); | ||
if (divmod == NULL) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -937,7 +937,6 @@ BINARY_FUNC(PyNumber_And, nb_and, "&") | |
BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<") | ||
BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>") | ||
BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-") | ||
BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()") | ||
|
||
PyObject * | ||
PyNumber_Add(PyObject *v, PyObject *w) | ||
|
@@ -954,6 +953,46 @@ PyNumber_Add(PyObject *v, PyObject *w) | |
return result; | ||
} | ||
|
||
PyObject * | ||
PyNumber_Divmod(PyObject *v, PyObject *w) | ||
{ | ||
PyObject *orig_result = NULL; | ||
PyObject *result = binary_op(v, w, NB_SLOT(nb_divmod), "divmod()"); | ||
if (result == NULL) { | ||
return NULL; | ||
} | ||
if (!PyTuple_Check(result)) { | ||
orig_result = result; | ||
result = PySequence_Tuple(result); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then there will be an undefined behavior in orig_result != NULL. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I missed the connection between Setting a single boolean in this branch would eliminate the need for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I now remember why I used this way. Keeping the reference to |
||
if (result == NULL) { | ||
Py_DECREF(orig_result); | ||
if (PyErr_ExceptionMatches(PyExc_TypeError)) { | ||
goto error; | ||
} | ||
return NULL; | ||
} | ||
} | ||
if (PyTuple_GET_SIZE(result) != 2) { | ||
Py_XDECREF(orig_result); | ||
Py_DECREF(result); | ||
error: | ||
PyErr_SetString(PyExc_TypeError, | ||
"__divmod__() and __rdivmod__() must return a 2-tuple"); | ||
return NULL; | ||
} | ||
if (orig_result != NULL) { | ||
Py_DECREF(orig_result); | ||
if (PyErr_WarnEx(PyExc_DeprecationWarning, | ||
"__divmod__() and __rdivmod__() must return a 2-tuple", | ||
1) < 0) | ||
{ | ||
Py_DECREF(result); | ||
return NULL; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
static PyObject * | ||
sequence_repeat(ssizeargfunc repeatfunc, PyObject *seq, PyObject *n) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to add a
namedtuple
example here, to state explicitly that tuple subclasses are intended to be supported.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hesitate. Should we allow tuple subclasses or require exact tuples? Most converting special methods (
__int__
,__index__
,__float__
,__complex__
) are required to return an instance of exact type. From C side, if use the C API for unpacking the tuple, there is no difference between tuples and tuple subclasses. But there mey be difference at Python level if the tuple subclass overrides some methods like__add__
,__iter__
,__len__
,__getitem__
.For now, I prefer to not promise the namedtuple support.