Skip to content

Commit 15c3d00

Browse files
[3.13] gh-117398: Add datetime C-API type check test for subinterpreters (gh-120463)
Check if the DateTime C-API type matches the datetime.date type on main and shared/isolated subinterpreters. (cherry picked from commit 50a3895, AKA gh-119604) Co-authored-by: neonene <[email protected]>
1 parent e0f4dd8 commit 15c3d00

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

Lib/test/datetimetester.py

+41
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import re
1414
import struct
1515
import sys
16+
import textwrap
1617
import unittest
1718
import warnings
1819

@@ -38,6 +39,10 @@
3839
import _testcapi
3940
except ImportError:
4041
_testcapi = None
42+
try:
43+
import _interpreters
44+
except ModuleNotFoundError:
45+
_interpreters = None
4146

4247
# Needed by test_datetime
4348
import _strptime
@@ -6798,6 +6803,42 @@ def test_datetime_from_timestamp(self):
67986803

67996804
self.assertEqual(dt_orig, dt_rt)
68006805

6806+
def test_type_check_in_subinterp(self):
6807+
script = textwrap.dedent(f"""
6808+
if {_interpreters is None}:
6809+
import _testcapi as module
6810+
module.test_datetime_capi()
6811+
else:
6812+
import importlib.machinery
6813+
import importlib.util
6814+
fullname = '_testcapi_datetime'
6815+
origin = importlib.util.find_spec('_testcapi').origin
6816+
loader = importlib.machinery.ExtensionFileLoader(fullname, origin)
6817+
spec = importlib.util.spec_from_loader(fullname, loader)
6818+
module = importlib.util.module_from_spec(spec)
6819+
spec.loader.exec_module(module)
6820+
6821+
def run(type_checker, obj):
6822+
if not type_checker(obj, True):
6823+
raise TypeError(f'{{type(obj)}} is not C API type')
6824+
6825+
import _datetime
6826+
run(module.datetime_check_date, _datetime.date.today())
6827+
run(module.datetime_check_datetime, _datetime.datetime.now())
6828+
run(module.datetime_check_time, _datetime.time(12, 30))
6829+
run(module.datetime_check_delta, _datetime.timedelta(1))
6830+
run(module.datetime_check_tzinfo, _datetime.tzinfo())
6831+
""")
6832+
if _interpreters is None:
6833+
ret = support.run_in_subinterp(script)
6834+
self.assertEqual(ret, 0)
6835+
else:
6836+
for name in ('isolated', 'legacy'):
6837+
with self.subTest(name):
6838+
config = _interpreters.new_config(name).__dict__
6839+
ret = support.run_in_subinterp_with_config(script, **config)
6840+
self.assertEqual(ret, 0)
6841+
68016842

68026843
def load_tests(loader, standard_tests, pattern):
68036844
standard_tests.addTest(ZoneInfoCompleteTest())

Lib/test/support/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1788,7 +1788,7 @@ def run_in_subinterp_with_config(code, *, own_gil=None, **config):
17881788
config['gil'] = 'shared'
17891789
elif gil == 2:
17901790
config['gil'] = 'own'
1791-
else:
1791+
elif not isinstance(gil, str):
17921792
raise NotImplementedError(gil)
17931793
config = types.SimpleNamespace(**config)
17941794
return _testinternalcapi.run_in_subinterp_with_config(code, config)

Modules/_testcapi/datetime.c

+45-3
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@ test_datetime_capi(PyObject *self, PyObject *args)
2222
test_run_counter++;
2323
PyDateTime_IMPORT;
2424

25-
if (PyDateTimeAPI) {
26-
Py_RETURN_NONE;
25+
if (PyDateTimeAPI == NULL) {
26+
return NULL;
2727
}
28-
return NULL;
28+
// The following C API types need to outlive interpreters, since the
29+
// borrowed references to them can be held by users without being updated.
30+
assert(!PyType_HasFeature(PyDateTimeAPI->DateType, Py_TPFLAGS_HEAPTYPE));
31+
assert(!PyType_HasFeature(PyDateTimeAPI->TimeType, Py_TPFLAGS_HEAPTYPE));
32+
assert(!PyType_HasFeature(PyDateTimeAPI->DateTimeType, Py_TPFLAGS_HEAPTYPE));
33+
assert(!PyType_HasFeature(PyDateTimeAPI->DeltaType, Py_TPFLAGS_HEAPTYPE));
34+
assert(!PyType_HasFeature(PyDateTimeAPI->TZInfoType, Py_TPFLAGS_HEAPTYPE));
35+
Py_RETURN_NONE;
2936
}
3037

3138
/* Functions exposing the C API type checking for testing */
@@ -479,3 +486,38 @@ _PyTestCapi_Init_DateTime(PyObject *mod)
479486
}
480487
return 0;
481488
}
489+
490+
491+
/* ---------------------------------------------------------------------------
492+
* Test module for subinterpreters.
493+
*/
494+
495+
static int
496+
_testcapi_datetime_exec(PyObject *mod)
497+
{
498+
if (test_datetime_capi(NULL, NULL) == NULL) {
499+
return -1;
500+
}
501+
return 0;
502+
}
503+
504+
static PyModuleDef_Slot _testcapi_datetime_slots[] = {
505+
{Py_mod_exec, _testcapi_datetime_exec},
506+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
507+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
508+
{0, NULL},
509+
};
510+
511+
static struct PyModuleDef _testcapi_datetime_module = {
512+
PyModuleDef_HEAD_INIT,
513+
.m_name = "_testcapi_datetime",
514+
.m_size = 0,
515+
.m_methods = test_methods,
516+
.m_slots = _testcapi_datetime_slots,
517+
};
518+
519+
PyMODINIT_FUNC
520+
PyInit__testcapi_datetime(void)
521+
{
522+
return PyModuleDef_Init(&_testcapi_datetime_module);
523+
}

0 commit comments

Comments
 (0)