summaryrefslogtreecommitdiffstats
path: root/Modules/_xxsubinterpretersmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_xxsubinterpretersmodule.c')
-rw-r--r--Modules/_xxsubinterpretersmodule.c284
1 files changed, 240 insertions, 44 deletions
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 5e5b3c1..9c2774e 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -12,8 +12,10 @@
#include "pycore_initconfig.h" // _PyErr_SetFromPyStatus()
#include "pycore_long.h" // _PyLong_IsNegative()
#include "pycore_modsupport.h" // _PyArg_BadArgument()
+#include "pycore_namespace.h" // _PyNamespace_New()
#include "pycore_pybuffer.h" // _PyBuffer_ReleaseInInterpreterAndRawFree()
#include "pycore_pyerrors.h" // _Py_excinfo
+#include "pycore_pylifecycle.h" // _PyInterpreterConfig_AsDict()
#include "pycore_pystate.h" // _PyInterpreterState_SetRunningMain()
#include "marshal.h" // PyMarshal_ReadObjectFromString()
@@ -368,6 +370,115 @@ get_code_str(PyObject *arg, Py_ssize_t *len_p, PyObject **bytes_p, int *flags_p)
/* interpreter-specific code ************************************************/
static int
+init_named_config(PyInterpreterConfig *config, const char *name)
+{
+ if (name == NULL
+ || strcmp(name, "") == 0
+ || strcmp(name, "default") == 0)
+ {
+ name = "isolated";
+ }
+
+ if (strcmp(name, "isolated") == 0) {
+ *config = (PyInterpreterConfig)_PyInterpreterConfig_INIT;
+ }
+ else if (strcmp(name, "legacy") == 0) {
+ *config = (PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
+ }
+ else if (strcmp(name, "empty") == 0) {
+ *config = (PyInterpreterConfig){0};
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "unsupported config name '%s'", name);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+config_from_object(PyObject *configobj, PyInterpreterConfig *config)
+{
+ if (configobj == NULL || configobj == Py_None) {
+ if (init_named_config(config, NULL) < 0) {
+ return -1;
+ }
+ }
+ else if (PyUnicode_Check(configobj)) {
+ if (init_named_config(config, PyUnicode_AsUTF8(configobj)) < 0) {
+ return -1;
+ }
+ }
+ else {
+ PyObject *dict = PyObject_GetAttrString(configobj, "__dict__");
+ if (dict == NULL) {
+ PyErr_Format(PyExc_TypeError, "bad config %R", configobj);
+ return -1;
+ }
+ int res = _PyInterpreterConfig_InitFromDict(config, dict);
+ Py_DECREF(dict);
+ if (res < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static PyInterpreterState *
+new_interpreter(PyInterpreterConfig *config, PyObject **p_idobj, PyThreadState **p_tstate)
+{
+ PyThreadState *save_tstate = PyThreadState_Get();
+ assert(save_tstate != NULL);
+ PyThreadState *tstate = NULL;
+ // XXX Possible GILState issues?
+ PyStatus status = Py_NewInterpreterFromConfig(&tstate, config);
+ PyThreadState_Swap(save_tstate);
+ if (PyStatus_Exception(status)) {
+ /* Since no new thread state was created, there is no exception to
+ propagate; raise a fresh one after swapping in the old thread
+ state. */
+ _PyErr_SetFromPyStatus(status);
+ return NULL;
+ }
+ assert(tstate != NULL);
+ PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate);
+
+ if (_PyInterpreterState_IDInitref(interp) < 0) {
+ goto error;
+ }
+
+ if (p_idobj != NULL) {
+ // We create the object using the original interpreter.
+ PyObject *idobj = get_interpid_obj(interp);
+ if (idobj == NULL) {
+ goto error;
+ }
+ *p_idobj = idobj;
+ }
+
+ if (p_tstate != NULL) {
+ *p_tstate = tstate;
+ }
+ else {
+ PyThreadState_Swap(tstate);
+ PyThreadState_Clear(tstate);
+ PyThreadState_Swap(save_tstate);
+ PyThreadState_Delete(tstate);
+ }
+
+ return interp;
+
+error:
+ // XXX Possible GILState issues?
+ save_tstate = PyThreadState_Swap(tstate);
+ Py_EndInterpreter(tstate);
+ PyThreadState_Swap(save_tstate);
+ return NULL;
+}
+
+
+static int
_run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
{
PyObject *result = NULL;
@@ -436,64 +547,98 @@ _run_in_interpreter(PyInterpreterState *interp,
/* module level code ********************************************************/
static PyObject *
-interp_create(PyObject *self, PyObject *args, PyObject *kwds)
+interp_new_config(PyObject *self, PyObject *args, PyObject *kwds)
{
+ const char *name = NULL;
+ if (!PyArg_ParseTuple(args, "|s:" MODULE_NAME_STR ".new_config",
+ &name))
+ {
+ return NULL;
+ }
+ PyObject *overrides = kwds;
- static char *kwlist[] = {"isolated", NULL};
- int isolated = 1;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$i:create", kwlist,
- &isolated)) {
+ PyInterpreterConfig config;
+ if (init_named_config(&config, name) < 0) {
return NULL;
}
- // Create and initialize the new interpreter.
- PyThreadState *save_tstate = PyThreadState_Get();
- assert(save_tstate != NULL);
- const PyInterpreterConfig config = isolated
- ? (PyInterpreterConfig)_PyInterpreterConfig_INIT
- : (PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
+ if (overrides != NULL && PyDict_GET_SIZE(overrides) > 0) {
+ if (_PyInterpreterConfig_UpdateFromDict(&config, overrides) < 0) {
+ return NULL;
+ }
+ }
- // XXX Possible GILState issues?
- PyThreadState *tstate = NULL;
- PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
- PyThreadState_Swap(save_tstate);
- if (PyStatus_Exception(status)) {
- /* Since no new thread state was created, there is no exception to
- propagate; raise a fresh one after swapping in the old thread
- state. */
- _PyErr_SetFromPyStatus(status);
+ PyObject *dict = _PyInterpreterConfig_AsDict(&config);
+ if (dict == NULL) {
+ return NULL;
+ }
+
+ PyObject *configobj = _PyNamespace_New(dict);
+ Py_DECREF(dict);
+ return configobj;
+}
+
+PyDoc_STRVAR(new_config_doc,
+"new_config(name='isolated', /, **overrides) -> type.SimpleNamespace\n\
+\n\
+Return a representation of a new PyInterpreterConfig.\n\
+\n\
+The name determines the initial values of the config. Supported named\n\
+configs are: default, isolated, legacy, and empty.\n\
+\n\
+Any keyword arguments are set on the corresponding config fields,\n\
+overriding the initial values.");
+
+
+static PyObject *
+interp_create(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"config", "reqrefs", NULL};
+ PyObject *configobj = NULL;
+ int reqrefs = 0;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O$p:create", kwlist,
+ &configobj, &reqrefs)) {
+ return NULL;
+ }
+
+ PyInterpreterConfig config;
+ if (config_from_object(configobj, &config) < 0) {
+ return NULL;
+ }
+
+ PyObject *idobj = NULL;
+ PyInterpreterState *interp = new_interpreter(&config, &idobj, NULL);
+ if (interp == NULL) {
+ // XXX Move the chained exception to interpreters.create()?
PyObject *exc = PyErr_GetRaisedException();
+ assert(exc != NULL);
PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
_PyErr_ChainExceptions1(exc);
return NULL;
}
- assert(tstate != NULL);
- PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate);
- PyObject *idobj = get_interpid_obj(interp);
- if (idobj == NULL) {
- // XXX Possible GILState issues?
- save_tstate = PyThreadState_Swap(tstate);
- Py_EndInterpreter(tstate);
- PyThreadState_Swap(save_tstate);
- return NULL;
+ if (reqrefs) {
+ // Decref to 0 will destroy the interpreter.
+ _PyInterpreterState_RequireIDRef(interp, 1);
}
- PyThreadState_Swap(tstate);
- PyThreadState_Clear(tstate);
- PyThreadState_Swap(save_tstate);
- PyThreadState_Delete(tstate);
-
- _PyInterpreterState_RequireIDRef(interp, 1);
return idobj;
}
+
PyDoc_STRVAR(create_doc,
-"create() -> ID\n\
+"create([config], *, reqrefs=False) -> ID\n\
\n\
Create a new interpreter and return a unique generated ID.\n\
\n\
-The caller is responsible for destroying the interpreter before exiting.");
+The caller is responsible for destroying the interpreter before exiting,\n\
+typically by using _interpreters.destroy(). This can be managed \n\
+automatically by passing \"reqrefs=True\" and then using _incref() and\n\
+_decref()` appropriately.\n\
+\n\
+\"config\" must be a valid interpreter config or the name of a\n\
+predefined config (\"isolated\" or \"legacy\"). The default\n\
+is \"isolated\".");
static PyObject *
@@ -1008,12 +1153,57 @@ Return whether or not the identified interpreter is running.");
static PyObject *
-interp_incref(PyObject *self, PyObject *args, PyObject *kwds)
+interp_get_config(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"id", NULL};
+ PyObject *idobj = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds,
+ "O:get_config", kwlist, &idobj))
+ {
+ return NULL;
+ }
+
+ PyInterpreterState *interp;
+ if (idobj == NULL) {
+ interp = PyInterpreterState_Get();
+ }
+ else {
+ interp = _PyInterpreterState_LookUpIDObject(idobj);
+ if (interp == NULL) {
+ return NULL;
+ }
+ }
+
+ PyInterpreterConfig config;
+ if (_PyInterpreterConfig_InitFromState(&config, interp) < 0) {
+ return NULL;
+ }
+ PyObject *dict = _PyInterpreterConfig_AsDict(&config);
+ if (dict == NULL) {
+ return NULL;
+ }
+
+ PyObject *configobj = _PyNamespace_New(dict);
+ Py_DECREF(dict);
+ return configobj;
+}
+
+PyDoc_STRVAR(get_config_doc,
+"get_config(id) -> types.SimpleNamespace\n\
+\n\
+Return a representation of the config used to initialize the interpreter.");
+
+
+static PyObject *
+interp_incref(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"id", "implieslink", NULL};
PyObject *id;
+ int implieslink = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O:_incref", kwlist, &id)) {
+ "O|$p:incref", kwlist,
+ &id, &implieslink))
+ {
return NULL;
}
@@ -1021,8 +1211,10 @@ interp_incref(PyObject *self, PyObject *args, PyObject *kwds)
if (interp == NULL) {
return NULL;
}
- if (_PyInterpreterState_IDInitref(interp) < 0) {
- return NULL;
+
+ if (implieslink) {
+ // Decref to 0 will destroy the interpreter.
+ _PyInterpreterState_RequireIDRef(interp, 1);
}
_PyInterpreterState_IDIncref(interp);
@@ -1036,7 +1228,7 @@ interp_decref(PyObject *self, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"id", NULL};
PyObject *id;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O:_incref", kwlist, &id)) {
+ "O:decref", kwlist, &id)) {
return NULL;
}
@@ -1051,6 +1243,8 @@ interp_decref(PyObject *self, PyObject *args, PyObject *kwds)
static PyMethodDef module_functions[] = {
+ {"new_config", _PyCFunction_CAST(interp_new_config),
+ METH_VARARGS | METH_KEYWORDS, new_config_doc},
{"create", _PyCFunction_CAST(interp_create),
METH_VARARGS | METH_KEYWORDS, create_doc},
{"destroy", _PyCFunction_CAST(interp_destroy),
@@ -1064,6 +1258,8 @@ static PyMethodDef module_functions[] = {
{"is_running", _PyCFunction_CAST(interp_is_running),
METH_VARARGS | METH_KEYWORDS, is_running_doc},
+ {"get_config", _PyCFunction_CAST(interp_get_config),
+ METH_VARARGS | METH_KEYWORDS, get_config_doc},
{"exec", _PyCFunction_CAST(interp_exec),
METH_VARARGS | METH_KEYWORDS, exec_doc},
{"call", _PyCFunction_CAST(interp_call),
@@ -1078,9 +1274,9 @@ static PyMethodDef module_functions[] = {
{"is_shareable", _PyCFunction_CAST(object_is_shareable),
METH_VARARGS | METH_KEYWORDS, is_shareable_doc},
- {"_incref", _PyCFunction_CAST(interp_incref),
+ {"incref", _PyCFunction_CAST(interp_incref),
METH_VARARGS | METH_KEYWORDS, NULL},
- {"_decref", _PyCFunction_CAST(interp_decref),
+ {"decref", _PyCFunction_CAST(interp_decref),
METH_VARARGS | METH_KEYWORDS, NULL},
{NULL, NULL} /* sentinel */