.gil = PyInterpreterConfig_OWN_GIL, \
}
+// gh-117649: The free-threaded build does not currently support single-phase
+// init extensions in subinterpreters. For now, we ensure that
+// `check_multi_interp_extensions` is always `1`, even in the legacy config.
+#ifdef Py_GIL_DISABLED
+# define _PyInterpreterConfig_LEGACY_CHECK_MULTI_INTERP_EXTENSIONS 1
+#else
+# define _PyInterpreterConfig_LEGACY_CHECK_MULTI_INTERP_EXTENSIONS 0
+#endif
+
#define _PyInterpreterConfig_LEGACY_INIT \
{ \
.use_main_obmalloc = 1, \
.allow_exec = 1, \
.allow_threads = 1, \
.allow_daemon_threads = 1, \
- .check_multi_interp_extensions = 0, \
+ .check_multi_interp_extensions = _PyInterpreterConfig_LEGACY_CHECK_MULTI_INTERP_EXTENSIONS, \
.gil = PyInterpreterConfig_SHARED_GIL, \
}
"""Decorator for skipping tests on the free-threaded build."""
return unittest.skipIf(Py_GIL_DISABLED, msg)
+def expected_failure_if_gil_disabled():
+ """Expect test failure if the GIL is disabled."""
+ if Py_GIL_DISABLED:
+ return unittest.expectedFailure
+ return lambda test_case: test_case
+
if Py_GIL_DISABLED:
_header = 'PHBBInP'
else:
from test.support import threading_helper
from test.support import warnings_helper
from test.support import requires_limited_api
+from test.support import requires_gil_enabled, expected_failure_if_gil_disabled
+from test.support import Py_GIL_DISABLED
from test.support.script_helper import assert_python_failure, assert_python_ok, run_python_until_end
try:
import _posixsubprocess
kwlist[-2] = 'check_multi_interp_extensions'
kwlist[-1] = 'own_gil'
- # expected to work
- for config, expected in {
+ expected_to_work = {
(True, True, True, True, True, True, True):
(ALL_FLAGS, True),
(True, False, False, False, False, False, False):
(OBMALLOC, False),
(False, False, False, True, False, True, False):
(THREADS | EXTENSIONS, False),
- }.items():
+ }
+
+ expected_to_fail = {
+ (False, False, False, False, False, False, False),
+ }
+
+ # gh-117649: The free-threaded build does not currently allow
+ # setting check_multi_interp_extensions to False.
+ if Py_GIL_DISABLED:
+ for config in list(expected_to_work.keys()):
+ kwargs = dict(zip(kwlist, config))
+ if not kwargs['check_multi_interp_extensions']:
+ del expected_to_work[config]
+ expected_to_fail.add(config)
+
+ # expected to work
+ for config, expected in expected_to_work.items():
kwargs = dict(zip(kwlist, config))
exp_flags, exp_gil = expected
expected = {
self.assertEqual(settings, expected)
# expected to fail
- for config in [
- (False, False, False, False, False, False, False),
- ]:
+ for config in expected_to_fail:
kwargs = dict(zip(kwlist, config))
with self.subTest(config):
script = textwrap.dedent(f'''
@unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module")
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
+ # gh-117649: The free-threaded build does not currently allow overriding
+ # the check_multi_interp_extensions setting.
+ @expected_failure_if_gil_disabled()
def test_overridden_setting_extensions_subinterp_check(self):
"""
PyInterpreterConfig.check_multi_interp_extensions can be overridden
self.assertFalse(hasattr(binascii.Error, "foobar"))
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
+ # gh-117649: The free-threaded build does not currently support sharing
+ # extension module state between interpreters.
+ @expected_failure_if_gil_disabled()
def test_module_state_shared_in_global(self):
"""
bpo-44050: Extension module state should be shared between interpreters
allow_exec=True,
allow_threads=True,
allow_daemon_threads=True,
- check_multi_interp_extensions=False,
+ check_multi_interp_extensions=bool(Py_GIL_DISABLED),
gil='shared',
),
'empty': types.SimpleNamespace(
check_multi_interp_extensions=False
),
]
+ if Py_GIL_DISABLED:
+ invalid.append(dict(check_multi_interp_extensions=False))
def match(config, override_cases):
ns = vars(config)
for overrides in override_cases:
with self.subTest('main'):
expected = _interpreters.new_config('legacy')
expected.gil = 'own'
+ if Py_GIL_DISABLED:
+ expected.check_multi_interp_extensions = False
interpid, *_ = _interpreters.get_main()
config = _interpreters.get_config(interpid)
self.assert_ns_equal(config, expected)
'empty',
use_main_obmalloc=True,
gil='shared',
+ check_multi_interp_extensions=bool(Py_GIL_DISABLED),
)
with new_interp(orig) as interpid:
config = _interpreters.get_config(interpid)
from test.support import os_helper
from test.support import (
STDLIB_DIR, swap_attr, swap_item, cpython_only, is_apple_mobile, is_emscripten,
- is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS)
+ is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS,
+ requires_gil_enabled, Py_GIL_DISABLED)
from test.support.import_helper import (
forget, make_legacy_pyc, unlink, unload, ready_to_import,
DirsOnSysPath, CleanImport, import_module)
finally:
restore__testsinglephase()
meth = cpython_only(meth)
+ # gh-117649: free-threaded build does not currently support single-phase
+ # init modules in subinterpreters.
+ meth = requires_gil_enabled(meth)
return unittest.skipIf(_testsinglephase is None,
'test requires _testsinglephase module')(meth)
# since they still don't implement multi-phase init.
module = '_imp'
require_builtin(module)
- with self.subTest(f'{module}: not strict'):
- self.check_compatible_here(module, strict=False)
+ if not Py_GIL_DISABLED:
+ with self.subTest(f'{module}: not strict'):
+ self.check_compatible_here(module, strict=False)
with self.subTest(f'{module}: strict, not fresh'):
self.check_compatible_here(module, strict=True)
require_frozen(module, skip=True)
if __import__(module).__spec__.origin != 'frozen':
raise unittest.SkipTest(f'{module} is unexpectedly not frozen')
- with self.subTest(f'{module}: not strict'):
- self.check_compatible_here(module, strict=False)
+ if not Py_GIL_DISABLED:
+ with self.subTest(f'{module}: not strict'):
+ self.check_compatible_here(module, strict=False)
with self.subTest(f'{module}: strict, not fresh'):
self.check_compatible_here(module, strict=True)
def test_multi_init_extension_compat(self):
module = '_testmultiphase'
require_extension(module)
- with self.subTest(f'{module}: not strict'):
- self.check_compatible_here(module, strict=False)
+ if not Py_GIL_DISABLED:
+ with self.subTest(f'{module}: not strict'):
+ self.check_compatible_here(module, strict=False)
with self.subTest(f'{module}: strict, not fresh'):
self.check_compatible_here(module, strict=True)
with self.subTest(f'{module}: strict, fresh'):
self.check_incompatible_here(modname, filename, isolated=True)
with self.subTest(f'{modname}: not isolated'):
self.check_incompatible_here(modname, filename, isolated=False)
- with self.subTest(f'{modname}: not strict'):
- self.check_compatible_here(modname, filename, strict=False)
+ if not Py_GIL_DISABLED:
+ with self.subTest(f'{modname}: not strict'):
+ self.check_compatible_here(modname, filename, strict=False)
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
def test_multi_init_extension_per_interpreter_gil_compat(self):
with self.subTest(f'{modname}: not isolated, strict'):
self.check_compatible_here(modname, filename,
strict=True, isolated=False)
- with self.subTest(f'{modname}: not isolated, not strict'):
- self.check_compatible_here(modname, filename,
- strict=False, isolated=False)
+ if not Py_GIL_DISABLED:
+ with self.subTest(f'{modname}: not isolated, not strict'):
+ self.check_compatible_here(modname, filename,
+ strict=False, isolated=False)
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
def test_python_compat(self):
module = 'threading'
require_pure_python(module)
- with self.subTest(f'{module}: not strict'):
- self.check_compatible_here(module, strict=False)
+ if not Py_GIL_DISABLED:
+ with self.subTest(f'{module}: not strict'):
+ self.check_compatible_here(module, strict=False)
with self.subTest(f'{module}: strict, not fresh'):
self.check_compatible_here(module, strict=True)
with self.subTest(f'{module}: strict, fresh'):
raise ImportError(excsnap.msg)
@unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module")
+ # gh-117649: single-phase init modules are not currently supported in
+ # subinterpreters in the free-threaded build
+ @support.expected_failure_if_gil_disabled()
def test_single_phase_init_module(self):
script = textwrap.dedent('''
from importlib.util import _incompatible_extension_module_restrictions
self.run_with_own_gil(script)
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
+ @support.requires_gil_enabled("gh-117649: not supported in free-threaded build")
def test_incomplete_multi_phase_init_module(self):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
from test.support import import_helper
# Raise SkipTest if subinterpreters not supported.
_interpreters = import_helper.import_module('_xxsubinterpreters')
+from test.support import Py_GIL_DISABLED
from test.support import interpreters
from test.support.interpreters import (
InterpreterError, InterpreterNotFoundError, ExecutionFailed,
allow_exec=True,
allow_threads=True,
allow_daemon_threads=True,
- check_multi_interp_extensions=False,
+ check_multi_interp_extensions=bool(Py_GIL_DISABLED),
gil='shared',
),
'empty': types.SimpleNamespace(
with self.subTest('custom'):
orig = _interpreters.new_config('empty')
orig.use_main_obmalloc = True
+ orig.check_multi_interp_extensions = bool(Py_GIL_DISABLED)
orig.gil = 'shared'
interpid = _interpreters.create(orig)
config = _interpreters.get_config(interpid)
with self.subTest('main'):
expected = _interpreters.new_config('legacy')
expected.gil = 'own'
- interpid, *_ = _interpreters.get_main()
- config = _interpreters.get_config(interpid)
- self.assert_ns_equal(config, expected)
-
- with self.subTest('main'):
- expected = _interpreters.new_config('legacy')
- expected.gil = 'own'
+ if Py_GIL_DISABLED:
+ expected.check_multi_interp_extensions = False
interpid, *_ = _interpreters.get_main()
config = _interpreters.get_config(interpid)
self.assert_ns_equal(config, expected)
{before_start}
t.start()
""")
+ check_multi_interp_extensions = bool(support.Py_GIL_DISABLED)
script = textwrap.dedent(f"""
import test.support
test.support.run_in_subinterp_with_config(
allow_exec=True,
allow_threads={allowed},
allow_daemon_threads={daemon_allowed},
- check_multi_interp_extensions=False,
+ check_multi_interp_extensions={check_multi_interp_extensions},
own_gil=False,
)
""")
"cannot be used in the main interpreter");
return NULL;
}
+#ifdef Py_GIL_DISABLED
+ PyErr_SetString(PyExc_RuntimeError,
+ "_imp._override_multi_interp_extensions_check() "
+ "cannot be used in the free-threaded build");
+ return NULL;
+#else
int oldvalue = OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp);
OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) = override;
return PyLong_FromLong(oldvalue);
+#endif
}
#ifdef HAVE_DYNAMIC_LOADING
return _PyStatus_ERR("per-interpreter obmalloc does not support "
"single-phase init extension modules");
}
+#ifdef Py_GIL_DISABLED
+ if (!_Py_IsMainInterpreter(interp) &&
+ !config->check_multi_interp_extensions)
+ {
+ return _PyStatus_ERR("The free-threaded build does not support "
+ "single-phase init extension modules in "
+ "subinterpreters");
+ }
+#endif
if (config->allow_fork) {
interp->feature_flags |= Py_RTFLAGS_FORK;
}
PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
- // The main interpreter always has its own GIL.
+ // The main interpreter always has its own GIL and supports single-phase
+ // init extensions.
config.gil = PyInterpreterConfig_OWN_GIL;
+ config.check_multi_interp_extensions = 0;
status = init_interp_settings(interp, &config);
if (_PyStatus_EXCEPTION(status)) {
return status;