Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bdaef6b
Fix a comment.
ericsnowcurrently Mar 7, 2024
6342635
Add PyInterpreterConfig helpers.
ericsnowcurrently Mar 21, 2024
c48dd00
Add config helpers to _testinternalcapi.
ericsnowcurrently Mar 21, 2024
0796fe9
Use the new helpers in run_in_subinterp_with_config().
ericsnowcurrently Mar 22, 2024
8993b41
Move the PyInterpreterConfig utils to their own file.
ericsnowcurrently Mar 22, 2024
a113017
_PyInterpreterState_ResolveConfig() -> _PyInterpreterConfig_InitFromS…
ericsnowcurrently Mar 22, 2024
fce72b8
_testinternalcapi.new_interpreter_config() -> _xxsubinterpreters.new_…
ericsnowcurrently Mar 22, 2024
c52e484
_testinternalcapi.get_interpreter_config() -> _xxsubinterpreters.get_…
ericsnowcurrently Mar 22, 2024
a2983ce
Call _PyInterpreterState_RequireIDRef() in _interpreters._incref().
ericsnowcurrently Mar 22, 2024
05a081e
_testinternalcapi.interpreter_incref() -> _interpreters._incref()
ericsnowcurrently Mar 23, 2024
8a39bbc
Supporting passing a config to _xxsubinterpreters.create().
ericsnowcurrently Mar 22, 2024
1173cd1
Factor out new_interpreter().
ericsnowcurrently Mar 22, 2024
92c11d3
Fix test_import.
ericsnowcurrently Mar 25, 2024
edda48d
Fix an outdent.
ericsnowcurrently Mar 25, 2024
5f617ed
Call _PyInterpreterState_RequireIDRef() in the right places.
ericsnowcurrently Apr 1, 2024
c504c79
Drop an unnecessary _PyInterpreterState_IDInitref() call.
ericsnowcurrently Apr 1, 2024
8a75c90
Reduce to just the new internal C-API.
ericsnowcurrently Apr 2, 2024
a38cda7
Adjust test_get_config.
ericsnowcurrently Apr 2, 2024
cae0482
Remove trailing whitespace.
ericsnowcurrently Apr 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use the new helpers in run_in_subinterp_with_config().
  • Loading branch information
ericsnowcurrently committed Mar 22, 2024
commit 0796fe99ad1a4f140f391dba57fa7721a5362553
15 changes: 13 additions & 2 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1728,8 +1728,19 @@ def run_in_subinterp_with_config(code, *, own_gil=None, **config):
import _testinternalcapi
if own_gil is not None:
assert 'gil' not in config, (own_gil, config)
config['gil'] = 2 if own_gil else 1
return _testinternalcapi.run_in_subinterp_with_config(code, **config)
config['gil'] = 'own' if own_gil else 'shared'
else:
gil = config['gil']
if gil == 0:
config['gil'] = 'default'
elif gil == 1:
config['gil'] = 'shared'
elif gil == 2:
config['gil'] = 'own'
else:
raise NotImplementedError(gil)
config = types.SimpleNamespace(**config)
return _testinternalcapi.run_in_subinterp_with_config(code, config)


