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())
#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;
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;
_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) \
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)
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) {
{
PyObject *self = NULL;
- PyObject *current_mod = NULL;
+ PyObject *current_mod;
datetime_state *st = GET_CURRENT_STATE(current_mod);
+ if (st == NULL) {
+ return NULL;
+ }
PyObject *x = NULL; /* running sum of microseconds */
PyObject *y = NULL; /* temp sum of microseconds */
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));
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);
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);
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));
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. */