From a9dee17af5721656ecbc20c173f494567cd3bf66 Mon Sep 17 00:00:00 2001 From: neonene Date: Mon, 1 Apr 2024 14:05:14 +0900 Subject: [PATCH 01/13] do not cache sub interp capi --- Include/datetime.h | 20 ++++++++++++--- Lib/test/test_capi/test_misc.py | 16 ++++++++++++ Modules/_testmultiphase.c | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/Include/datetime.h b/Include/datetime.h index b78cc0e8e2e5ac..46a743bfd492b8 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -194,10 +194,24 @@ typedef struct { * */ #ifndef _PY_DATETIME_IMPL /* Define global variable for the C API and a macro for setting it. */ -static PyDateTime_CAPI *PyDateTimeAPI = NULL; +static PyDateTime_CAPI *_pydatetimeapi_main = NULL; -#define PyDateTime_IMPORT \ - PyDateTimeAPI = (PyDateTime_CAPI *)PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0) +static inline void _import_pydatetime(void) { + if (PyInterpreterState_Get() == PyInterpreterState_Main()) { + _pydatetimeapi_main = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); + } +} +#define PyDateTime_IMPORT _import_pydatetime() + +static inline PyDateTime_CAPI *_get_pydatetime_api(void) { + if (PyInterpreterState_Get() == PyInterpreterState_Main()) { + return _pydatetimeapi_main; + } + else { + return PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); + } +} +#define PyDateTimeAPI _get_pydatetime_api() /* Macro for access to the UTC singleton */ #define PyDateTime_TimeZone_UTC PyDateTimeAPI->TimeZone_UTC diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 55a1ab6d6d9359..dd5d57f2a05ec1 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2203,6 +2203,22 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) + @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") + def test_datetime_capi(self): + script = textwrap.dedent(""" + import importlib.machinery + import importlib.util + fullname = '_test_datetime_capi' + origin = importlib.util.find_spec('_testmultiphase').origin + loader = importlib.machinery.ExtensionFileLoader(fullname, origin) + spec = importlib.util.spec_from_loader(fullname, loader) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + """) + exec(script) + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + @requires_subinterpreters class InterpreterIDTests(unittest.TestCase): diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 21c5f696a4f2ec..2220cf408586a5 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -952,3 +952,46 @@ PyInit__test_shared_gil_only(void) { return PyModuleDef_Init(&shared_gil_only_def); } + + +#include "datetime.h" + +static int +datetime_capi_exec(PyObject *m) +{ + _pydatetimeapi_main = NULL; + PyDateTime_IMPORT; + if (PyDateTimeAPI == NULL) { + return -1; + } + if (PyDateTimeAPI != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { + return -1; + } + if (PyInterpreterState_Get() == PyInterpreterState_Main()) { + if (PyDateTimeAPI != _pydatetimeapi_main) { + return -1; + } + } + else { + if (PyDateTimeAPI == _pydatetimeapi_main) { + return -1; + } + } + return 0; +} + +static PyModuleDef_Slot datetime_capi_slots[] = { + {Py_mod_exec, datetime_capi_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL}, +}; + +static PyModuleDef datetime_capi_def = TEST_MODULE_DEF("_test_datetime_capi", + datetime_capi_slots, + testexport_methods); + +PyMODINIT_FUNC +PyInit__test_datetime_capi(void) +{ + return PyModuleDef_Init(&datetime_capi_def); +} From 4c9d2001686c119a5e452b133c0f091d01514912 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:07:34 +0900 Subject: [PATCH 02/13] Apply PEP 7 Co-authored-by: Kirill Podoprigora --- Include/datetime.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Include/datetime.h b/Include/datetime.h index 46a743bfd492b8..76544a5ece7267 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -196,14 +196,16 @@ typedef struct { /* Define global variable for the C API and a macro for setting it. */ static PyDateTime_CAPI *_pydatetimeapi_main = NULL; -static inline void _import_pydatetime(void) { +static inline void +_import_pydatetime(void) { if (PyInterpreterState_Get() == PyInterpreterState_Main()) { _pydatetimeapi_main = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); } } #define PyDateTime_IMPORT _import_pydatetime() -static inline PyDateTime_CAPI *_get_pydatetime_api(void) { +static inline PyDateTime_CAPI * +_get_pydatetime_api(void) { if (PyInterpreterState_Get() == PyInterpreterState_Main()) { return _pydatetimeapi_main; } From 836f5cf495a5f1da5326222b47bdb6c214cd61a8 Mon Sep 17 00:00:00 2001 From: neonene Date: Mon, 1 Apr 2024 21:57:58 +0900 Subject: [PATCH 03/13] non-null main on subinterp test --- Modules/_testmultiphase.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 2220cf408586a5..e0494566d0d2e7 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -959,7 +959,10 @@ PyInit__test_shared_gil_only(void) static int datetime_capi_exec(PyObject *m) { - _pydatetimeapi_main = NULL; + int ismain = PyInterpreterState_Get() == PyInterpreterState_Main(); + if (ismain) { + _pydatetimeapi_main = NULL; + } PyDateTime_IMPORT; if (PyDateTimeAPI == NULL) { return -1; @@ -967,7 +970,7 @@ datetime_capi_exec(PyObject *m) if (PyDateTimeAPI != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { return -1; } - if (PyInterpreterState_Get() == PyInterpreterState_Main()) { + if (ismain) { if (PyDateTimeAPI != _pydatetimeapi_main) { return -1; } From 34f5a6f7cbcc9d0c0c8e966909b38ec230e1bc31 Mon Sep 17 00:00:00 2001 From: neonene Date: Mon, 1 Apr 2024 22:19:58 +0900 Subject: [PATCH 04/13] don't touch main-ptr during tests --- Modules/_testmultiphase.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index e0494566d0d2e7..53841ecd84f256 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -959,10 +959,6 @@ PyInit__test_shared_gil_only(void) static int datetime_capi_exec(PyObject *m) { - int ismain = PyInterpreterState_Get() == PyInterpreterState_Main(); - if (ismain) { - _pydatetimeapi_main = NULL; - } PyDateTime_IMPORT; if (PyDateTimeAPI == NULL) { return -1; @@ -970,7 +966,7 @@ datetime_capi_exec(PyObject *m) if (PyDateTimeAPI != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { return -1; } - if (ismain) { + if (PyInterpreterState_Get() == PyInterpreterState_Main()) { if (PyDateTimeAPI != _pydatetimeapi_main) { return -1; } From 321ea7d289455691450c31bbd61876bdc5139055 Mon Sep 17 00:00:00 2001 From: neonene Date: Mon, 1 Apr 2024 23:08:55 +0900 Subject: [PATCH 05/13] fix test_datetime_capi() --- Modules/_testcapi/datetime.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index b1796039f0d83a..9803a830a25af1 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -3,23 +3,11 @@ #include "datetime.h" // PyDateTimeAPI -static int test_run_counter = 0; - static PyObject * test_datetime_capi(PyObject *self, PyObject *args) { - if (PyDateTimeAPI) { - if (test_run_counter) { - /* Probably regrtest.py -R */ - Py_RETURN_NONE; - } - else { - PyErr_SetString(PyExc_AssertionError, - "PyDateTime_CAPI somehow initialized"); - return NULL; - } - } - test_run_counter++; + // PyDateTimeAPI cannot be carried over + // with multi-phase init enabled. PyDateTime_IMPORT; if (PyDateTimeAPI) { From 39186c2f8c6ed80d677fdceed69c864793c7c7cf Mon Sep 17 00:00:00 2001 From: neonene Date: Tue, 2 Apr 2024 00:44:52 +0900 Subject: [PATCH 06/13] check if legacy module or not --- Modules/_testmultiphase.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 53841ecd84f256..65b03eff09fda0 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -973,7 +973,20 @@ datetime_capi_exec(PyObject *m) } else { if (PyDateTimeAPI == _pydatetimeapi_main) { - return -1; + PyObject *module = PyImport_ImportModule("_datetime"); + if (module == NULL) { + return -1; + } + PyModuleDef *def = PyModule_GetDef(module); + Py_DECREF(module); + if (def) { + // multi-phase init + return -1; + } + else { + // legacy init (shared module) + return 0; + } } } return 0; From b8e545fd29ef6340c2b7de58f227a7a406c40731 Mon Sep 17 00:00:00 2001 From: neonene Date: Tue, 2 Apr 2024 01:24:56 +0900 Subject: [PATCH 07/13] try again 836f5cf --- Modules/_testmultiphase.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 65b03eff09fda0..d6d333fd47288e 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -959,6 +959,11 @@ PyInit__test_shared_gil_only(void) static int datetime_capi_exec(PyObject *m) { + int ismain = PyInterpreterState_Get() == PyInterpreterState_Main(); + if (ismain) { + _pydatetimeapi_main = NULL; + } + PyDateTime_IMPORT; if (PyDateTimeAPI == NULL) { return -1; @@ -966,7 +971,7 @@ datetime_capi_exec(PyObject *m) if (PyDateTimeAPI != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { return -1; } - if (PyInterpreterState_Get() == PyInterpreterState_Main()) { + if (ismain) { if (PyDateTimeAPI != _pydatetimeapi_main) { return -1; } From d2de01a8299050afdee88a1c8b25f808172e116f Mon Sep 17 00:00:00 2001 From: neonene Date: Tue, 2 Apr 2024 14:03:37 +0900 Subject: [PATCH 08/13] renames --- Lib/test/test_capi/test_misc.py | 6 +++--- Modules/_testmultiphase.c | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index dd5d57f2a05ec1..9e38ce9992d971 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2204,18 +2204,18 @@ def test_module_state_shared_in_global(self): self.assertEqual(main_attr_id, subinterp_attr_id) @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") - def test_datetime_capi(self): + def test_datetime_capi_client(self): script = textwrap.dedent(""" import importlib.machinery import importlib.util - fullname = '_test_datetime_capi' + fullname = '_test_datetime_capi_client' origin = importlib.util.find_spec('_testmultiphase').origin loader = importlib.machinery.ExtensionFileLoader(fullname, origin) spec = importlib.util.spec_from_loader(fullname, loader) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) """) - exec(script) + exec(script) # run main interp first ret = support.run_in_subinterp(script) self.assertEqual(ret, 0) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index d6d333fd47288e..e09de1d5a7b793 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -957,7 +957,7 @@ PyInit__test_shared_gil_only(void) #include "datetime.h" static int -datetime_capi_exec(PyObject *m) +datetime_capi_client_exec(PyObject *m) { int ismain = PyInterpreterState_Get() == PyInterpreterState_Main(); if (ismain) { @@ -997,18 +997,17 @@ datetime_capi_exec(PyObject *m) return 0; } -static PyModuleDef_Slot datetime_capi_slots[] = { - {Py_mod_exec, datetime_capi_exec}, +static PyModuleDef_Slot datetime_capi_client_slots[] = { + {Py_mod_exec, datetime_capi_client_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {0, NULL}, }; -static PyModuleDef datetime_capi_def = TEST_MODULE_DEF("_test_datetime_capi", - datetime_capi_slots, - testexport_methods); +static PyModuleDef datetime_capi_client_def = TEST_MODULE_DEF( + "_testmultiphase_datetime_capi_client", datetime_capi_client_slots, NULL); PyMODINIT_FUNC -PyInit__test_datetime_capi(void) +PyInit__test_datetime_capi_client(void) { - return PyModuleDef_Init(&datetime_capi_def); + return PyModuleDef_Init(&datetime_capi_client_def); } From 1f902728f5b4643c31bece0f5025192ef025d0ee Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 3 Apr 2024 22:57:00 +0900 Subject: [PATCH 09/13] globals-to-fix.tsv --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 65f94e50e1bd7d..f731d3517c8005 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -446,6 +446,7 @@ Modules/_tkinter.c - trbInCmd - ## other Include/datetime.h - PyDateTimeAPI - +Include/datetime.h - _pydatetimeapi_main - Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized - Modules/_ctypes/malloc_closure.c - _pagesize - Modules/_cursesmodule.c - initialised - From 777fdbb361c0381cd18b077b27a31a72c9a494ac Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 3 Apr 2024 23:13:40 +0900 Subject: [PATCH 10/13] edit globals-to-fix --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 1 file changed, 1 deletion(-) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index f731d3517c8005..7ba2448afbff2e 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -445,7 +445,6 @@ Modules/_tkinter.c - trbInCmd - ## initialized once ## other -Include/datetime.h - PyDateTimeAPI - Include/datetime.h - _pydatetimeapi_main - Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized - Modules/_ctypes/malloc_closure.c - _pagesize - From da769ba5e8cf1da46513a6afb38c3df7b5470fa5 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Mon, 29 Apr 2024 00:39:56 +0900 Subject: [PATCH 11/13] fix gil-disabled test --- Modules/_testmultiphase.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index e09de1d5a7b793..a8ed507f8815d0 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -965,19 +965,20 @@ datetime_capi_client_exec(PyObject *m) } PyDateTime_IMPORT; - if (PyDateTimeAPI == NULL) { - return -1; - } - if (PyDateTimeAPI != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { + PyErr_Clear(); + PyDateTime_CAPI *capi = PyDateTimeAPI; + PyErr_Clear(); + if (capi != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { return -1; } + PyErr_Clear(); if (ismain) { - if (PyDateTimeAPI != _pydatetimeapi_main) { + if (capi != _pydatetimeapi_main) { return -1; } } else { - if (PyDateTimeAPI == _pydatetimeapi_main) { + if (capi == _pydatetimeapi_main) { PyObject *module = PyImport_ImportModule("_datetime"); if (module == NULL) { return -1; From fb2145cb3fa7c067658196cf4e3481a4c6ee2f9e Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 2 May 2024 00:28:00 +0900 Subject: [PATCH 12/13] use cache instead of interp state --- Include/datetime.h | 31 ++++---- Lib/test/datetimetester.py | 2 +- Lib/test/test_capi/test_misc.py | 1 + Modules/_datetimemodule.c | 59 ++++++++++++++++ Modules/_testcapi/datetime.c | 16 ++++- Modules/_testmultiphase.c | 78 +++++++++++++-------- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 +- Tools/c-analyzer/cpython/ignored.tsv | 1 + 8 files changed, 139 insertions(+), 51 deletions(-) diff --git a/Include/datetime.h b/Include/datetime.h index 76544a5ece7267..54d80303a281ce 100644 --- a/Include/datetime.h +++ b/Include/datetime.h @@ -186,34 +186,31 @@ typedef struct { } PyDateTime_CAPI; #define PyDateTime_CAPSULE_NAME "datetime.datetime_CAPI" - +#define PyDateTime_INTERNAL_CAPSULE_NAME "datetime.datetime_CAPI_INTERNAL" /* This block is only used as part of the public API and should not be * included in _datetimemodule.c, which does not use the C API capsule. * See bpo-35081 for more details. * */ #ifndef _PY_DATETIME_IMPL -/* Define global variable for the C API and a macro for setting it. */ -static PyDateTime_CAPI *_pydatetimeapi_main = NULL; +static PyDateTime_CAPI * +_PyDateTimeAPI_not_ready(void) +{ + return NULL; +} +static PyDateTime_CAPI *(*_PyDateTimeAPI_Get)(void) = _PyDateTimeAPI_not_ready; static inline void -_import_pydatetime(void) { - if (PyInterpreterState_Get() == PyInterpreterState_Main()) { - _pydatetimeapi_main = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); +_PyDateTimeAPI_Import(void) +{ + void *(*func)(void) = PyCapsule_Import(PyDateTime_INTERNAL_CAPSULE_NAME, 0); + if (func) { + _PyDateTimeAPI_Get = func(); } } -#define PyDateTime_IMPORT _import_pydatetime() -static inline PyDateTime_CAPI * -_get_pydatetime_api(void) { - if (PyInterpreterState_Get() == PyInterpreterState_Main()) { - return _pydatetimeapi_main; - } - else { - return PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); - } -} -#define PyDateTimeAPI _get_pydatetime_api() +#define PyDateTimeAPI _PyDateTimeAPI_Get() +#define PyDateTime_IMPORT _PyDateTimeAPI_Import() /* Macro for access to the UTC singleton */ #define PyDateTime_TimeZone_UTC PyDateTimeAPI->TimeZone_UTC diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 570110893629cf..6946b394e68a82 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -91,7 +91,7 @@ def test_name_cleanup(self): if not name.startswith('__') and not name.endswith('__')) allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime', 'datetime_CAPI', 'time', 'timedelta', 'timezone', - 'tzinfo', 'UTC', 'sys']) + 'tzinfo', 'UTC', 'sys', 'datetime_CAPI_INTERNAL']) self.assertEqual(names - allowed, set([])) def test_divide_and_round(self): diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 9786e0d88cbf66..2cbaac346e915c 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2295,6 +2295,7 @@ def test_datetime_capi_client(self): spec.loader.exec_module(module) """) exec(script) # run main interp first + exec(script) # run main interp twice ret = support.run_in_subinterp(script) self.assertEqual(ret, 0) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 06004e258b2eff..43088f4b0b047c 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -63,6 +63,51 @@ static datetime_state _datetime_global_state; #define STATIC_STATE() (&_datetime_global_state) +typedef struct { + PyInterpreterState *interp; + PyDateTime_CAPI *capi; +} CAPI_Cache; + +static CAPI_Cache apicache[2]; + +static inline void +set_datetime_capi_by_interp(PyDateTime_CAPI *capi) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + int i = interp == PyInterpreterState_Main() ? 0 : 1; + apicache[i].interp = interp; + apicache[i].capi = capi; +} + +static PyDateTime_CAPI * +_PyDateTimeAPI_Get(void) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + for (int i = 0; i < 2; i++) { + if (apicache[i].interp == interp) { + return apicache[i].capi; + } + } + PyDateTime_CAPI *capi = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); + if (capi) { + set_datetime_capi_by_interp(capi); + } + return capi; +} + +static void * +_PyDateTimeAPI_Import(void) +{ + PyDateTime_CAPI *capi = PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0); + if (capi) { + // PyInit__datetime() is not called when the module is already loaded + // with single-phase init. + set_datetime_capi_by_interp(capi); + return _PyDateTimeAPI_Get; + } + return NULL; +} + /* We require that C int be at least 32 bits, and use int virtually * everywhere. In just a few cases we use a temp long, where a Python * API returns a C long. In such cases, we have to ensure that the @@ -6944,6 +6989,20 @@ _datetime_exec(PyObject *module) goto error; } + capsule = PyCapsule_New(_PyDateTimeAPI_Import, + PyDateTime_INTERNAL_CAPSULE_NAME, NULL); + if (capsule == NULL) { + PyMem_Free(capi); + goto error; + } + if (PyModule_Add(module, "datetime_CAPI_INTERNAL", capsule) < 0) { + PyMem_Free(capi); + goto error; + } + + /* Ensure that the newest capi is used on multi-phase init */ + set_datetime_capi_by_interp(capi); + /* A 4-year cycle has an extra leap day over what we'd get from * pasting together 4 single years. */ diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 9803a830a25af1..b1796039f0d83a 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -3,11 +3,23 @@ #include "datetime.h" // PyDateTimeAPI +static int test_run_counter = 0; + static PyObject * test_datetime_capi(PyObject *self, PyObject *args) { - // PyDateTimeAPI cannot be carried over - // with multi-phase init enabled. + if (PyDateTimeAPI) { + if (test_run_counter) { + /* Probably regrtest.py -R */ + Py_RETURN_NONE; + } + else { + PyErr_SetString(PyExc_AssertionError, + "PyDateTime_CAPI somehow initialized"); + return NULL; + } + } + test_run_counter++; PyDateTime_IMPORT; if (PyDateTimeAPI) { diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index a8ed507f8815d0..429824fbede1fa 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -957,44 +957,62 @@ PyInit__test_shared_gil_only(void) #include "datetime.h" static int -datetime_capi_client_exec(PyObject *m) +datetime_capi_import_with_error(void) { + static int is_datetime_multiphase = -1; int ismain = PyInterpreterState_Get() == PyInterpreterState_Main(); - if (ismain) { - _pydatetimeapi_main = NULL; + if (ismain && is_datetime_multiphase < 0) { + PyObject *module = PyImport_ImportModule("_datetime"); + if (module == NULL) { + return -1; + } + PyModuleDef *def = PyModule_GetDef(module); + Py_DECREF(module); + if (def && def->m_size >= 0) { + is_datetime_multiphase = 1; + } + else { + is_datetime_multiphase = 0; + } + } + if (is_datetime_multiphase < 0) { + PyErr_SetString(PyExc_AssertionError, + "Main interpreter must be loaded first."); + return -1; } - PyDateTime_IMPORT; - PyErr_Clear(); - PyDateTime_CAPI *capi = PyDateTimeAPI; - PyErr_Clear(); - if (capi != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { + _PyDateTimeAPI_Import(); + if (!PyErr_Occurred()) { + return 0; + } +#ifdef Py_GIL_DISABLED + if (!ismain && !is_datetime_multiphase) { + // _datetime module and Capsule are not imported + PyErr_WriteUnraisable(NULL); + return 0; + } +#endif + return -1; +} + +static int +datetime_capi_client_exec(PyObject *m) +{ + _PyDateTimeAPI_Get = _PyDateTimeAPI_not_ready; + if (_PyDateTimeAPI_Get() != NULL) { + PyErr_SetString(PyExc_AssertionError, + "DateTime API is expected to remain NULL."); return -1; } - PyErr_Clear(); - if (ismain) { - if (capi != _pydatetimeapi_main) { - return -1; - } + if (datetime_capi_import_with_error() < 0) { + return -1; } - else { - if (capi == _pydatetimeapi_main) { - PyObject *module = PyImport_ImportModule("_datetime"); - if (module == NULL) { - return -1; - } - PyModuleDef *def = PyModule_GetDef(module); - Py_DECREF(module); - if (def) { - // multi-phase init - return -1; - } - else { - // legacy init (shared module) - return 0; - } - } + if (PyDateTimeAPI != PyCapsule_Import(PyDateTime_CAPSULE_NAME, 0)) { + PyErr_SetString(PyExc_AssertionError, + "DateTime API does not match Capsule CAPI."); + return -1; } + PyErr_Clear(); return 0; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 1fc4e8ae43fb0e..3c0f3b39530d3f 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -446,7 +446,7 @@ Modules/_tkinter.c - trbInCmd - ## initialized once ## other -Include/datetime.h - _pydatetimeapi_main - +Include/datetime.h - _PyDateTimeAPI_Get - Modules/_ctypes/cfield.c _ctypes_get_fielddesc initialized - Modules/_ctypes/malloc_closure.c - _pagesize - Modules/_cursesmodule.c - initialised - diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 87b695de23e25e..11e56658951af8 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -591,6 +591,7 @@ Modules/_testmultiphase.c - slots_exec_unreported_exception - Modules/_testmultiphase.c - slots_nonmodule_with_exec_slots - Modules/_testmultiphase.c - testexport_methods - Modules/_testmultiphase.c - uninitialized_def - +Modules/_testmultiphase.c datetime_capi_import_with_error is_datetime_multiphase - Modules/_testsinglephase.c - global_state - Modules/_xxtestfuzz/_xxtestfuzz.c - _fuzzmodule - Modules/_xxtestfuzz/_xxtestfuzz.c - module_methods - From 041b1dd2a2979de9f6c0f560329b384d463d3298 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 2 May 2024 00:55:02 +0900 Subject: [PATCH 13/13] Update globals-to-fix.tsv --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 3c0f3b39530d3f..8f5a7048bcc5f1 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -464,6 +464,7 @@ Modules/readline.c - libedit_history_start - Modules/_ctypes/cfield.c - formattable - Modules/_ctypes/malloc_closure.c - free_list - Modules/_curses_panel.c - lop - +Modules/_datetimemodule.c - apicache - Modules/_ssl/debughelpers.c _PySSL_keylog_callback lock - Modules/_tkinter.c - quitMainLoop - Modules/_tkinter.c - errorInCmd -