def _check_tracemalloc():
Expand Down
12 changes: 10 additions & 2 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1823,15 +1823,19 @@ def check_compatible_fresh(self, name, *, strict=False, isolated=False):
**(self.ISOLATED if isolated else self.NOT_ISOLATED),
check_multi_interp_extensions=strict,
)
gil = kwargs['gil']
kwargs['gil'] = 'default' if gil == 0 else (
'shared' if gil == 1 else 'own' if gil == 2 else gil)
_, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f'''
import _testinternalcapi, sys
assert (
{name!r} in sys.builtin_module_names or
{name!r} not in sys.modules
), repr({name!r})
config = type(sys.implementation)(**{kwargs})
ret = _testinternalcapi.run_in_subinterp_with_config(
{self.import_script(name, "sys.stdout.fileno()")!r},
**{kwargs},
config,
)
assert ret == 0, ret
'''))
Expand All @@ -1847,12 +1851,16 @@ def check_incompatible_fresh(self, name, *, isolated=False):
**(self.ISOLATED if isolated else self.NOT_ISOLATED),
check_multi_interp_extensions=True,
)
gil = kwargs['gil']
kwargs['gil'] = 'default' if gil == 0 else (
'shared' if gil == 1 else 'own' if gil == 2 else gil)
_, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f'''
import _testinternalcapi, sys
assert {name!r} not in sys.modules, {name!r}
config = type(sys.implementation)(**{kwargs})
ret = _testinternalcapi.run_in_subinterp_with_config(
{self.import_script(name, "sys.stdout.fileno()")!r},
**{kwargs},
config,
)
assert ret == 0, ret
'''))
Expand Down
80 changes: 18 additions & 62 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1469,78 +1469,32 @@ static PyObject *
run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
{
const char *code;
int use_main_obmalloc = -1;
int allow_fork = -1;
int allow_exec = -1;
int allow_threads = -1;
int allow_daemon_threads = -1;
int check_multi_interp_extensions = -1;
int gil = -1;
int r;
PyThreadState *substate, *mainstate;
/* only initialise 'cflags.cf_flags' to test backwards compatibility */
PyCompilerFlags cflags = {0};

static char *kwlist[] = {"code",
"use_main_obmalloc",
"allow_fork",
"allow_exec",
"allow_threads",
"allow_daemon_threads",
"check_multi_interp_extensions",
"gil",
NULL};
PyObject *configobj;
static char *kwlist[] = {"code", "config", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"s$ppppppi:run_in_subinterp_with_config", kwlist,
&code, &use_main_obmalloc,
&allow_fork, &allow_exec,
&allow_threads, &allow_daemon_threads,
&check_multi_interp_extensions,
&gil)) {
return NULL;
}
if (use_main_obmalloc < 0) {
PyErr_SetString(PyExc_ValueError, "missing use_main_obmalloc");
return NULL;
}
if (allow_fork < 0) {
PyErr_SetString(PyExc_ValueError, "missing allow_fork");
return NULL;
}
if (allow_exec < 0) {
PyErr_SetString(PyExc_ValueError, "missing allow_exec");
return NULL;
}
if (allow_threads < 0) {
PyErr_SetString(PyExc_ValueError, "missing allow_threads");
return NULL;
}
if (gil < 0) {
PyErr_SetString(PyExc_ValueError, "missing gil");
"sO:run_in_subinterp_with_config", kwlist,
&code, &configobj))
{
return NULL;
}
if (allow_daemon_threads < 0) {
PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads");

PyObject *dict = PyObject_GetAttrString(configobj, "__dict__");
if (dict == NULL) {
PyErr_Format(PyExc_TypeError, "bad config %R", configobj);
return NULL;
}
if (check_multi_interp_extensions < 0) {
PyErr_SetString(PyExc_ValueError, "missing check_multi_interp_extensions");
PyInterpreterConfig config = {0};
int res = _PyInterpreterConfig_FromDict(dict, &config);
Py_DECREF(dict);
if (res < 0) {
return NULL;
}

mainstate = PyThreadState_Get();
PyThreadState *mainstate = PyThreadState_Get();

PyThreadState_Swap(NULL);

const PyInterpreterConfig config = {
.use_main_obmalloc = use_main_obmalloc,
.allow_fork = allow_fork,
.allow_exec = allow_exec,
.allow_threads = allow_threads,
.allow_daemon_threads = allow_daemon_threads,
.check_multi_interp_extensions = check_multi_interp_extensions,
.gil = gil,
};
PyThreadState *substate;
PyStatus status = Py_NewInterpreterFromConfig(&substate, &config);
if (PyStatus_Exception(status)) {
/* Since no new thread state was created, there is no exception to
Expand All @@ -1554,7 +1508,9 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
assert(substate != NULL);
r = PyRun_SimpleStringFlags(code, &cflags);
/* only initialise 'cflags.cf_flags' to test backwards compatibility */
PyCompilerFlags cflags = {0};
int r = PyRun_SimpleStringFlags(code, &cflags);
Py_EndInterpreter(substate);

PyThreadState_Swap(mainstate);
Expand Down
39 changes: 29 additions & 10 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -3401,18 +3401,27 @@ _config_dict_copy_str(PyObject *dict, const char *name,
}

static int
interp_config_from_dict(PyObject *dict, PyInterpreterConfig *config,
interp_config_from_dict(PyObject *origdict, PyInterpreterConfig *config,
bool missing_allowed)
{
Py_ssize_t unused = PyDict_GET_SIZE(dict);
PyObject *dict = PyDict_New();
if (dict == NULL) {
return -1;
}
if (PyDict_Update(dict, origdict) < 0) {
goto error;
}

#define CHECK(NAME) \
do { \
if (!PyErr_Occurred()) { \
if (PyErr_Occurred()) { \
goto error; \
} \
else { \
if (!missing_allowed) { \
(void)config_dict_get(dict, NAME); \
assert(PyErr_Occurred()); \
return -1; \
goto error; \
} \
} \
} while (0)
Expand All @@ -3424,7 +3433,7 @@ interp_config_from_dict(PyObject *dict, PyInterpreterConfig *config,
} \
else { \
config->FIELD = flag; \
unused -= 1; \
(void)PyDict_PopString(dict, #FIELD, NULL); \
} \
} while (0)

Expand All @@ -3443,21 +3452,31 @@ interp_config_from_dict(PyObject *dict, PyInterpreterConfig *config,
else {
int flag;
if (gil_flag_from_str(buf, &flag) < 0) {
return -1;
goto error;
}
config->gil = flag;
unused -= 1;
(void)PyDict_PopString(dict, "gil", NULL);
}

#undef COPY_BOOL
#undef CHECK

if (unused > 0) {
Py_ssize_t unused = PyDict_GET_SIZE(dict);
if (unused == 1) {
PyErr_Format(PyExc_ValueError,
"dict as %d extra items", unused);
return -1;
"config dict has 1 extra item (%R)", dict);
goto error;
}
else if (unused > 0) {
PyErr_Format(PyExc_ValueError,
"config dict has %d extra items (%R)", unused, dict);
goto error;
}
return 0;

error:
Py_DECREF(dict);
return -1;
}

int
Expand Down