[Python-checkins] [2.7] bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039) (GH-10617)

Serhiy Storchaka webhook-mailer at python.org
Tue Nov 20 14:56:41 EST 2018


https://github.com/python/cpython/commit/40fdf471931f029ea07e6190f0fe116e0735661b
commit: 40fdf471931f029ea07e6190f0fe116e0735661b
branch: 2.7
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-11-20T21:56:34+02:00
summary:

[2.7] bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039) (GH-10617)

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().
(cherry picked from commit 3ec0f495163da3b7a15deb2805cec48aed432f58)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

files:
M Lib/test/test_datetime.py
M Modules/datetimemodule.c

diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index f56c3f5fe3de..2620fbb9fbf7 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -495,41 +495,64 @@ def as_hours(self):
     def test_issue31752(self):
         # The interpreter shouldn't crash because divmod() returns negative
         # remainder.
-        class BadInt(int):
-            def __mul__(self, other):
-                return Prod()
-
-        class BadLong(long):
-            def __mul__(self, other):
-                return Prod()
-
-        class Prod:
-            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))
-        timedelta(microseconds=BadLong(1))
-        timedelta(hours=BadLong(1))
-        timedelta(weeks=BadLong(1))
-
-        class Sum(long):
-            def __divmod__(self, other):
-                # negative remainder
-                return (0, -1)
-
-        timedelta(microseconds=BadInt(1))
-        timedelta(hours=BadInt(1))
-        timedelta(weeks=BadInt(1))
-        timedelta(microseconds=BadLong(1))
-        timedelta(hours=BadLong(1))
-        timedelta(weeks=BadLong(1))
+        for inttype in (int, long):
+            class BadInt(inttype):
+                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 BadLong(long):
+                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()
+
+            for inttype2 in (int, long):
+                class Sum(inttype2):
+                    def __divmod__(self, other):
+                        return divmodresult
+
+                for divmodresult in [None, (), (0, 1, 2), (0, -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
 
 
 #############################################################################
diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c
index c0b7102de234..29492de5167c 100644
--- a/Modules/datetimemodule.c
+++ b/Modules/datetimemodule.c
@@ -1546,6 +1546,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 or long) to a timedelta.
  */
 static PyObject *
@@ -1554,70 +1577,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(_PyAnyInt_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);
@@ -1626,6 +1628,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) \
@@ -1642,7 +1649,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;
@@ -1853,13 +1860,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
     assert(num != NULL);
 
     if (_PyAnyInt_Check(num)) {
-        prod = PyNumber_Multiply(factor, num);
+        prod = PyNumber_Multiply(num, factor);
         if (prod == NULL)
             return NULL;
-        assert(_PyAnyInt_CheckExact(prod));
         sum = PyNumber_Add(sofar, prod);
         Py_DECREF(prod);
-        assert(sum == NULL || _PyAnyInt_CheckExact(sum));
         return sum;
     }
 
@@ -1920,7 +1925,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
         Py_DECREF(sum);
         Py_DECREF(x);
         *leftover += fracpart;
-        assert(y == NULL || _PyAnyInt_CheckExact(y));
         return y;
     }
 



More information about the Python-checkins mailing list