https://github.com/python/cpython/commit/bdaf1520429a0eaa6d95e6757d5d953f5fa4106f commit: bdaf1520429a0eaa6d95e6757d5d953f5fa4106f branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: sobolevn <[email protected]> date: 2026-06-09T12:10:49Z summary:
[3.14] gh-151039: Fix a crash when `_datetime` types outlive `_datetime` module (GH-151044) (#151144) gh-151039: Fix a crash when `_datetime` types outlive `_datetime` module (GH-151044) (cherry picked from commit 9fdbade99e6bcc607d9f12416bfca5bbf94022b9) Co-authored-by: sobolevn <[email protected]> files: A Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst M Lib/test/datetimetester.py M Modules/_datetimemodule.c diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index c6534adb1fcf83..c1bb6138a1beb7 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -7313,6 +7313,36 @@ def func(): self.assertEqual(out, b"a" * 8) self.assertEqual(err, b"") + @support.cpython_only + @support.subTests(("setup", "call"), [ + ("obj = _datetime.timedelta", "obj(seconds=2)"), + ("obj = _datetime.timedelta(seconds=2)", "obj.total_seconds()"), + ("obj = _datetime.date(2026, 6, 7)", "obj.isocalendar()"), + ]) + def test_static_datetime_types_outlive_collected_module(self, setup, call): + # gh-151039: This code used to crash + script = f"""if True: + import sys, gc + import _datetime + + {setup} # static C type, survives the module + del sys.modules['_datetime'] + del _datetime + sys.modules['_datetime'] = None # block re-import + gc.collect() # module object is collected + + try: + {call} # used to be a segmentation fault + except ImportError: + pass + else: + raise AssertionError("ImportError not raised") + """ + rc, out, err = script_helper.assert_python_ok("-c", script) + self.assertEqual(rc, 0) + self.assertEqual(out, b'') + self.assertEqual(err, b'') + def load_tests(loader, standard_tests, pattern): standard_tests.addTest(ZoneInfoCompleteTest()) diff --git a/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst b/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst new file mode 100644 index 00000000000000..1e99567f555057 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst @@ -0,0 +1 @@ +Fix a crash when static :mod:`datetime` types outlive the ``_datetime`` module. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 7a00e78c039c34..b3b76acc97ee0a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -125,8 +125,8 @@ get_module_state(PyObject *module) #define INTERP_KEY ((PyObject *)&_Py_ID(cached_datetime_module)) -static PyObject * -get_current_module(PyInterpreterState *interp) +static int +get_current_module(PyInterpreterState *interp, PyObject **p_mod) { PyObject *mod = NULL; @@ -138,20 +138,24 @@ get_current_module(PyInterpreterState *interp) if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { goto error; } - if (ref != NULL) { - if (ref != Py_None) { - (void)PyWeakref_GetRef(ref, &mod); - if (mod == Py_None) { - Py_CLEAR(mod); - } + if (ref != NULL && ref != Py_None) { + if (PyWeakref_GetRef(ref, &mod) < 0) { Py_DECREF(ref); + goto error; + } + if (mod == Py_None) { + Py_CLEAR(mod); } + Py_DECREF(ref); } - return mod; + assert(!PyErr_Occurred()); + *p_mod = mod; + return mod != NULL; error: assert(PyErr_Occurred()); - return NULL; + *p_mod = NULL; + return -1; } static PyModuleDef datetimemodule; @@ -160,22 +164,26 @@ static datetime_state * _get_current_state(PyObject **p_mod) { PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *mod = get_current_module(interp); + PyObject *mod; + if (get_current_module(interp, &mod) < 0) { + goto error; + } if (mod == NULL) { - assert(!PyErr_Occurred()); - if (PyErr_Occurred()) { - return NULL; - } /* The static types can outlive the module, * so we must re-import the module. */ mod = PyImport_ImportModule("_datetime"); if (mod == NULL) { - return NULL; + goto error; } } datetime_state *st = get_module_state(mod); *p_mod = mod; return st; + +error: + assert(PyErr_Occurred()); + *p_mod = NULL; + return NULL; } #define GET_CURRENT_STATE(MOD_VAR) \ @@ -2120,8 +2128,11 @@ delta_to_microseconds(PyDateTime_Delta *self) PyObject *x3 = NULL; PyObject *result = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } x1 = PyLong_FromLong(GET_TD_DAYS(self)); if (x1 == NULL) @@ -2199,8 +2210,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *num = NULL; PyObject *result = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st)); if (tuple == NULL) { @@ -2786,8 +2800,11 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) { PyObject *self = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } /* Argument objects. */ PyObject *day = NULL; @@ -3005,8 +3022,12 @@ delta_total_seconds(PyObject *op, PyObject *Py_UNUSED(dummy)) if (total_microseconds == NULL) return NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + Py_DECREF(total_microseconds); + return NULL; + } total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st)); @@ -3788,8 +3809,11 @@ date_isocalendar(PyObject *self, PyObject *Py_UNUSED(dummy)) week = 0; } - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st), year, week + 1, day + 1); @@ -6621,8 +6645,11 @@ local_timezone(PyDateTime_DateTime *utc_time) PyObject *one_second; PyObject *seconds; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st)); RELEASE_CURRENT_STATE(st, current_mod); @@ -6865,8 +6892,11 @@ datetime_timestamp(PyObject *op, PyObject *Py_UNUSED(dummy)) PyObject *result; if (HASTZINFO(self) && self->tzinfo != Py_None) { - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *delta; delta = datetime_subtract(op, CONST_EPOCH(st)); @@ -7430,9 +7460,8 @@ _datetime_exec(PyObject *module) datetime_state *st = get_module_state(module); PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *old_module = get_current_module(interp); - if (PyErr_Occurred()) { - assert(old_module == NULL); + PyObject *old_module; + if (get_current_module(interp, &old_module) < 0) { goto error; } /* We actually set the "current" module right before a successful return. */ _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
