]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-117398: Add datetime C-API type check test for subinterpreters (gh-120463)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 13 Jun 2024 18:30:42 +0000 (20:30 +0200)
committerGitHub <noreply@github.com>
Thu, 13 Jun 2024 18:30:42 +0000 (18:30 +0000)
Check if the DateTime C-API type matches the datetime.date type on main and shared/isolated subinterpreters.

(cherry picked from commit 50a389565aa0b480792ed06a2ab56fb5a72fc2d8, AKA gh-119604)

Co-authored-by: neonene <53406459+neonene@users.noreply.github.com>
Lib/test/datetimetester.py
Lib/test/support/__init__.py
Modules/_testcapi/datetime.c

index ddd8e02022f188d6d71bf9c66b8a639312d47518..f899be53acba09d8dcd15d83bcf1e0f3a1bc4b28 100644 (file)
@@ -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
@@ -6798,6 +6803,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())
index d7a3a549d9cdc245e5735606b5af718e82405c25..e90ef3d1505a27c1f99690a6f759780c91409b33 100644 (file)
@@ -1788,7 +1788,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)
index b1796039f0d83af8b945189d9387ec1ee082ffa0..f3d54215e04232bb9907c03baacf98bc15f72fe2 100644 (file)
@@ -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);
+}