From 7211eb1f879b76b453b780e33f215a2cd4773ac4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 21 May 2024 23:08:58 -0400 Subject: [PATCH 1/5] gh-117398: Add multiphase support to _datetime --- Modules/_datetimemodule.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 9a66f0358179b5..653931d569c451 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6992,30 +6992,26 @@ _datetime_exec(PyObject *module) } #undef DATETIME_ADD_MACRO -static struct PyModuleDef datetimemodule = { +static PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, _datetime_exec}, + {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + +static PyModuleDef datetimemodule = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_datetime", .m_doc = "Fast implementation of the datetime type.", - .m_size = -1, + .m_size = 0, .m_methods = module_methods, + .m_slots = module_slots, }; PyMODINIT_FUNC PyInit__datetime(void) { - PyObject *mod = PyModule_Create(&datetimemodule); - if (mod == NULL) - return NULL; -#ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED); -#endif - - if (_datetime_exec(mod) < 0) { - Py_DECREF(mod); - return NULL; - } - - return mod; + return PyModuleDef_Init(&datetimemodule); } /* --------------------------------------------------------------------------- From 863ecd71d302896cdb964d4ae2e6b117c2a90eae Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 22 May 2024 10:02:13 -0400 Subject: [PATCH 2/5] Kinda fix the C API tests --- Lib/test/datetimetester.py | 6 +++++- Modules/_testcapi/datetime.c | 34 +++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index b3838d5b406e94..5806ff7f0dcaee 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -6384,6 +6384,7 @@ class IranTest(ZoneInfoTest): @unittest.skipIf(_testcapi is None, 'need _testcapi module') class CapiTest(unittest.TestCase): + def setUp(self): # Since the C API is not present in the _Pure tests, skip all tests if self.__class__.__name__.endswith('Pure'): @@ -6391,7 +6392,10 @@ def setUp(self): # This *must* be called, and it must be called first, so until either # restriction is loosened, we'll call it as part of test setup - _testcapi.test_datetime_capi() + _testcapi.setup_capi() + + def tearDown(self): + _testcapi.teardown_capi() def test_utc_capi(self): for use_macro in (True, False): diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index b1796039f0d83a..325a4a971a85a0 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -6,26 +6,21 @@ static int test_run_counter = 0; static PyObject * -test_datetime_capi(PyObject *self, PyObject *args) +setup_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++; - PyDateTime_IMPORT; - - if (PyDateTimeAPI) { - Py_RETURN_NONE; + if (!test_run_counter++) { + PyDateTime_IMPORT; } - return NULL; + assert(PyDateTimeAPI); + Py_RETURN_NONE; +} + +static PyObject * +teardown_capi(PyObject *self, PyObject *args) +{ + test_run_counter--; + assert(test_run_counter >= 0); + Py_RETURN_NONE; } /* Functions exposing the C API type checking for testing */ @@ -467,7 +462,8 @@ static PyMethodDef test_methods[] = { {"get_timezone_utc_capi", get_timezone_utc_capi, METH_VARARGS}, {"get_timezones_offset_zero", get_timezones_offset_zero, METH_NOARGS}, {"make_timezones_capi", make_timezones_capi, METH_NOARGS}, - {"test_datetime_capi", test_datetime_capi, METH_NOARGS}, + {"setup_capi", setup_capi, METH_NOARGS}, + {"teardown_capi", teardown_capi, METH_NOARGS}, {NULL}, }; From 8de63177e7a503cbaa44c328707d938d2ae690f3 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 22 May 2024 11:27:57 -0400 Subject: [PATCH 3/5] Revert "Kinda fix the C API tests" This reverts commit 863ecd71d302896cdb964d4ae2e6b117c2a90eae. --- Lib/test/datetimetester.py | 6 +----- Modules/_testcapi/datetime.c | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 5806ff7f0dcaee..b3838d5b406e94 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -6384,7 +6384,6 @@ class IranTest(ZoneInfoTest): @unittest.skipIf(_testcapi is None, 'need _testcapi module') class CapiTest(unittest.TestCase): - def setUp(self): # Since the C API is not present in the _Pure tests, skip all tests if self.__class__.__name__.endswith('Pure'): @@ -6392,10 +6391,7 @@ def setUp(self): # This *must* be called, and it must be called first, so until either # restriction is loosened, we'll call it as part of test setup - _testcapi.setup_capi() - - def tearDown(self): - _testcapi.teardown_capi() + _testcapi.test_datetime_capi() def test_utc_capi(self): for use_macro in (True, False): diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 325a4a971a85a0..b1796039f0d83a 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -6,21 +6,26 @@ static int test_run_counter = 0; static PyObject * -setup_capi(PyObject *self, PyObject *args) +test_datetime_capi(PyObject *self, PyObject *args) { - if (!test_run_counter++) { - PyDateTime_IMPORT; + 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) { + Py_RETURN_NONE; } - assert(PyDateTimeAPI); - Py_RETURN_NONE; -} - -static PyObject * -teardown_capi(PyObject *self, PyObject *args) -{ - test_run_counter--; - assert(test_run_counter >= 0); - Py_RETURN_NONE; + return NULL; } /* Functions exposing the C API type checking for testing */ @@ -462,8 +467,7 @@ static PyMethodDef test_methods[] = { {"get_timezone_utc_capi", get_timezone_utc_capi, METH_VARARGS}, {"get_timezones_offset_zero", get_timezones_offset_zero, METH_NOARGS}, {"make_timezones_capi", make_timezones_capi, METH_NOARGS}, - {"setup_capi", setup_capi, METH_NOARGS}, - {"teardown_capi", teardown_capi, METH_NOARGS}, + {"test_datetime_capi", test_datetime_capi, METH_NOARGS}, {NULL}, }; From 0c091eb0abb10b3d0734c67a9395e879197af83a Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 22 May 2024 11:31:22 -0400 Subject: [PATCH 4/5] Skip encapsulated C API tests on reruns --- Lib/test/datetimetester.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index b3838d5b406e94..ddadef154bd289 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -23,6 +23,7 @@ from test import support from test.support import is_resource_enabled, ALWAYS_EQ, LARGEST, SMALLEST from test.support import warnings_helper +from test.test_import import no_rerun import datetime as datetime_module from datetime import MINYEAR, MAXYEAR @@ -6383,6 +6384,7 @@ class IranTest(ZoneInfoTest): @unittest.skipIf(_testcapi is None, 'need _testcapi module') +@no_rerun("the encapsulated datetime C API does not support reloading") class CapiTest(unittest.TestCase): def setUp(self): # Since the C API is not present in the _Pure tests, skip all tests From 90ff7d6835449afa4a5a11a196aa7dee2801d983 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 27 May 2024 14:05:41 -0600 Subject: [PATCH 5/5] Copy no_rerun(). --- Lib/test/datetimetester.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index ddadef154bd289..ba7f185a092629 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -23,7 +23,6 @@ from test import support from test.support import is_resource_enabled, ALWAYS_EQ, LARGEST, SMALLEST from test.support import warnings_helper -from test.test_import import no_rerun import datetime as datetime_module from datetime import MINYEAR, MAXYEAR @@ -48,6 +47,26 @@ pass # +# This is copied from test_import/__init__.py. +# XXX Move it to support/__init__.py. +def no_rerun(reason): + """Skip rerunning for a particular test. + + WARNING: Use this decorator with care; skipping rerunning makes it + impossible to find reference leaks. Provide a clear reason for skipping the + test using the 'reason' parameter. + """ + def deco(func): + _has_run = False + def wrapper(self): + nonlocal _has_run + if _has_run: + self.skipTest(reason) + func(self) + _has_run = True + return wrapper + return deco + pickle_loads = {pickle.loads, pickle._loads} pickle_choices = [(pickle, pickle, proto)