[Python-checkins] bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039)
Serhiy Storchaka
webhook-mailer at python.org
Tue Nov 20 13:41:12 EST 2018
https://github.com/python/cpython/commit/3ec0f495163da3b7a15deb2805cec48aed432f58
commit: 3ec0f495163da3b7a15deb2805cec48aed432f58
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-11-20T20:41:09+02:00
summary:
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().
files:
M Lib/test/datetimetester.py
M Modules/_datetimemodule.c
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 06fe647fb1a4..78b123f5b118 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -896,19 +896,50 @@ def test_issue31752(self):
class BadInt(int):
def __mul__(self, other):
return Prod()
+ def __rmul__(self, other):
+ return Prod()
+ def __floordiv__(self, other):
+ return Prod()
+ def __rfloordiv__(self, other):
+ return Prod()
class Prod:
+ def __add__(self, other):
+ return Sum()
def __radd__(self, other):
return Sum()
class Sum(int):
def __divmod__(self, other):
- # negative remainder
- return (0, -1)
-
- timedelta(microseconds=BadInt(1))
- timedelta(hours=BadInt(1))
- timedelta(weeks=BadInt(1))
+ return divmodresult
+
+ for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
+ with self.subTest(divmodresult=divmodresult):
+ # 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
#############################################################################
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 02c510f055bd..371bfaec3a05 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -1797,6 +1797,29 @@ 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)",
+ 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.
*/
static PyObject *
@@ -1805,70 +1828,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
int us;
int s;
int d;
- long temp;
PyObject *tuple = NULL;
PyObject *num = NULL;
PyObject *result = NULL;
- assert(PyLong_CheckExact(pyus));
- tuple = PyNumber_Divmod(pyus, us_per_second);
- if (tuple == NULL)
+ tuple = checked_divmod(pyus, us_per_second);
+ if (tuple == NULL) {
goto Done;
+ }
- num = PyTuple_GetItem(tuple, 1); /* us */
- if (num == NULL)
- goto Done;
- temp = PyLong_AsLong(num);
+ num = PyTuple_GET_ITEM(tuple, 1); /* us */
+ us = _PyLong_AsInt(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());
+ if (us == -1 && PyErr_Occurred()) {
goto Done;
}
+ if (!(0 <= us && us < 1000000)) {
+ goto BadDivmod;
+ }
- num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
- if (num == NULL)
- goto Done;
+ num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
Py_INCREF(num);
Py_DECREF(tuple);
- tuple = PyNumber_Divmod(num, seconds_per_day);
+ tuple = checked_divmod(num, seconds_per_day);
if (tuple == NULL)
goto Done;
Py_DECREF(num);
- num = PyTuple_GetItem(tuple, 1); /* seconds */
- if (num == NULL)
- goto Done;
- temp = PyLong_AsLong(num);
+ num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
+ s = _PyLong_AsInt(num);
num = NULL;
- if (temp == -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());
+ if (s == -1 && PyErr_Occurred()) {
goto Done;
}
+ if (!(0 <= s && s < 24*3600)) {
+ goto BadDivmod;
+ }
- num = PyTuple_GetItem(tuple, 0); /* leftover days */
- if (num == NULL)
- goto Done;
+ num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
Py_INCREF(num);
- temp = PyLong_AsLong(num);
- if (temp == -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");
+ d = _PyLong_AsInt(num);
+ if (d == -1 && PyErr_Occurred()) {
goto Done;
}
result = new_delta_ex(d, s, us, 0, type);
@@ -1877,6 +1879,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
Py_XDECREF(tuple);
Py_XDECREF(num);
return result;
+
+BadDivmod:
+ PyErr_SetString(PyExc_TypeError,
+ "divmod() returned a value out of range");
+ goto Done;
}
#define microseconds_to_delta(pymicros) \
@@ -1893,7 +1900,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
if (pyus_in == NULL)
return NULL;
- pyus_out = PyNumber_Multiply(pyus_in, intobj);
+ pyus_out = PyNumber_Multiply(intobj, pyus_in);
Py_DECREF(pyus_in);
if (pyus_out == NULL)
return NULL;
@@ -2297,13 +2304,12 @@ delta_divmod(PyObject *left, PyObject *right)
return NULL;
}
- divmod = PyNumber_Divmod(pyus_left, pyus_right);
+ divmod = checked_divmod(pyus_left, pyus_right);
Py_DECREF(pyus_left);
Py_DECREF(pyus_right);
if (divmod == NULL)
return NULL;
- assert(PyTuple_Size(divmod) == 2);
delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
if (delta == NULL) {
Py_DECREF(divmod);
@@ -2334,13 +2340,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
assert(num != NULL);
if (PyLong_Check(num)) {
- prod = PyNumber_Multiply(factor, num);
+ prod = PyNumber_Multiply(num, factor);
if (prod == NULL)
return NULL;
- assert(PyLong_CheckExact(prod));
sum = PyNumber_Add(sofar, prod);
Py_DECREF(prod);
- assert(sum == NULL || PyLong_CheckExact(sum));
return sum;
}
@@ -2398,7 +2402,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
Py_DECREF(sum);
Py_DECREF(x);
*leftover += fracpart;
- assert(y == NULL || PyLong_CheckExact(y));
return y;
}
More information about the Python-checkins
mailing list