bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039)
Fixes assertion failures in _datetimemodule.c introduced in the previous fix (see bpo-31752). Rather of trying to handle an int subclass as exact int, let it to use overridden special methods, but check the result of divmod().
This commit is contained in:
parent
97f1efb606
commit
3ec0f49516
|
@ -896,19 +896,50 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
|
||||||
class BadInt(int):
|
class BadInt(int):
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return Prod()
|
return Prod()
|
||||||
|
def __rmul__(self, other):
|
||||||
|
return Prod()
|
||||||
|
def __floordiv__(self, other):
|
||||||
|
return Prod()
|
||||||
|
def __rfloordiv__(self, other):
|
||||||
|
return Prod()
|
||||||
|
|
||||||
class Prod:
|
class Prod:
|
||||||
|
def __add__(self, other):
|
||||||
|
return Sum()
|
||||||
def __radd__(self, other):
|
def __radd__(self, other):
|
||||||
return Sum()
|
return Sum()
|
||||||
|
|
||||||
class Sum(int):
|
class Sum(int):
|
||||||
def __divmod__(self, other):
|
def __divmod__(self, other):
|
||||||
# negative remainder
|
return divmodresult
|
||||||
return (0, -1)
|
|
||||||
|
|
||||||
timedelta(microseconds=BadInt(1))
|
for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
|
||||||
timedelta(hours=BadInt(1))
|
with self.subTest(divmodresult=divmodresult):
|
||||||
timedelta(weeks=BadInt(1))
|
# The following examples should not crash.
|
||||||
|
try:
|
||||||
|
timedelta(microseconds=BadInt(1))
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
timedelta(hours=BadInt(1))
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
timedelta(weeks=BadInt(1))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
timedelta(1) * BadInt(1)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
BadInt(1) * timedelta(1)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
timedelta(1) // BadInt(1)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
|
@ -1797,6 +1797,29 @@ Done:
|
||||||
return result;
|
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)",
|
||||||
|
result->ob_type->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.
|
/* Convert a number of us (as a Python int) to a timedelta.
|
||||||
*/
|
*/
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1805,70 +1828,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
|
||||||
int us;
|
int us;
|
||||||
int s;
|
int s;
|
||||||
int d;
|
int d;
|
||||||
long temp;
|
|
||||||
|
|
||||||
PyObject *tuple = NULL;
|
PyObject *tuple = NULL;
|
||||||
PyObject *num = NULL;
|
PyObject *num = NULL;
|
||||||
PyObject *result = NULL;
|
PyObject *result = NULL;
|
||||||
|
|
||||||
assert(PyLong_CheckExact(pyus));
|
tuple = checked_divmod(pyus, us_per_second);
|
||||||
tuple = PyNumber_Divmod(pyus, us_per_second);
|
if (tuple == NULL) {
|
||||||
if (tuple == NULL)
|
|
||||||
goto Done;
|
|
||||||
|
|
||||||
num = PyTuple_GetItem(tuple, 1); /* us */
|
|
||||||
if (num == NULL)
|
|
||||||
goto Done;
|
|
||||||
temp = PyLong_AsLong(num);
|
|
||||||
num = NULL;
|
|
||||||
if (temp == -1 && PyErr_Occurred())
|
|
||||||
goto Done;
|
|
||||||
assert(0 <= temp && temp < 1000000);
|
|
||||||
us = (int)temp;
|
|
||||||
if (us < 0) {
|
|
||||||
/* The divisor was positive, so this must be an error. */
|
|
||||||
assert(PyErr_Occurred());
|
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
|
num = PyTuple_GET_ITEM(tuple, 1); /* us */
|
||||||
if (num == NULL)
|
us = _PyLong_AsInt(num);
|
||||||
|
num = NULL;
|
||||||
|
if (us == -1 && PyErr_Occurred()) {
|
||||||
goto Done;
|
goto Done;
|
||||||
|
}
|
||||||
|
if (!(0 <= us && us < 1000000)) {
|
||||||
|
goto BadDivmod;
|
||||||
|
}
|
||||||
|
|
||||||
|
num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
|
||||||
Py_INCREF(num);
|
Py_INCREF(num);
|
||||||
Py_DECREF(tuple);
|
Py_DECREF(tuple);
|
||||||
|
|
||||||
tuple = PyNumber_Divmod(num, seconds_per_day);
|
tuple = checked_divmod(num, seconds_per_day);
|
||||||
if (tuple == NULL)
|
if (tuple == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
Py_DECREF(num);
|
Py_DECREF(num);
|
||||||
|
|
||||||
num = PyTuple_GetItem(tuple, 1); /* seconds */
|
num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
|
||||||
if (num == NULL)
|
s = _PyLong_AsInt(num);
|
||||||
goto Done;
|
|
||||||
temp = PyLong_AsLong(num);
|
|
||||||
num = NULL;
|
num = NULL;
|
||||||
if (temp == -1 && PyErr_Occurred())
|
if (s == -1 && PyErr_Occurred()) {
|
||||||
goto Done;
|
|
||||||
assert(0 <= temp && temp < 24*3600);
|
|
||||||
s = (int)temp;
|
|
||||||
|
|
||||||
if (s < 0) {
|
|
||||||
/* The divisor was positive, so this must be an error. */
|
|
||||||
assert(PyErr_Occurred());
|
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
if (!(0 <= s && s < 24*3600)) {
|
||||||
|
goto BadDivmod;
|
||||||
|
}
|
||||||
|
|
||||||
num = PyTuple_GetItem(tuple, 0); /* leftover days */
|
num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
|
||||||
if (num == NULL)
|
|
||||||
goto Done;
|
|
||||||
Py_INCREF(num);
|
Py_INCREF(num);
|
||||||
temp = PyLong_AsLong(num);
|
d = _PyLong_AsInt(num);
|
||||||
if (temp == -1 && PyErr_Occurred())
|
if (d == -1 && PyErr_Occurred()) {
|
||||||
goto Done;
|
|
||||||
d = (int)temp;
|
|
||||||
if ((long)d != temp) {
|
|
||||||
PyErr_SetString(PyExc_OverflowError, "normalized days too "
|
|
||||||
"large to fit in a C int");
|
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
result = new_delta_ex(d, s, us, 0, type);
|
result = new_delta_ex(d, s, us, 0, type);
|
||||||
|
@ -1877,6 +1879,11 @@ Done:
|
||||||
Py_XDECREF(tuple);
|
Py_XDECREF(tuple);
|
||||||
Py_XDECREF(num);
|
Py_XDECREF(num);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
BadDivmod:
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"divmod() returned a value out of range");
|
||||||
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define microseconds_to_delta(pymicros) \
|
#define microseconds_to_delta(pymicros) \
|
||||||
|
@ -1893,7 +1900,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
|
||||||
if (pyus_in == NULL)
|
if (pyus_in == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
pyus_out = PyNumber_Multiply(pyus_in, intobj);
|
pyus_out = PyNumber_Multiply(intobj, pyus_in);
|
||||||
Py_DECREF(pyus_in);
|
Py_DECREF(pyus_in);
|
||||||
if (pyus_out == NULL)
|
if (pyus_out == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2297,13 +2304,12 @@ delta_divmod(PyObject *left, PyObject *right)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
divmod = PyNumber_Divmod(pyus_left, pyus_right);
|
divmod = checked_divmod(pyus_left, pyus_right);
|
||||||
Py_DECREF(pyus_left);
|
Py_DECREF(pyus_left);
|
||||||
Py_DECREF(pyus_right);
|
Py_DECREF(pyus_right);
|
||||||
if (divmod == NULL)
|
if (divmod == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
assert(PyTuple_Size(divmod) == 2);
|
|
||||||
delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
|
delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
|
||||||
if (delta == NULL) {
|
if (delta == NULL) {
|
||||||
Py_DECREF(divmod);
|
Py_DECREF(divmod);
|
||||||
|
@ -2334,13 +2340,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
|
||||||
assert(num != NULL);
|
assert(num != NULL);
|
||||||
|
|
||||||
if (PyLong_Check(num)) {
|
if (PyLong_Check(num)) {
|
||||||
prod = PyNumber_Multiply(factor, num);
|
prod = PyNumber_Multiply(num, factor);
|
||||||
if (prod == NULL)
|
if (prod == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
assert(PyLong_CheckExact(prod));
|
|
||||||
sum = PyNumber_Add(sofar, prod);
|
sum = PyNumber_Add(sofar, prod);
|
||||||
Py_DECREF(prod);
|
Py_DECREF(prod);
|
||||||
assert(sum == NULL || PyLong_CheckExact(sum));
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2398,7 +2402,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
|
||||||
Py_DECREF(sum);
|
Py_DECREF(sum);
|
||||||
Py_DECREF(x);
|
Py_DECREF(x);
|
||||||
*leftover += fracpart;
|
*leftover += fracpart;
|
||||||
assert(y == NULL || PyLong_CheckExact(y));
|
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue