Skip to content

Commit 77ab121

Browse files
committed
gh-151039: Fix a crash when datetime.timedelta outlives _datetime module
1 parent ea4c855 commit 77ab121

3 files changed

Lines changed: 56 additions & 2 deletions

File tree

Lib/test/datetimetester.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7509,6 +7509,29 @@ def func():
75097509
self.assertEqual(out, b"a" * 8)
75107510
self.assertEqual(err, b"")
75117511

7512+
@support.cpython_only
7513+
def test_gh_151039(self):
7514+
# gh-151039: This code used to crash
7515+
script = """if True:
7516+
import sys, gc
7517+
import _datetime
7518+
7519+
td = _datetime.timedelta # static C type, survives the module
7520+
del sys.modules['_datetime']
7521+
del _datetime
7522+
sys.modules['_datetime'] = None # block re-import
7523+
gc.collect() # module object is collected
7524+
7525+
try:
7526+
td(seconds=2) # used to be a segmentation fault
7527+
except ImportError:
7528+
pass
7529+
else:
7530+
assert False, "ImportError not raised"
7531+
"""
7532+
rc, out, err = script_helper.assert_python_ok("-c", script)
7533+
self.assertEqual(rc, 0)
7534+
75127535

75137536
def load_tests(loader, standard_tests, pattern):
75147537
standard_tests.addTest(ZoneInfoCompleteTest())
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a crash when :class:`datetime.timezone` outlives ``_datetime`` module.

Modules/_datetimemodule.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ get_current_module(PyInterpreterState *interp)
141141
}
142142
if (ref != NULL) {
143143
if (ref != Py_None) {
144-
(void)PyWeakref_GetRef(ref, &mod);
144+
if (PyWeakref_GetRef(ref, &mod) < 0) {
145+
Py_DECREF(ref);
146+
goto error;
147+
}
145148
if (mod == Py_None) {
146149
Py_CLEAR(mod);
147150
}
@@ -163,7 +166,6 @@ _get_current_state(PyObject **p_mod)
163166
PyInterpreterState *interp = PyInterpreterState_Get();
164167
PyObject *mod = get_current_module(interp);
165168
if (mod == NULL) {
166-
assert(!PyErr_Occurred());
167169
if (PyErr_Occurred()) {
168170
return NULL;
169171
}
@@ -2130,6 +2132,10 @@ delta_to_microseconds(PyDateTime_Delta *self)
21302132

21312133
PyObject *current_mod = NULL;
21322134
datetime_state *st = GET_CURRENT_STATE(current_mod);
2135+
if (st == NULL) {
2136+
assert(current_mod == NULL);
2137+
return NULL;
2138+
}
21332139

21342140
x1 = PyLong_FromLong(GET_TD_DAYS(self));
21352141
if (x1 == NULL)
@@ -2209,6 +2215,10 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
22092215

22102216
PyObject *current_mod = NULL;
22112217
datetime_state *st = GET_CURRENT_STATE(current_mod);
2218+
if (st == NULL) {
2219+
assert(current_mod == NULL);
2220+
return NULL;
2221+
}
22122222

22132223
tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st));
22142224
if (tuple == NULL) {
@@ -2817,6 +2827,10 @@ delta_new_impl(PyTypeObject *type, PyObject *days, PyObject *seconds,
28172827

28182828
PyObject *current_mod = NULL;
28192829
datetime_state *st = GET_CURRENT_STATE(current_mod);
2830+
if (st == NULL) {
2831+
assert(current_mod == NULL);
2832+
return NULL;
2833+
}
28202834

28212835
PyObject *x = NULL; /* running sum of microseconds */
28222836
PyObject *y = NULL; /* temp sum of microseconds */
@@ -3016,6 +3030,10 @@ delta_total_seconds(PyObject *op, PyObject *Py_UNUSED(dummy))
30163030

30173031
PyObject *current_mod = NULL;
30183032
datetime_state *st = GET_CURRENT_STATE(current_mod);
3033+
if (st == NULL) {
3034+
assert(current_mod == NULL);
3035+
return NULL;
3036+
}
30193037

30203038
total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st));
30213039

@@ -3869,6 +3887,10 @@ date_isocalendar(PyObject *self, PyObject *Py_UNUSED(dummy))
38693887

38703888
PyObject *current_mod = NULL;
38713889
datetime_state *st = GET_CURRENT_STATE(current_mod);
3890+
if (st == NULL) {
3891+
assert(current_mod == NULL);
3892+
return NULL;
3893+
}
38723894

38733895
PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st),
38743896
year, week + 1, day + 1);
@@ -6802,6 +6824,10 @@ local_timezone(PyDateTime_DateTime *utc_time)
68026824

68036825
PyObject *current_mod = NULL;
68046826
datetime_state *st = GET_CURRENT_STATE(current_mod);
6827+
if (st == NULL) {
6828+
assert(current_mod == NULL);
6829+
return NULL;
6830+
}
68056831

68066832
delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st));
68076833
RELEASE_CURRENT_STATE(st, current_mod);
@@ -7049,6 +7075,10 @@ datetime_timestamp(PyObject *op, PyObject *Py_UNUSED(dummy))
70497075
if (HASTZINFO(self) && self->tzinfo != Py_None) {
70507076
PyObject *current_mod = NULL;
70517077
datetime_state *st = GET_CURRENT_STATE(current_mod);
7078+
if (st == NULL) {
7079+
assert(current_mod == NULL);
7080+
return NULL;
7081+
}
70527082

70537083
PyObject *delta;
70547084
delta = datetime_subtract(op, CONST_EPOCH(st));

0 commit comments

Comments
 (0)