10000 bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039) · python/cpython@d57ab8a · GitHub
[go: up one dir, main page]

Skip to content

Commit d57ab8a

Browse files
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(). (cherry picked from commit 3ec0f49) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent f8f9915 commit d57ab8a

File tree

2 files changed

+88
-54
lines changed

2 files changed

+88
-54
lines changed

Lib/test/datetimetester.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -896,19 +896,50 @@ def test_issue31752(self):
896896
class BadInt(int):
897897
def __mul__(self, other):
898898
return Prod()
899+
def __rmul__(self, other):
900+
return Prod()
901+
def __floordiv__(self, other):
902+
return Prod()
903+
def __rfloordiv__(self, other):
904+
return Prod()
899905

900906
class Prod:
907+
def __add__(self, other):
908+
return Sum()
901909
def __radd__(self, other):
902910
return Sum()
903911

904912
class Sum(int):
905913
def __divmod__(self, other):
906-
# negative remainder
907-
return (0, -1)
908-
909-
timedelta(microseconds=BadInt(1))
910-
timedelta(hours=BadInt(1))
911-
timedelta(weeks=BadInt(1))
914+
return divmodresult
915+
916+
for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
917+
with self.subTest(divmodresult=divmodresult):
918+
# The following examples should not crash.
919+
try:
920+
timedelta(microseconds=BadInt(1))
921+
except TypeError:
922+
pass
923+
try:
924+
timedelta(hours=BadInt(1))
925+
except TypeError:
926+
pass
927+
try:
928+
timedelta(weeks=BadInt(1))
929+
except (TypeError, ValueError):
930+
pass
931+
try:
932+
timedelta(1) * BadInt(1)
933+
except (TypeError, ValueError):
934+
pass
935+
try:
936+
BadInt(1) * timedelta(1)
937+
except TypeError:
938+
pass
939+
try:
940+
timedelta(1) // BadInt(1)
941+
except TypeError:
942+
pass
912943

913944

914945
#############################################################################

Modules/_datetimemodule.c

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,29 @@ delta_to_microseconds(PyDateTime_Delta *self)
17831783
return result;
17841784
}
17851785

1786+
static PyObject *
1787+
checked_divmod(PyObject *a, PyObject *b)
1788+
{
1789+
PyObject *result = PyNumber_Divmod(a, b);
1790+
if (result != NULL) {
1791+
if (!PyTuple_Check(result)) {
1792+
PyErr_Format(PyExc_TypeError,
1793+
"divmod() returned non-tuple (type %.200s)",
1794+
result->ob_type->tp_name);
1795+
Py_DECREF(result);
1796+
return NULL;
1797+
}
1798+
if (PyTuple_GET_SIZE(result) != 2) {
1799+
PyErr_Format(PyExc_TypeError,
1800+
"divmod() returned a tuple of size %zd",
1801+
PyTuple_GET_SIZE(result));
1802+
Py_DECREF(result);
1803+
return NULL;
1804+
}
1805+
}
1806+
return result;
1807+
}
1808+
17861809
/* Convert a number of us (as a Python int) to a timedelta.
17871810
*/
17881811
static PyObject *
@@ -1791,70 +1814,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
17911814
int us;
17921815
int s;
17931816
int d;
1794-
long temp;
17951817

17961818
PyObject *tuple = NULL;
17971819
PyObject *num = NULL;
17981820
PyObject *result = NULL;
17991821

1800-
assert(PyLong_CheckExact(pyus));
1801-
tuple = PyNumber_Divmod(pyus, us_per_second);
1802-
if (tuple == NULL)
1822+
tuple = checked_divmod(pyus, us_per_second);
1823+
if (tuple == NULL) {
18031824
goto Done;
1825+
}
18041826

1805-
num = PyTuple_GetItem(tuple, 1); /* us */
1806-
if (num == NULL)
1807-
goto Done;
1808-
temp = PyLong_AsLong(num);
1827+
num = PyTuple_GET_ITEM(tuple, 1); /* us */
1828+
us = _PyLong_AsInt(num);
18091829
num = NULL;
1810-
if (temp == -1 && PyErr_Occurred())
1811-
goto Done;
1812-
assert(0 <= temp && temp < 1000000);
1813-
us = (int)temp;
1814-
if (us < 0) {
1815-
/* The divisor was positive, so this must be an error. */
1816-
assert(PyErr_Occurred());
1830+
if (us == -1 && PyErr_Occurred()) {
18171831
goto Done;
18181832
}
1833+
if (!(0 <= us && us < 1000000)) {
1834+
goto BadDivmod;
1835+
}
18191836

1820-
num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
1821-
if (num == NULL)
1822-
goto Done;
1837+
num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
18231838
Py_INCREF(num);
18241839
Py_DECREF(tuple);
18251840

1826-
tuple = PyNumber_Divmod(num, seconds_per_day);
1841+
tuple = checked_divmod(num, seconds_per_day);
18271842
if (tuple == NULL)
18281843
goto Done;
18291844
Py_DECREF(num);
18301845

1831-
num = PyTuple_GetItem(tuple, 1); /* seconds */
1832-
if (num == NULL)
1833-
goto Done;
1834-
temp = PyLong_AsLong(num);
1846+
num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
1847+
s = _PyLong_AsInt(num);
18351848
num = NULL;
1836-
if (temp == -1 && PyErr_Occurred())
1837-
goto Done;
1838-
assert(0 <= temp && temp < 24*3600);
1839-
s = (int)temp;
1840-
1841-
if (s < 0) {
1842-
/* The divisor was positive, so this must be an error. */
1843-
assert(PyErr_Occurred());
1849+
if (s == -1 && PyErr_Occurred()) {
18441850
goto Done;
18451851
}
1852+
if (!(0 <= s && s < 24*3600)) {
1853+
goto BadDivmod;
1854+
}
18461855

1847-
num = PyTuple_GetItem(tuple, 0); /* leftover days */
1848-
if (num == NULL)
1849-
goto Done;
1856+
num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
18501857
Py_INCREF(num);
1851-
temp = PyLong_AsLong(num);
1852-
if (temp == -1 && PyErr_Occurred())
1853-
goto Done;
1854-
d = (int)temp;
1855-
if ((long)d != temp) {
1856-
PyErr_SetString(PyExc_OverflowError, "normalized days too "
1857-
"large to fit in a C int");
1858+
d = _PyLong_AsInt(num);
1859+
if (d == -1 && PyErr_Occurred()) {
18581860
goto Done;
18591861
}
18601862
result = new_delta_ex(d, s, us, 0, type);
@@ -1863,6 +1865,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
18631865
Py_XDECREF(tuple);
18641866
Py_XDECREF(num);
18651867
return result;
1868+
1869+
BadDivmod:
1870+
PyErr_SetString(PyExc_TypeError,
1871+
"divmod() returned a value out of range");
1872+
goto Done;
18661873
}
18671874

18681875
#define microseconds_to_delta(pymicros) \
@@ -1879,7 +1886,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
18791886
if (pyus_in == NULL)
18801887
return NULL;
18811888

1882-
pyus_out = PyNumber_Multiply(pyus_in, intobj);
1889+
pyus_out = PyNumber_Multiply(intobj, pyus_in);
18831890
Py_DECREF(pyus_in);
18841891
if (pyus_out == NULL)
18851892
return NULL;
@@ -2283,13 +2290,12 @@ delta_divmod(PyObject *left, PyObject *right)
22832290
return NULL;
22842291
}
22852292

2286-
divmod = PyNumber_Divmod(pyus_left, pyus_right);
2293+
divmod = checked_divmod(pyus_left, pyus_right);
22872294
Py_DECREF(pyus_left);
22882295
Py_DECREF(pyus_right);
22892296
if (divmod == NULL)
22902297
return NULL;
22912298

2292-
assert(PyTuple_Size(divmod) == 2);
22932299
delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
22942300
if (delta == NULL) {
22952301
Py_DECREF(divmod);
@@ -2320,13 +2326,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
23202326
assert(num != NULL);
23212327

23222328
if (PyLong_Check(num)) {
2323-
prod = PyNumber_Multiply(factor, num);
2329+
prod = PyNumber_Multiply(num, factor);
23242330
if (prod == NULL)
23252331
return NULL;
2326-
assert(PyLong_CheckExact(prod));
23272332
sum = PyNumber_Add(sofar, prod);
23282333
Py_DECREF(prod);
2329-
assert(sum == NULL || PyLong_CheckExact(sum));
23302334
return sum;
23312335
}
23322336

@@ -2384,7 +2388,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
23842388
Py_DECREF(sum);
23852389
Py_DECREF(x);
23862390
*leftover += fracpart;
2387-
assert(y == NULL || PyLong_CheckExact(y));
23882391
return y;
23892392
}
23902393

0 commit comments

Comments
 (0)
0