summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorneonene <53406459+neonene@users.noreply.github.com>2024-06-13 18:05:03 (GMT)
committerGitHub <noreply@github.com>2024-06-13 18:05:03 (GMT)
commit50a389565aa0b480792ed06a2ab56fb5a72fc2d8 (patch)
tree7a0d815edb80d7d3e64110dd88738b14648c7dd5
parentc2d810b6d4deeea530648a8d0983e3a2adf6c942 (diff)
downloadcpython-50a389565aa0b480792ed06a2ab56fb5a72fc2d8.zip
cpython-50a389565aa0b480792ed06a2ab56fb5a72fc2d8.tar.gz
cpython-50a389565aa0b480792ed06a2ab56fb5a72fc2d8.tar.bz2
gh-117398: Add datetime C-API type check test for subinterpreters (gh-119604)
Check if the DateTime C-API type matches the datetime.date type on main and shared/isolated subinterpreters.
-rw-r--r--Lib/test/datetimetester.py41
-rw-r--r--Lib/test/support/__init__.py2
-rw-r--r--Modules/_testcapi/datetime.c48
3 files changed, 87 insertions, 4 deletions
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 28f75a8..4518873 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -13,6 +13,7 @@ import random
import re
import struct
import sys
+import textwrap
import unittest
import warnings
@@ -38,6 +39,10 @@ try:
import _testcapi
except ImportError:
_testcapi = None
+try:
+ import _interpreters
+except ModuleNotFoundError:
+ _interpreters = None
# Needed by test_datetime
import _strptime
@@ -6780,6 +6785,42 @@ class CapiTest(unittest.TestCase):
self.assertEqual(dt_orig, dt_rt)
+ def test_type_check_in_subinterp(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/support/__init__.py b/Lib/test/support/__init__.py
index 9e6100d..adc6362 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -1808,7 +1808,7 @@ def run_in_subinterp_with_config(code, *, own_gil=None, **config):
config['gil'] = 'shared'
elif gil == 2:
config['gil'] = 'own'
- else:
+ elif not isinstance(gil, str):
raise NotImplementedError(gil)
config = types.SimpleNamespace(**config)
return _testinternalcapi.run_in_subinterp_with_config(code, config)
diff --git a/Modules/_testcapi/datetime.c b/Modules/_testcapi/datetime.c
index b179603..f3d5421 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 */
@@ -479,3 +486,38 @@ _PyTestCapi_Init_DateTime(PyObject *mod)
}
return 0;
}
+
+
+/* ---------------------------------------------------------------------------
+ * Test module for subinterpreters.
+ */
+
+static int
+_testcapi_datetime_exec(PyObject *mod)
+{
+ if (test_datetime_capi(NULL, NULL) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+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},
+};
+
+static struct PyModuleDef _testcapi_datetime_module = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "_testcapi_datetime",
+ .m_size = 0,
+ .m_methods = test_methods,
+ .m_slots = _testcapi_datetime_slots,
+};
+
+PyMODINIT_FUNC
+PyInit__testcapi_datetime(void)
+{
+ return PyModuleDef_Init(&_testcapi_datetime_module);
+}