From 77d117c145ee7a2ced7f716e8b1815fb5d7cf587 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 12 Jul 2025 09:47:12 -0400 Subject: [PATCH 1/6] Load _datetime during interpreter initialization --- Include/internal/pycore_pylifecycle.h | 1 + Modules/Setup.bootstrap.in | 2 ++ Modules/Setup.stdlib.in | 3 --- Modules/_datetimemodule.c | 17 +++++------------ Python/pylifecycle.c | 5 +++++ 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 6e89ca33e4208c..2a5986ad9941ee 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -41,6 +41,7 @@ extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); +extern PyStatus _PyDateTime_Init(PyInterpreterState *interp); /* Various internal finalizers */ diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index 2b2e8cb3e3cacd..65a1fefe72e92e 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -12,6 +12,8 @@ posix posixmodule.c _signal signalmodule.c _tracemalloc _tracemalloc.c _suggestions _suggestions.c +# needs libm and on some platforms librt +_datetime _datetimemodule.c # modules used by importlib, deepfreeze, freeze, runpy, and sysconfig _codecs _codecsmodule.c diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 3a38a60a152e8c..905ea4aa2e57a9 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -56,9 +56,6 @@ @MODULE_CMATH_TRUE@cmath cmathmodule.c @MODULE__STATISTICS_TRUE@_statistics _statisticsmodule.c -# needs libm and on some platforms librt -@MODULE__DATETIME_TRUE@_datetime _datetimemodule.c - # _decimal uses libmpdec # either static libmpdec.a from Modules/_decimal/libmpdec or libmpdec.so # with ./configure --with-system-libmpdec diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 7a6426593d021f..a328e54e8ae3fe 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -14,6 +14,7 @@ #include "pycore_object.h" // _PyObject_Init() #include "pycore_time.h" // _PyTime_ObjectToTime_t() #include "pycore_unicodeobject.h" // _PyUnicode_Copy() +#include "pycore_initconfig.h" // _PyStatus_OK() #include "datetime.h" @@ -7329,13 +7330,9 @@ clear_state(datetime_state *st) } -static int -init_static_types(PyInterpreterState *interp, int reloading) +PyStatus +_PyDateTime_Init(PyInterpreterState *interp) { - if (reloading) { - return 0; - } - // `&...` is not a constant expression according to a strict reading // of C standards. Fill tp_base at run-time rather than statically. // See https://bugs.python.org/issue40777 @@ -7347,11 +7344,11 @@ init_static_types(PyInterpreterState *interp, int reloading) for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { PyTypeObject *type = capi_types[i]; if (_PyStaticType_InitForExtension(interp, type) < 0) { - return -1; + return _PyStatus_ERR("could not initialize static types"); } } - return 0; + return _PyStatus_OK(); } @@ -7379,10 +7376,6 @@ _datetime_exec(PyObject *module) } /* We actually set the "current" module right before a successful return. */ - if (init_static_types(interp, reloading) < 0) { - goto error; - } - for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { PyTypeObject *type = capi_types[i]; const char *name = _PyType_Name(type); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 00e8d030765560..7ceba7ab6d0ff4 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -915,6 +915,11 @@ pycore_interp_init(PyThreadState *tstate) goto done; } + status = _PyDateTime_Init(tstate->interp); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + const PyConfig *config = _PyInterpreterState_GetConfig(interp); status = _PyImport_InitCore(tstate, sysmod, config->_install_importlib); From 963a9ee58be7635b42c2dcc11c1fbef6500c09c5 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 12 Jul 2025 09:58:40 -0400 Subject: [PATCH 2/6] Add a test case. --- Lib/test/datetimetester.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 93b3382b9c654e..57255f45c4afc2 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3651,6 +3651,35 @@ def test_repr_subclass(self): td = SubclassDatetime(2010, 10, 2, second=3) self.assertEqual(repr(td), "SubclassDatetime(2010, 10, 2, 0, 0, 3)") + @support.cpython_only + def test_concurrent_initialization(self): + try: + from concurrent.futures import InterpreterPoolExecutor as _ + except ImportError: + self.skipTest("requires subinterpreters") + + try: + import _datetime as _ + except ImportError: + self.skipTest("requires C implementation of datetime") + + # Run in a subprocess to ensure we get a clean version of _datetime + script = """if True: + from concurrent.futures import InterpreterPoolExecutor + + def func(): + import _datetime + print('a', end='') + + with InterpreterPoolExecutor() as executor: + for _ in range(8): + executor.submit(func) + """ + rc, out, err = script_helper.assert_python_ok("-c", script) + self.assertEqual(rc, 0) + self.assertEqual(out, b"a" * 8) + self.assertEqual(err, b"") + class TestSubclassDateTime(TestDateTime): theclass = SubclassDatetime From e16fb5453e9fdefc79c630c3a9ab403b1ecf1fc5 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 12 Jul 2025 09:59:19 -0400 Subject: [PATCH 3/6] Add blurb. --- .../2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst new file mode 100644 index 00000000000000..dcc73267a78546 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst @@ -0,0 +1 @@ +Fix crash when initializing :mod:`datetime` concurrently. From ed656820a919706499ec6e7f98831edc6422adc8 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 12 Jul 2025 10:06:06 -0400 Subject: [PATCH 4/6] Move to pycore_init_types() --- Include/internal/pycore_pylifecycle.h | 2 +- Modules/_datetimemodule.c | 2 +- Python/pylifecycle.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 2a5986ad9941ee..8faf7a4d403f84 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -41,7 +41,7 @@ extern PyStatus _Py_HashRandomization_Init(const PyConfig *); extern PyStatus _PyGC_Init(PyInterpreterState *interp); extern PyStatus _PyAtExit_Init(PyInterpreterState *interp); -extern PyStatus _PyDateTime_Init(PyInterpreterState *interp); +extern PyStatus _PyDateTime_InitTypes(PyInterpreterState *interp); /* Various internal finalizers */ diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index a328e54e8ae3fe..43dcbf503fee82 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -7331,7 +7331,7 @@ clear_state(datetime_state *st) PyStatus -_PyDateTime_Init(PyInterpreterState *interp) +_PyDateTime_InitTypes(PyInterpreterState *interp) { // `&...` is not a constant expression according to a strict reading // of C standards. Fill tp_base at run-time rather than statically. diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 7ceba7ab6d0ff4..e22a9cc1c75050 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -760,6 +760,11 @@ pycore_init_types(PyInterpreterState *interp) return status; } + status = _PyDateTime_InitTypes(interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + return _PyStatus_OK(); } @@ -915,11 +920,6 @@ pycore_interp_init(PyThreadState *tstate) goto done; } - status = _PyDateTime_Init(tstate->interp); - if (_PyStatus_EXCEPTION(status)) { - goto done; - } - const PyConfig *config = _PyInterpreterState_GetConfig(interp); status = _PyImport_InitCore(tstate, sysmod, config->_install_importlib); From 43b4843c82752a91b6cc39a82a90a75e447add12 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 12 Jul 2025 10:12:02 -0400 Subject: [PATCH 5/6] Fix lint. --- Lib/test/datetimetester.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 57255f45c4afc2..01bc6d2aa3c376 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3653,16 +3653,6 @@ def test_repr_subclass(self): @support.cpython_only def test_concurrent_initialization(self): - try: - from concurrent.futures import InterpreterPoolExecutor as _ - except ImportError: - self.skipTest("requires subinterpreters") - - try: - import _datetime as _ - except ImportError: - self.skipTest("requires C implementation of datetime") - # Run in a subprocess to ensure we get a clean version of _datetime script = """if True: from concurrent.futures import InterpreterPoolExecutor From 0ad304fa2121a2676efd3c7ee5c96725b1f56bff Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 12 Jul 2025 10:22:27 -0400 Subject: [PATCH 6/6] Add _datetime to the frozen modules. I have no idea if this will fix the Windows build, but let's hope. --- PCbuild/_freeze_module.vcxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index efff6a58d895cb..5ceddf759b8f3b 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -106,6 +106,7 @@ +