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:
Serhiy Storchaka 2018-11-20 20:41:09 +02:00 committed by GitHub
parent 97f1efb606
commit 3ec0f49516
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 55 deletions

View File

@ -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
############################################################################# #############################################################################

View File

@ -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;
} }