From 0db4a812924abd45c025bcbb73bb63d1bc9106e5 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Mon, 27 May 2024 21:22:39 +0900 Subject: [PATCH 01/34] Update _testmultiphase.c --- Modules/_testmultiphase.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 886b260aceb20d..f570097f8f350e 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -969,3 +969,36 @@ PyInit__test_shared_gil_only(void) { return PyModuleDef_Init(&shared_gil_only_def); } + + +#include "datetime.h" + +static PyObject * +datetime_capi_pydate_check(PyObject *self, PyObject *op) +{ + PyDateTime_IMPORT; + if (PyDate_Check(op)) { + Py_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "object is not instance of capi type"); + return NULL; +} + +static PyMethodDef datetime_capi_client_methods[] = { + {"pydate_check", datetime_capi_pydate_check, METH_O, NULL}, + {0}, +}; + +static PyModuleDef_Slot datetime_capi_client_slots[] = { + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL}, +}; + +static PyModuleDef datetime_capi_client_def = TEST_MODULE_DEF( + "_testmultiphase_datetime_capi_client", datetime_capi_client_slots, datetime_capi_client_methods); + +PyMODINIT_FUNC +PyInit__test_datetime_capi_client(void) +{ + return PyModuleDef_Init(&datetime_capi_client_def); +} From 593f05aee4efbc0118b12d77a25206b16dd3710a Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Mon, 27 May 2024 21:25:14 +0900 Subject: [PATCH 02/34] Update test_misc.py --- Lib/test/test_capi/test_misc.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ed42d7b64302f9..a57aa8519559d1 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,6 +2285,24 @@ 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_type_check(self): + script = textwrap.dedent(""" + import importlib.machinery + import importlib.util + 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) + import datetime + module.pydate_check(datetime.date.today()) + """) + exec(script) + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): From d85c8bca5d0c154fed184a54e63bcc68dbac12c4 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Mon, 27 May 2024 23:05:12 +0900 Subject: [PATCH 03/34] ingore import error on subinterp with GIL disabled --- Modules/_testmultiphase.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index f570097f8f350e..5a2367e7e03735 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -977,6 +977,13 @@ static PyObject * datetime_capi_pydate_check(PyObject *self, PyObject *op) { PyDateTime_IMPORT; +#ifdef Py_GIL_DISABLED + if (!PyDateTimeAPI && PyInterpreterState_Get() != PyInterpreterState_Main()) { + // Subinterpreter cannot import lagacy _datetime module and capsule. + PyErr_WriteUnraisable(NULL); + Py_RETURN_NONE; + } +#endif if (PyDate_Check(op)) { Py_RETURN_NONE; } From 9b667d735b55ca40a0dc41a61e14faa6a3e3407d Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 28 May 2024 12:16:10 +0900 Subject: [PATCH 04/34] revert --- Lib/test/test_capi/test_misc.py | 18 --------------- Modules/_testmultiphase.c | 40 --------------------------------- 2 files changed, 58 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index a57aa8519559d1..ed42d7b64302f9 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,24 +2285,6 @@ 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_type_check(self): - script = textwrap.dedent(""" - import importlib.machinery - import importlib.util - 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) - import datetime - module.pydate_check(datetime.date.today()) - """) - exec(script) - ret = support.run_in_subinterp(script) - self.assertEqual(ret, 0) - @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index 5a2367e7e03735..886b260aceb20d 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -969,43 +969,3 @@ PyInit__test_shared_gil_only(void) { return PyModuleDef_Init(&shared_gil_only_def); } - - -#include "datetime.h" - -static PyObject * -datetime_capi_pydate_check(PyObject *self, PyObject *op) -{ - PyDateTime_IMPORT; -#ifdef Py_GIL_DISABLED - if (!PyDateTimeAPI && PyInterpreterState_Get() != PyInterpreterState_Main()) { - // Subinterpreter cannot import lagacy _datetime module and capsule. - PyErr_WriteUnraisable(NULL); - Py_RETURN_NONE; - } -#endif - if (PyDate_Check(op)) { - Py_RETURN_NONE; - } - PyErr_SetString(PyExc_TypeError, "object is not instance of capi type"); - return NULL; -} - -static PyMethodDef datetime_capi_client_methods[] = { - {"pydate_check", datetime_capi_pydate_check, METH_O, NULL}, - {0}, -}; - -static PyModuleDef_Slot datetime_capi_client_slots[] = { - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {0, NULL}, -}; - -static PyModuleDef datetime_capi_client_def = TEST_MODULE_DEF( - "_testmultiphase_datetime_capi_client", datetime_capi_client_slots, datetime_capi_client_methods); - -PyMODINIT_FUNC -PyInit__test_datetime_capi_client(void) -{ - return PyModuleDef_Init(&datetime_capi_client_def); -} From 6a67b45e206805a71e6f41ed51c01124020ae765 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 28 May 2024 12:20:58 +0900 Subject: [PATCH 05/34] Update Modules/_testcapi/datetime.c --- Modules/_testcapi/datetime.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index b1796039f0d83a..b2003dfb895b16 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -43,33 +43,41 @@ do { \ Py_RETURN_FALSE; \ } while (0) \ +// The following C API types need to outlive interpreters, since the borrowed +// references to them can be held by users without being updated. + static PyObject * datetime_check_date(PyObject *self, PyObject *args) { + assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyDate_Check, PyDate_CheckExact); } static PyObject * datetime_check_time(PyObject *self, PyObject *args) { + assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyTime_Check, PyTime_CheckExact); } static PyObject * datetime_check_datetime(PyObject *self, PyObject *args) { + assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyDateTime_Check, PyDateTime_CheckExact); } static PyObject * datetime_check_delta(PyObject *self, PyObject *args) { + assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyDelta_Check, PyDelta_CheckExact); } static PyObject * datetime_check_tzinfo(PyObject *self, PyObject *args) { + assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyTZInfo_Check, PyTZInfo_CheckExact); } #undef MAKE_DATETIME_CHECK_FUNC From f21ef4a103b45ac68889664abae0160d64c01e66 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 29 May 2024 15:40:03 +0900 Subject: [PATCH 06/34] revert --- Modules/_testcapi/datetime.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index b2003dfb895b16..b1796039f0d83a 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -43,41 +43,33 @@ do { \ Py_RETURN_FALSE; \ } while (0) \ -// The following C API types need to outlive interpreters, since the borrowed -// references to them can be held by users without being updated. - static PyObject * datetime_check_date(PyObject *self, PyObject *args) { - assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyDate_Check, PyDate_CheckExact); } static PyObject * datetime_check_time(PyObject *self, PyObject *args) { - assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyTime_Check, PyTime_CheckExact); } static PyObject * datetime_check_datetime(PyObject *self, PyObject *args) { - assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyDateTime_Check, PyDateTime_CheckExact); } static PyObject * datetime_check_delta(PyObject *self, PyObject *args) { - assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyDelta_Check, PyDelta_CheckExact); } static PyObject * datetime_check_tzinfo(PyObject *self, PyObject *args) { - assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE)); MAKE_DATETIME_CHECK_FUNC(PyTZInfo_Check, PyTZInfo_CheckExact); } #undef MAKE_DATETIME_CHECK_FUNC From ac9ad4b31e4d9a9272d5db469f9a24bddf0b0340 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 29 May 2024 15:45:49 +0900 Subject: [PATCH 07/34] add test for subinterpreters --- Lib/test/test_capi/test_misc.py | 39 +++++++++++++++++++++++++ Modules/_testcapi/datetime.c | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ed42d7b64302f9..eb301b46c91e23 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,6 +2285,45 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) + def test_datetime_capi_type_check(self): + script = textwrap.dedent(""" + try: + import _datetime + except ImportError: + _datetime = None + + def run(type_checker, obj): + if not type_checker(obj, True): # exact check + raise TypeError(f'{type(obj)} is not C API type') + + if _datetime: + import importlib.machinery + import importlib.util + fullname = '_testcapi_datetime' + origin = importlib.util.find_spec('_testcapi').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) + + run(module.check_date, _datetime.date.today()) + run(module.check_datetime, _datetime.datetime.now()) + run(module.check_time, _datetime.time(12, 30)) + run(module.check_delta, _datetime.timedelta(1)) + run(module.check_tzinfo, _datetime.tzinfo()) + """) + with self.subTest("main interpreter"): + exec(script) + with self.subTest("non-isolated subinterpreter"): + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + if _interpreters: + with self.subTest("isolated subinterpreter"): + interpid = _interpreters.create() + ret = _interpreters.run_string(interpid, script) + _interpreters.destroy(interpid) + self.assertIsNone(ret) + @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index b1796039f0d83a..18355480d4b346 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -479,3 +479,54 @@ _PyTestCapi_Init_DateTime(PyObject *mod) } return 0; } + + +/* --------------------------------------------------------------------------- + * Test module for subinterpreters + */ + +static int +_testcapi_datetime_exec(PyObject *mod) +{ + PyDateTime_IMPORT; + if (PyDateTimeAPI == NULL) { + return -1; + } + // The following C API types need to outlive interpreters, since the + // borrowed references to them can be held by users without being updated. + assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE)); + return 0; +} + +static PyMethodDef _testcapi_datetime_methods[] = { + {"check_date", datetime_check_date, METH_VARARGS}, + {"check_datetime", datetime_check_datetime, METH_VARARGS}, + {"check_delta", datetime_check_delta, METH_VARARGS}, + {"check_time", datetime_check_time, METH_VARARGS}, + {"check_tzinfo", datetime_check_tzinfo, METH_VARARGS}, + {0}, +}; + +static PyModuleDef_Slot _testcapi_datetime_slots[] = { + {Py_mod_exec, _testcapi_datetime_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL}, +}; + +static struct PyModuleDef _testcapi_datetime_module = { + PyModuleDef_HEAD_INIT, + .m_name = "_testcapi_datetime", + .m_size = 0, + .m_methods = _testcapi_datetime_methods, + .m_slots = _testcapi_datetime_slots, +}; + +PyMODINIT_FUNC +PyInit__testcapi_datetime(void) +{ + return PyModuleDef_Init(&_testcapi_datetime_module); +} From f0654f59bf8d14b487f96c7679d8de2b52071c8a Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 29 May 2024 17:14:56 +0900 Subject: [PATCH 08/34] restore _testmultiphase check for WASI? --- Lib/test/test_capi/test_misc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index eb301b46c91e23..5a6098a6637269 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,6 +2285,7 @@ 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_type_check(self): script = textwrap.dedent(""" try: From 17cb0c12364a312ec152c0a71d9c602842b004bf Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 29 May 2024 17:29:34 +0900 Subject: [PATCH 09/34] use test_datetime_capi() instead of PyDateTime_IMPORT --- Modules/_testcapi/datetime.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 18355480d4b346..7e6bb8394c6394 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -488,7 +488,9 @@ _PyTestCapi_Init_DateTime(PyObject *mod) static int _testcapi_datetime_exec(PyObject *mod) { - PyDateTime_IMPORT; + if (test_datetime_capi(NULL, NULL) == NULL) { // PyDateTime_IMPORT + return -1; + } if (PyDateTimeAPI == NULL) { return -1; } From fdfe135b3d8fb16e6dfb04c8b0b44916af77a725 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 29 May 2024 17:45:49 +0900 Subject: [PATCH 10/34] remove redundant PyDateTimeAPI check --- Modules/_testcapi/datetime.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 7e6bb8394c6394..7fcc245b1ca251 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -491,9 +491,6 @@ _testcapi_datetime_exec(PyObject *mod) if (test_datetime_capi(NULL, NULL) == NULL) { // PyDateTime_IMPORT return -1; } - if (PyDateTimeAPI == NULL) { - return -1; - } // The following C API types need to outlive interpreters, since the // borrowed references to them can be held by users without being updated. assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE)); From c7d9de44be81d10f1e8dd5ddcd43ace083a78cd0 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 29 May 2024 17:46:38 +0900 Subject: [PATCH 11/34] add Py_MOD_GIL_NOT_USED --- Modules/_testcapi/datetime.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 7fcc245b1ca251..c2f0bfcfcb663c 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -513,6 +513,7 @@ static PyMethodDef _testcapi_datetime_methods[] = { static PyModuleDef_Slot _testcapi_datetime_slots[] = { {Py_mod_exec, _testcapi_datetime_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; From b89801c679f44d4d75e32bd4713567183a6ae58f Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 30 May 2024 12:43:26 +0900 Subject: [PATCH 12/34] ignore dlopen error on WASI --- Lib/test/test_capi/test_misc.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 5a6098a6637269..f13e795a605807 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,9 +2285,8 @@ 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_type_check(self): - script = textwrap.dedent(""" + script = textwrap.dedent(f""" try: import _datetime except ImportError: @@ -2295,8 +2294,9 @@ def test_datetime_capi_type_check(self): def run(type_checker, obj): if not type_checker(obj, True): # exact check - raise TypeError(f'{type(obj)} is not C API type') + raise TypeError(f'{{type(obj)}} is not C API type') + module = None if _datetime: import importlib.machinery import importlib.util @@ -2304,9 +2304,15 @@ def run(type_checker, obj): origin = importlib.util.find_spec('_testcapi').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) + try: + module = importlib.util.module_from_spec(spec) + except ImportError: + is_wasi = {support.is_wasi} # no dlopen currently + if not is_wasi: + raise + if _datetime and module: + spec.loader.exec_module(module) run(module.check_date, _datetime.date.today()) run(module.check_datetime, _datetime.datetime.now()) run(module.check_time, _datetime.time(12, 30)) From dc9c5e3108972e52dc35108f014c9d57e48c9e84 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 30 May 2024 19:51:42 +0900 Subject: [PATCH 13/34] edit comment --- Lib/test/test_capi/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f13e795a605807..947f466d784b8e 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2307,7 +2307,7 @@ def run(type_checker, obj): try: module = importlib.util.module_from_spec(spec) except ImportError: - is_wasi = {support.is_wasi} # no dlopen currently + is_wasi = {support.is_wasi} # current WASI has no dlopen if not is_wasi: raise From 6d57d33dd781f44c6fb08905fc977ad0e0f1d773 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Fri, 31 May 2024 04:06:49 +0900 Subject: [PATCH 14/34] rely on support module to run subinterps --- Lib/test/test_capi/test_misc.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 947f466d784b8e..1f754daaabf1b0 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2321,15 +2321,34 @@ def run(type_checker, obj): """) with self.subTest("main interpreter"): exec(script) - with self.subTest("non-isolated subinterpreter"): + with self.subTest("legacy subinterpreter"): ret = support.run_in_subinterp(script) self.assertEqual(ret, 0) if _interpreters: - with self.subTest("isolated subinterpreter"): - interpid = _interpreters.create() - ret = _interpreters.run_string(interpid, script) - _interpreters.destroy(interpid) - self.assertIsNone(ret) + with self.subTest("legacy subinterpreter (config)"): + ret = support.run_in_subinterp_with_config( + script, + use_main_obmalloc=True, + own_gil=False, + allow_fork=True, + allow_exec=True, + allow_threads=True, + allow_daemon_threads=True, + check_multi_interp_extensions=bool(Py_GIL_DISABLED), + ) + self.assertEqual(ret, 0) + with self.subTest("isolated subinterpreter (config)"): + ret = support.run_in_subinterp_with_config( + script, + own_gil=True, + use_main_obmalloc=False, + allow_fork=False, + allow_exec=False, + allow_threads=True, + allow_daemon_threads=False, + check_multi_interp_extensions=True, + ) + self.assertEqual(ret, 0) @requires_subinterpreters From 4a04542f8cbb05c95a4f420dd287dc07473f9a0d Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Fri, 31 May 2024 04:36:31 +0900 Subject: [PATCH 15/34] nit --- Lib/test/test_capi/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 1f754daaabf1b0..56fbbd99952da0 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2328,8 +2328,8 @@ def run(type_checker, obj): with self.subTest("legacy subinterpreter (config)"): ret = support.run_in_subinterp_with_config( script, - use_main_obmalloc=True, own_gil=False, + use_main_obmalloc=True, allow_fork=True, allow_exec=True, allow_threads=True, From ba08b04046cc2d7c6fb0e37ce712d5d1ad40f833 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:13:57 +0900 Subject: [PATCH 16/34] add blank lines --- Lib/test/test_capi/test_misc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 56fbbd99952da0..f1e473ccfa0f33 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2321,9 +2321,11 @@ def run(type_checker, obj): """) with self.subTest("main interpreter"): exec(script) + with self.subTest("legacy subinterpreter"): ret = support.run_in_subinterp(script) self.assertEqual(ret, 0) + if _interpreters: with self.subTest("legacy subinterpreter (config)"): ret = support.run_in_subinterp_with_config( @@ -2337,6 +2339,7 @@ def run(type_checker, obj): check_multi_interp_extensions=bool(Py_GIL_DISABLED), ) self.assertEqual(ret, 0) + with self.subTest("isolated subinterpreter (config)"): ret = support.run_in_subinterp_with_config( script, From 8c26d7ef2a8336e712c81b49fbb936b25ae3ef74 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sun, 2 Jun 2024 01:19:22 +0900 Subject: [PATCH 17/34] simplify WASI check --- Lib/test/test_capi/test_misc.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f1e473ccfa0f33..442da03b32850d 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2286,33 +2286,30 @@ def test_module_state_shared_in_global(self): self.assertEqual(main_attr_id, subinterp_attr_id) def test_datetime_capi_type_check(self): - script = textwrap.dedent(f""" + script = textwrap.dedent(""" try: import _datetime except ImportError: _datetime = None - def run(type_checker, obj): - if not type_checker(obj, True): # exact check - raise TypeError(f'{{type(obj)}} is not C API type') - - module = None - if _datetime: + def load_module(): import importlib.machinery import importlib.util fullname = '_testcapi_datetime' origin = importlib.util.find_spec('_testcapi').origin + if origin == 'built-in': + return # WASI loader = importlib.machinery.ExtensionFileLoader(fullname, origin) spec = importlib.util.spec_from_loader(fullname, loader) - try: - module = importlib.util.module_from_spec(spec) - except ImportError: - is_wasi = {support.is_wasi} # current WASI has no dlopen - if not is_wasi: - raise - - if _datetime and module: + module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) + return module + + def run(type_checker, obj): + if not type_checker(obj, True): # exact check + raise TypeError(f'{type(obj)} is not C API type') + + if _datetime and (module := load_module()): run(module.check_date, _datetime.date.today()) run(module.check_datetime, _datetime.datetime.now()) run(module.check_time, _datetime.time(12, 30)) From 0360151e68265482efe4e7ddabbcfe905a99e032 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sun, 2 Jun 2024 08:59:46 +0900 Subject: [PATCH 18/34] use _interpreters.new_config() --- Lib/test/test_capi/test_misc.py | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 442da03b32850d..6c9f5a3c5d9a23 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2324,31 +2324,12 @@ def run(type_checker, obj): self.assertEqual(ret, 0) if _interpreters: - with self.subTest("legacy subinterpreter (config)"): - ret = support.run_in_subinterp_with_config( - script, - own_gil=False, - use_main_obmalloc=True, - allow_fork=True, - allow_exec=True, - allow_threads=True, - allow_daemon_threads=True, - check_multi_interp_extensions=bool(Py_GIL_DISABLED), - ) - self.assertEqual(ret, 0) - - with self.subTest("isolated subinterpreter (config)"): - ret = support.run_in_subinterp_with_config( - script, - own_gil=True, - use_main_obmalloc=False, - allow_fork=False, - allow_exec=False, - allow_threads=True, - allow_daemon_threads=False, - check_multi_interp_extensions=True, - ) - self.assertEqual(ret, 0) + for name in ('legacy', 'isolated'): + with self.subTest(f'{name} (config)'): + config = dict(_interpreters.new_config(name).__dict__) + config['gil'] = {'shared': 1, 'own': 2}[config['gil']] + ret = support.run_in_subinterp_with_config(script, **config) + self.assertEqual(ret, 0) @requires_subinterpreters From b2c1e708f0d1a6d5a70868b91e05c5302c7c9a32 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sun, 2 Jun 2024 09:35:01 +0900 Subject: [PATCH 19/34] do not run_in_subinterp() --- Lib/test/test_capi/test_misc.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 6c9f5a3c5d9a23..cfb41885fbf66e 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2298,7 +2298,7 @@ def load_module(): fullname = '_testcapi_datetime' origin = importlib.util.find_spec('_testcapi').origin if origin == 'built-in': - return # WASI + return loader = importlib.machinery.ExtensionFileLoader(fullname, origin) spec = importlib.util.spec_from_loader(fullname, loader) module = importlib.util.module_from_spec(spec) @@ -2316,16 +2316,11 @@ def run(type_checker, obj): run(module.check_delta, _datetime.timedelta(1)) run(module.check_tzinfo, _datetime.tzinfo()) """) - with self.subTest("main interpreter"): + with self.subTest('main'): exec(script) - - with self.subTest("legacy subinterpreter"): - ret = support.run_in_subinterp(script) - self.assertEqual(ret, 0) - if _interpreters: for name in ('legacy', 'isolated'): - with self.subTest(f'{name} (config)'): + with self.subTest(name): config = dict(_interpreters.new_config(name).__dict__) config['gil'] = {'shared': 1, 'own': 2}[config['gil']] ret = support.run_in_subinterp_with_config(script, **config) From 23909a87216510e184537988264f57039da2821b Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sun, 2 Jun 2024 10:27:09 +0900 Subject: [PATCH 20/34] use @requires_subinterpreters instead of builtin _testcapi check --- Lib/test/test_capi/test_misc.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index cfb41885fbf66e..44a7de2cc6ad2b 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,6 +2285,7 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) + @requires_subinterpreters def test_datetime_capi_type_check(self): script = textwrap.dedent(""" try: @@ -2297,8 +2298,6 @@ def load_module(): import importlib.util fullname = '_testcapi_datetime' origin = importlib.util.find_spec('_testcapi').origin - if origin == 'built-in': - return loader = importlib.machinery.ExtensionFileLoader(fullname, origin) spec = importlib.util.spec_from_loader(fullname, loader) module = importlib.util.module_from_spec(spec) @@ -2318,13 +2317,12 @@ def run(type_checker, obj): """) with self.subTest('main'): exec(script) - if _interpreters: - for name in ('legacy', 'isolated'): - with self.subTest(name): - config = dict(_interpreters.new_config(name).__dict__) - config['gil'] = {'shared': 1, 'own': 2}[config['gil']] - ret = support.run_in_subinterp_with_config(script, **config) - self.assertEqual(ret, 0) + for name in ('legacy', 'isolated'): + with self.subTest(name): + config = dict(_interpreters.new_config(name).__dict__) + config['gil'] = {'shared': 1, 'own': 2}[config['gil']] + ret = support.run_in_subinterp_with_config(script, **config) + self.assertEqual(ret, 0) @requires_subinterpreters From daac38172058eae947fe7a1c65adbb7e4dbf8bda Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sun, 2 Jun 2024 11:06:31 +0900 Subject: [PATCH 21/34] do not use function to create module --- Lib/test/test_capi/test_misc.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 44a7de2cc6ad2b..42a9aab5ca69a2 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2293,7 +2293,11 @@ def test_datetime_capi_type_check(self): except ImportError: _datetime = None - def load_module(): + def run(type_checker, obj): + if not type_checker(obj, True): # exact check + raise TypeError(f'{type(obj)} is not C API type') + + if _datetime: import importlib.machinery import importlib.util fullname = '_testcapi_datetime' @@ -2302,13 +2306,7 @@ def load_module(): spec = importlib.util.spec_from_loader(fullname, loader) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) - return module - - def run(type_checker, obj): - if not type_checker(obj, True): # exact check - raise TypeError(f'{type(obj)} is not C API type') - if _datetime and (module := load_module()): run(module.check_date, _datetime.date.today()) run(module.check_datetime, _datetime.datetime.now()) run(module.check_time, _datetime.time(12, 30)) From bf4d78a5ffa8f8fb189df6e755d0b0f1ee4e2b14 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Sun, 2 Jun 2024 11:34:33 +0900 Subject: [PATCH 22/34] do not copy config dict --- Lib/test/test_capi/test_misc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 42a9aab5ca69a2..f191b1ab715a55 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2317,9 +2317,9 @@ def run(type_checker, obj): exec(script) for name in ('legacy', 'isolated'): with self.subTest(name): - config = dict(_interpreters.new_config(name).__dict__) - config['gil'] = {'shared': 1, 'own': 2}[config['gil']] - ret = support.run_in_subinterp_with_config(script, **config) + config = _interpreters.new_config(name) + config.gil = {'shared': 1, 'own': 2}[config.gil] + ret = support.run_in_subinterp_with_config(script, **config.__dict__) self.assertEqual(ret, 0) From b3460b45173593599cdea810269cad4661a82b02 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:28:54 +0900 Subject: [PATCH 23/34] add test for all OSes --- Lib/test/test_capi/test_misc.py | 36 ++++++++++++++++++++++++++++----- Modules/_testcapi/datetime.c | 12 +++++------ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index b0ee3d12027ce3..508df82ae8522c 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2285,11 +2285,11 @@ def run(type_checker, obj): module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) - run(module.check_date, _datetime.date.today()) - run(module.check_datetime, _datetime.datetime.now()) - run(module.check_time, _datetime.time(12, 30)) - run(module.check_delta, _datetime.timedelta(1)) - run(module.check_tzinfo, _datetime.tzinfo()) + run(module.datetime_check_date, _datetime.date.today()) + run(module.datetime_check_datetime, _datetime.datetime.now()) + run(module.datetime_check_time, _datetime.time(12, 30)) + run(module.datetime_check_delta, _datetime.timedelta(1)) + run(module.datetime_check_tzinfo, _datetime.tzinfo()) """) with self.subTest('main'): exec(script) @@ -2300,6 +2300,32 @@ def run(type_checker, obj): ret = support.run_in_subinterp_with_config(script, **config.__dict__) self.assertEqual(ret, 0) + def test_datetime_capi_type_check_allos(self): + script = textwrap.dedent(""" + try: + import _datetime + except ImportError: + _datetime = None + + def run(type_checker, obj): + if not type_checker(obj, True): # exact check + raise TypeError(f'{type(obj)} is not C API type') + + if _datetime: + import _testcapi as module + module.test_datetime_capi() + run(module.datetime_check_date, _datetime.date.today()) + run(module.datetime_check_datetime, _datetime.datetime.now()) + run(module.datetime_check_time, _datetime.time(12, 30)) + run(module.datetime_check_delta, _datetime.timedelta(1)) + run(module.datetime_check_tzinfo, _datetime.tzinfo()) + """) + with self.subTest('main interpreter'): + exec(script) + with self.subTest('non-isolated subinterpreter'): + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index c2f0bfcfcb663c..8d071cf6cc9dd4 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -482,7 +482,7 @@ _PyTestCapi_Init_DateTime(PyObject *mod) /* --------------------------------------------------------------------------- - * Test module for subinterpreters + * Test module for subinterpreters. */ static int @@ -502,11 +502,11 @@ _testcapi_datetime_exec(PyObject *mod) } static PyMethodDef _testcapi_datetime_methods[] = { - {"check_date", datetime_check_date, METH_VARARGS}, - {"check_datetime", datetime_check_datetime, METH_VARARGS}, - {"check_delta", datetime_check_delta, METH_VARARGS}, - {"check_time", datetime_check_time, METH_VARARGS}, - {"check_tzinfo", datetime_check_tzinfo, METH_VARARGS}, + {"datetime_check_date", datetime_check_date, METH_VARARGS}, + {"datetime_check_datetime", datetime_check_datetime, METH_VARARGS}, + {"datetime_check_delta", datetime_check_delta, METH_VARARGS}, + {"datetime_check_time", datetime_check_time, METH_VARARGS}, + {"datetime_check_tzinfo", datetime_check_tzinfo, METH_VARARGS}, {0}, }; From aab36c3cee3e47d612153ad5949f9ab55f50b124 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:16:06 +0900 Subject: [PATCH 24/34] _datetime does now multi-phase init --- Lib/test/test_capi/test_misc.py | 41 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 508df82ae8522c..ba6587cad3a635 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2266,30 +2266,25 @@ def test_module_state_shared_in_global(self): @requires_subinterpreters def test_datetime_capi_type_check(self): script = textwrap.dedent(""" - try: - import _datetime - except ImportError: - _datetime = None + import importlib.machinery + import importlib.util + fullname = '_testcapi_datetime' + origin = importlib.util.find_spec('_testcapi').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) def run(type_checker, obj): if not type_checker(obj, True): # exact check raise TypeError(f'{type(obj)} is not C API type') - if _datetime: - import importlib.machinery - import importlib.util - fullname = '_testcapi_datetime' - origin = importlib.util.find_spec('_testcapi').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) - - run(module.datetime_check_date, _datetime.date.today()) - run(module.datetime_check_datetime, _datetime.datetime.now()) - run(module.datetime_check_time, _datetime.time(12, 30)) - run(module.datetime_check_delta, _datetime.timedelta(1)) - run(module.datetime_check_tzinfo, _datetime.tzinfo()) + import _datetime + run(module.datetime_check_date, _datetime.date.today()) + run(module.datetime_check_datetime, _datetime.datetime.now()) + run(module.datetime_check_time, _datetime.time(12, 30)) + run(module.datetime_check_delta, _datetime.timedelta(1)) + run(module.datetime_check_tzinfo, _datetime.tzinfo()) """) with self.subTest('main'): exec(script) @@ -2303,17 +2298,17 @@ def run(type_checker, obj): def test_datetime_capi_type_check_allos(self): script = textwrap.dedent(""" try: - import _datetime + import _testcapi as module except ImportError: - _datetime = None + module = None def run(type_checker, obj): if not type_checker(obj, True): # exact check raise TypeError(f'{type(obj)} is not C API type') - if _datetime: - import _testcapi as module + if module: module.test_datetime_capi() + import _datetime run(module.datetime_check_date, _datetime.date.today()) run(module.datetime_check_datetime, _datetime.datetime.now()) run(module.datetime_check_time, _datetime.time(12, 30)) From 378b1fd821e605c820c77ae69bdfb3e9490d6771 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:40:04 +0900 Subject: [PATCH 25/34] remove test for all OSes --- Lib/test/test_capi/test_misc.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ba6587cad3a635..e1cbe64e28003f 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2295,32 +2295,6 @@ def run(type_checker, obj): ret = support.run_in_subinterp_with_config(script, **config.__dict__) self.assertEqual(ret, 0) - def test_datetime_capi_type_check_allos(self): - script = textwrap.dedent(""" - try: - import _testcapi as module - except ImportError: - module = None - - def run(type_checker, obj): - if not type_checker(obj, True): # exact check - raise TypeError(f'{type(obj)} is not C API type') - - if module: - module.test_datetime_capi() - import _datetime - run(module.datetime_check_date, _datetime.date.today()) - run(module.datetime_check_datetime, _datetime.datetime.now()) - run(module.datetime_check_time, _datetime.time(12, 30)) - run(module.datetime_check_delta, _datetime.timedelta(1)) - run(module.datetime_check_tzinfo, _datetime.tzinfo()) - """) - with self.subTest('main interpreter'): - exec(script) - with self.subTest('non-isolated subinterpreter'): - ret = support.run_in_subinterp(script) - self.assertEqual(ret, 0) - @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): From df4c43ebb89ca40591de7ffb4c86d1d584c1d496 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:36:56 +0900 Subject: [PATCH 26/34] remove duplicate test for main interpreter --- Lib/test/test_capi/test_misc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index e1cbe64e28003f..ce88ac49868a18 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2286,9 +2286,7 @@ def run(type_checker, obj): run(module.datetime_check_delta, _datetime.timedelta(1)) run(module.datetime_check_tzinfo, _datetime.tzinfo()) """) - with self.subTest('main'): - exec(script) - for name in ('legacy', 'isolated'): + for name in ('isolated', 'legacy'): with self.subTest(name): config = _interpreters.new_config(name) config.gil = {'shared': 1, 'own': 2}[config.gil] From 5ce05a404b8fe31d9ffcd60fa7297761e832da00 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 4 Jun 2024 23:32:50 +0900 Subject: [PATCH 27/34] reuse existing PyMethodDef --- Modules/_testcapi/datetime.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 8d071cf6cc9dd4..12a014e374c153 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -501,15 +501,6 @@ _testcapi_datetime_exec(PyObject *mod) return 0; } -static PyMethodDef _testcapi_datetime_methods[] = { - {"datetime_check_date", datetime_check_date, METH_VARARGS}, - {"datetime_check_datetime", datetime_check_datetime, METH_VARARGS}, - {"datetime_check_delta", datetime_check_delta, METH_VARARGS}, - {"datetime_check_time", datetime_check_time, METH_VARARGS}, - {"datetime_check_tzinfo", datetime_check_tzinfo, METH_VARARGS}, - {0}, -}; - static PyModuleDef_Slot _testcapi_datetime_slots[] = { {Py_mod_exec, _testcapi_datetime_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, @@ -521,7 +512,7 @@ static struct PyModuleDef _testcapi_datetime_module = { PyModuleDef_HEAD_INIT, .m_name = "_testcapi_datetime", .m_size = 0, - .m_methods = _testcapi_datetime_methods, + .m_methods = test_methods, .m_slots = _testcapi_datetime_slots, }; From ba41ef17b50f6ea0c108feaaa416795e93293a42 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 5 Jun 2024 08:34:42 +0900 Subject: [PATCH 28/34] use expected config of InterpreterConfigTests instead --- Lib/test/test_capi/test_misc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ce88ac49868a18..04586addc2c1ef 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2286,11 +2286,12 @@ def run(type_checker, obj): run(module.datetime_check_delta, _datetime.timedelta(1)) run(module.datetime_check_tzinfo, _datetime.tzinfo()) """) + configs = InterpreterConfigTests for name in ('isolated', 'legacy'): with self.subTest(name): - config = _interpreters.new_config(name) - config.gil = {'shared': 1, 'own': 2}[config.gil] - ret = support.run_in_subinterp_with_config(script, **config.__dict__) + config = dict(configs.supported[name].__dict__) + config['gil'] = configs.gil_supported.index(config['gil']) + ret = support.run_in_subinterp_with_config(script, **config) self.assertEqual(ret, 0) From 272f25fca4589851c27907a250777d9fac0f085f Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:15:50 +0900 Subject: [PATCH 29/34] move static type assertions to test_datetime_capi() --- Modules/_testcapi/datetime.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c index 12a014e374c153..f3d54215e04232 100644 --- a/Modules/_testcapi/datetime.c +++ b/Modules/_testcapi/datetime.c @@ -22,10 +22,17 @@ test_datetime_capi(PyObject *self, PyObject *args) test_run_counter++; PyDateTime_IMPORT; - if (PyDateTimeAPI) { - Py_RETURN_NONE; + if (PyDateTimeAPI == NULL) { + return NULL; } - return NULL; + // The following C API types need to outlive interpreters, since the + // borrowed references to them can be held by users without being updated. + assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE)); + assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE)); + Py_RETURN_NONE; } /* Functions exposing the C API type checking for testing */ @@ -488,16 +495,9 @@ _PyTestCapi_Init_DateTime(PyObject *mod) static int _testcapi_datetime_exec(PyObject *mod) { - if (test_datetime_capi(NULL, NULL) == NULL) { // PyDateTime_IMPORT + if (test_datetime_capi(NULL, NULL) == NULL) { return -1; } - // The following C API types need to outlive interpreters, since the - // borrowed references to them can be held by users without being updated. - assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE)); - assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE)); - assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE)); - assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE)); - assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE)); return 0; } From 17615ef00d556e7b187da9432519dd182dd8612a Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 5 Jun 2024 21:10:44 +0900 Subject: [PATCH 30/34] WASI support (only on legacy subinterp) --- Lib/test/test_capi/test_misc.py | 43 +++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 04586addc2c1ef..a9bb61704c40d4 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2263,21 +2263,24 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) - @requires_subinterpreters def test_datetime_capi_type_check(self): - script = textwrap.dedent(""" - import importlib.machinery - import importlib.util - fullname = '_testcapi_datetime' - origin = importlib.util.find_spec('_testcapi').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) + script = textwrap.dedent(f""" + if {_interpreters is None}: + import _testcapi as module + module.test_datetime_capi() + else: + import importlib.machinery + import importlib.util + fullname = '_testcapi_datetime' + origin = importlib.util.find_spec('_testcapi').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) def run(type_checker, obj): if not type_checker(obj, True): # exact check - raise TypeError(f'{type(obj)} is not C API type') + raise TypeError(f'{{type(obj)}} is not C API type') import _datetime run(module.datetime_check_date, _datetime.date.today()) @@ -2286,13 +2289,17 @@ def run(type_checker, obj): run(module.datetime_check_delta, _datetime.timedelta(1)) run(module.datetime_check_tzinfo, _datetime.tzinfo()) """) - configs = InterpreterConfigTests - for name in ('isolated', 'legacy'): - with self.subTest(name): - config = dict(configs.supported[name].__dict__) - config['gil'] = configs.gil_supported.index(config['gil']) - ret = support.run_in_subinterp_with_config(script, **config) - self.assertEqual(ret, 0) + if _interpreters is None: + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + else: + configs = InterpreterConfigTests + for name in ('isolated', 'legacy'): + with self.subTest(name): + config = dict(configs.supported[name].__dict__) + config['gil'] = configs.gil_supported.index(config['gil']) + ret = support.run_in_subinterp_with_config(script, **config) + self.assertEqual(ret, 0) @requires_subinterpreters From 9c46fe0624642b3b30c8d7649838608845e7b777 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 13 Jun 2024 18:32:04 +0900 Subject: [PATCH 31/34] use _interpreters.new_config() --- Lib/test/support/__init__.py | 11 ++++------- Lib/test/test_capi/test_misc.py | 8 +++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 4b320b494bb8dd..67bec0e1716359 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1800,14 +1800,11 @@ def run_in_subinterp_with_config(code, *, own_gil=None, **config): assert 'gil' not in config, (own_gil, config) config['gil'] = 'own' if own_gil else 'shared' else: + gil_supported = {0: 'default', 1: 'shared', 2: 'own'} gil = config['gil'] - if gil == 0: - config['gil'] = 'default' - elif gil == 1: - config['gil'] = 'shared' - elif gil == 2: - config['gil'] = 'own' - else: + if gil in gil_supported: + config['gil'] = gil_supported[gil] + elif gil not in gil_supported.values(): raise NotImplementedError(gil) config = types.SimpleNamespace(**config) return _testinternalcapi.run_in_subinterp_with_config(code, config) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index a9bb61704c40d4..d4b859980a4a9c 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2263,7 +2263,7 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) - def test_datetime_capi_type_check(self): + def test_type_check_per_interp(self): script = textwrap.dedent(f""" if {_interpreters is None}: import _testcapi as module @@ -2279,7 +2279,7 @@ def test_datetime_capi_type_check(self): spec.loader.exec_module(module) def run(type_checker, obj): - if not type_checker(obj, True): # exact check + if not type_checker(obj, True): raise TypeError(f'{{type(obj)}} is not C API type') import _datetime @@ -2293,11 +2293,9 @@ def run(type_checker, obj): ret = support.run_in_subinterp(script) self.assertEqual(ret, 0) else: - configs = InterpreterConfigTests for name in ('isolated', 'legacy'): with self.subTest(name): - config = dict(configs.supported[name].__dict__) - config['gil'] = configs.gil_supported.index(config['gil']) + config = _interpreters.new_config(name).__dict__ ret = support.run_in_subinterp_with_config(script, **config) self.assertEqual(ret, 0) From 0c40fc88e92d49450887a8f5855d5da5df6bf22a Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 13 Jun 2024 18:37:30 +0900 Subject: [PATCH 32/34] move test to datetimetester.py --- Lib/test/datetimetester.py | 41 +++++++++++++++++++++++++++++++++ Lib/test/test_capi/test_misc.py | 36 ----------------------------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 535b17d0727611..7d28d45d50d6c0 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -13,6 +13,7 @@ import re import struct import sys +import textwrap import unittest import warnings @@ -38,6 +39,10 @@ import _testcapi except ImportError: _testcapi = None +try: + import _interpreters +except ModuleNotFoundError: + _interpreters = None # Needed by test_datetime import _strptime @@ -6774,6 +6779,42 @@ def test_datetime_from_timestamp(self): self.assertEqual(dt_orig, dt_rt) + def test_type_check_per_interp(self): + script = textwrap.dedent(f""" + if {_interpreters is None}: + import _testcapi as module + module.test_datetime_capi() + else: + import importlib.machinery + import importlib.util + fullname = '_testcapi_datetime' + origin = importlib.util.find_spec('_testcapi').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) + + def run(type_checker, obj): + if not type_checker(obj, True): + raise TypeError(f'{{type(obj)}} is not C API type') + + import _datetime + run(module.datetime_check_date, _datetime.date.today()) + run(module.datetime_check_datetime, _datetime.datetime.now()) + run(module.datetime_check_time, _datetime.time(12, 30)) + run(module.datetime_check_delta, _datetime.timedelta(1)) + run(module.datetime_check_tzinfo, _datetime.tzinfo()) + """) + if _interpreters is None: + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + else: + for name in ('isolated', 'legacy'): + with self.subTest(name): + config = _interpreters.new_config(name).__dict__ + ret = support.run_in_subinterp_with_config(script, **config) + self.assertEqual(ret, 0) + def load_tests(loader, standard_tests, pattern): standard_tests.addTest(ZoneInfoCompleteTest()) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index d4b859980a4a9c..0dc0b530aec971 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2263,42 +2263,6 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) - def test_type_check_per_interp(self): - script = textwrap.dedent(f""" - if {_interpreters is None}: - import _testcapi as module - module.test_datetime_capi() - else: - import importlib.machinery - import importlib.util - fullname = '_testcapi_datetime' - origin = importlib.util.find_spec('_testcapi').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) - - def run(type_checker, obj): - if not type_checker(obj, True): - raise TypeError(f'{{type(obj)}} is not C API type') - - import _datetime - run(module.datetime_check_date, _datetime.date.today()) - run(module.datetime_check_datetime, _datetime.datetime.now()) - run(module.datetime_check_time, _datetime.time(12, 30)) - run(module.datetime_check_delta, _datetime.timedelta(1)) - run(module.datetime_check_tzinfo, _datetime.tzinfo()) - """) - if _interpreters is None: - ret = support.run_in_subinterp(script) - self.assertEqual(ret, 0) - else: - for name in ('isolated', 'legacy'): - with self.subTest(name): - config = _interpreters.new_config(name).__dict__ - ret = support.run_in_subinterp_with_config(script, **config) - self.assertEqual(ret, 0) - @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): From b861bd32981fcb8fdf48faf75129b8a7893b80b9 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 13 Jun 2024 19:57:11 +0900 Subject: [PATCH 33/34] rename test case --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index e713be6bfc38ef..45188731eed688 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -6785,7 +6785,7 @@ def test_datetime_from_timestamp(self): self.assertEqual(dt_orig, dt_rt) - def test_type_check_per_interp(self): + def test_type_check_in_subinterp(self): script = textwrap.dedent(f""" if {_interpreters is None}: import _testcapi as module From a1421dda7ee60653c6becd104ebf448a8b1bf71d Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Fri, 14 Jun 2024 02:27:30 +0900 Subject: [PATCH 34/34] simplify conditions in support.run_in_subinterp_with_config() --- Lib/test/support/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 88ccf3efe3fb53..adc6362e20df00 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1801,11 +1801,14 @@ def run_in_subinterp_with_config(code, *, own_gil=None, **config): assert 'gil' not in config, (own_gil, config) config['gil'] = 'own' if own_gil else 'shared' else: - gil_supported = {0: 'default', 1: 'shared', 2: 'own'} gil = config['gil'] - if gil in gil_supported: - config['gil'] = gil_supported[gil] - elif gil not in gil_supported.values(): + if gil == 0: + config['gil'] = 'default' + elif gil == 1: + config['gil'] = 'shared' + elif gil == 2: + config['gil'] = 'own' + elif not isinstance(gil, str): raise NotImplementedError(gil) config = types.SimpleNamespace(**config) return _testinternalcapi.run_in_subinterp_with_config(code, config)