mirror of
https://github.com/python/cpython.git
synced 2026-06-19 08:02:29 +00:00
gh-151039: Fix a crash when _datetime types outlive _datetime module (#151044)
This commit is contained in:
parent
c3cd75afdf
commit
9fdbade99e
3 changed files with 86 additions and 26 deletions
|
|
@ -7509,6 +7509,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())
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Fix a crash when static :mod:`datetime` types outlive the ``_datetime`` module.
|
||||
|
|
@ -126,8 +126,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;
|
||||
|
||||
|
|
@ -139,20 +139,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;
|
||||
|
|
@ -161,22 +165,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) \
|
||||
|
|
@ -2128,8 +2136,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)
|
||||
|
|
@ -2207,8 +2218,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) {
|
||||
|
|
@ -2815,8 +2829,11 @@ delta_new_impl(PyTypeObject *type, PyObject *days, PyObject *seconds,
|
|||
{
|
||||
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 */
|
||||
|
|
@ -3014,8 +3031,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));
|
||||
|
||||
|
|
@ -3867,8 +3888,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);
|
||||
|
|
@ -6800,8 +6824,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);
|
||||
|
|
@ -7047,8 +7074,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));
|
||||
|
|
@ -7581,9 +7611,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. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue