Skip to content

Commit

Permalink
pythongh-117398: Add datetime C-API type check test for subinterprete…
Browse files Browse the repository at this point in the history
…rs (pythongh-119604)

Check if the DateTime C-API type matches the datetime.date type on main and shared/isolated subinterpreters.
  • Loading branch information
neonene authored and estyxx committed Jul 17, 2024
1 parent dddbe9b commit 6285938
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 4 deletions.
41 changes: 41 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import re
import struct
import sys
import textwrap
import unittest
import warnings

Expand All @@ -38,6 +39,10 @@
import _testcapi
except ImportError:
_testcapi = None
try:
import _interpreters
except ModuleNotFoundError:
_interpreters = None

# Needed by test_datetime
import _strptime
Expand Down Expand Up @@ -6780,6 +6785,42 @@ def test_datetime_from_timestamp(self):

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())
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
48 changes: 45 additions & 3 deletions Modules/_testcapi/datetime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
}

0 comments on commit 6285938

Please sign in to comment.