| /* interpreters module */ |
| /* low-level access to interpreter primitives */ |
| |
| #ifndef Py_BUILD_CORE_BUILTIN |
| # define Py_BUILD_CORE_MODULE 1 |
| #endif |
| |
| #include "Python.h" |
| #include "pycore_abstract.h" // _PyIndex_Check() |
| #include "pycore_crossinterp.h" // struct _xid |
| #include "pycore_interp.h" // _PyInterpreterState_IDIncref() |
| #include "pycore_initconfig.h" // _PyErr_SetFromPyStatus() |
| #include "pycore_long.h" // _PyLong_IsNegative() |
| #include "pycore_modsupport.h" // _PyArg_BadArgument() |
| #include "pycore_pybuffer.h" // _PyBuffer_ReleaseInInterpreterAndRawFree() |
| #include "pycore_pyerrors.h" // _Py_excinfo |
| #include "pycore_pystate.h" // _PyInterpreterState_SetRunningMain() |
| |
| #include "interpreteridobject.h" |
| #include "marshal.h" // PyMarshal_ReadObjectFromString() |
| |
| |
| #define MODULE_NAME "_xxsubinterpreters" |
| |
| |
| static PyInterpreterState * |
| _get_current_interp(void) |
| { |
| // PyInterpreterState_Get() aborts if lookup fails, so don't need |
| // to check the result for NULL. |
| return PyInterpreterState_Get(); |
| } |
| |
| static int64_t |
| pylong_to_interpid(PyObject *idobj) |
| { |
| assert(PyLong_CheckExact(idobj)); |
| |
| if (_PyLong_IsNegative((PyLongObject *)idobj)) { |
| PyErr_Format(PyExc_ValueError, |
| "interpreter ID must be a non-negative int, got %R", |
| idobj); |
| return -1; |
| } |
| |
| int overflow; |
| long long id = PyLong_AsLongLongAndOverflow(idobj, &overflow); |
| if (id == -1) { |
| if (!overflow) { |
| assert(PyErr_Occurred()); |
| return -1; |
| } |
| assert(!PyErr_Occurred()); |
| // For now, we don't worry about if LLONG_MAX < INT64_MAX. |
| goto bad_id; |
| } |
| #if LLONG_MAX > INT64_MAX |
| if (id > INT64_MAX) { |
| goto bad_id; |
| } |
| #endif |
| return (int64_t)id; |
| |
| bad_id: |
| PyErr_Format(PyExc_RuntimeError, |
| "unrecognized interpreter ID %O", idobj); |
| return -1; |
| } |
| |
| static int64_t |
| convert_interpid_obj(PyObject *arg) |
| { |
| int64_t id = -1; |
| if (_PyIndex_Check(arg)) { |
| PyObject *idobj = PyNumber_Long(arg); |
| if (idobj == NULL) { |
| return -1; |
| } |
| id = pylong_to_interpid(idobj); |
| Py_DECREF(idobj); |
| if (id < 0) { |
| return -1; |
| } |
| } |
| else { |
| PyErr_Format(PyExc_TypeError, |
| "interpreter ID must be an int, got %.100s", |
| Py_TYPE(arg)->tp_name); |
| return -1; |
| } |
| return id; |
| } |
| |
| static PyInterpreterState * |
| look_up_interp(PyObject *arg) |
| { |
| int64_t id = convert_interpid_obj(arg); |
| if (id < 0) { |
| return NULL; |
| } |
| return _PyInterpreterState_LookUpID(id); |
| } |
| |
| |
| static PyObject * |
| interpid_to_pylong(int64_t id) |
| { |
| assert(id < LLONG_MAX); |
| return PyLong_FromLongLong(id); |
| } |
| |
| static PyObject * |
| get_interpid_obj(PyInterpreterState *interp) |
| { |
| if (_PyInterpreterState_IDInitref(interp) != 0) { |
| return NULL; |
| }; |
| int64_t id = PyInterpreterState_GetID(interp); |
| if (id < 0) { |
| return NULL; |
| } |
| return interpid_to_pylong(id); |
| } |
| |
| static PyObject * |
| _get_current_module(void) |
| { |
| PyObject *name = PyUnicode_FromString(MODULE_NAME); |
| if (name == NULL) { |
| return NULL; |
| } |
| PyObject *mod = PyImport_GetModule(name); |
| Py_DECREF(name); |
| if (mod == NULL) { |
| return NULL; |
| } |
| assert(mod != Py_None); |
| return mod; |
| } |
| |
| |
| /* Cross-interpreter Buffer Views *******************************************/ |
| |
| // XXX Release when the original interpreter is destroyed. |
| |
| typedef struct { |
| PyObject_HEAD |
| Py_buffer *view; |
| int64_t interpid; |
| } XIBufferViewObject; |
| |
| static PyObject * |
| xibufferview_from_xid(PyTypeObject *cls, _PyCrossInterpreterData *data) |
| { |
| assert(data->data != NULL); |
| assert(data->obj == NULL); |
| assert(data->interpid >= 0); |
| XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject)); |
| if (self == NULL) { |
| return NULL; |
| } |
| PyObject_Init((PyObject *)self, cls); |
| self->view = (Py_buffer *)data->data; |
| self->interpid = data->interpid; |
| return (PyObject *)self; |
| } |
| |
| static void |
| xibufferview_dealloc(XIBufferViewObject *self) |
| { |
| PyInterpreterState *interp = _PyInterpreterState_LookUpID(self->interpid); |
| /* If the interpreter is no longer alive then we have problems, |
| since other objects may be using the buffer still. */ |
| assert(interp != NULL); |
| |
| if (_PyBuffer_ReleaseInInterpreterAndRawFree(interp, self->view) < 0) { |
| // XXX Emit a warning? |
| PyErr_Clear(); |
| } |
| |
| PyTypeObject *tp = Py_TYPE(self); |
| tp->tp_free(self); |
| /* "Instances of heap-allocated types hold a reference to their type." |
| * See: https://docs.python.org/3.11/howto/isolating-extensions.html#garbage-collection-protocol |
| * See: https://docs.python.org/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse |
| */ |
| // XXX Why don't we implement Py_TPFLAGS_HAVE_GC, e.g. Py_tp_traverse, |
| // like we do for _abc._abc_data? |
| Py_DECREF(tp); |
| } |
| |
| static int |
| xibufferview_getbuf(XIBufferViewObject *self, Py_buffer *view, int flags) |
| { |
| /* Only PyMemoryView_FromObject() should ever call this, |
| via _memoryview_from_xid() below. */ |
| *view = *self->view; |
| view->obj = (PyObject *)self; |
| // XXX Should we leave it alone? |
| view->internal = NULL; |
| return 0; |
| } |
| |
| static PyType_Slot XIBufferViewType_slots[] = { |
| {Py_tp_dealloc, (destructor)xibufferview_dealloc}, |
| {Py_bf_getbuffer, (getbufferproc)xibufferview_getbuf}, |
| // We don't bother with Py_bf_releasebuffer since we don't need it. |
| {0, NULL}, |
| }; |
| |
| static PyType_Spec XIBufferViewType_spec = { |
| .name = MODULE_NAME ".CrossInterpreterBufferView", |
| .basicsize = sizeof(XIBufferViewObject), |
| .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | |
| Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE), |
| .slots = XIBufferViewType_slots, |
| }; |
| |
| |
| static PyTypeObject * _get_current_xibufferview_type(void); |
| |
| static PyObject * |
| _memoryview_from_xid(_PyCrossInterpreterData *data) |
| { |
| PyTypeObject *cls = _get_current_xibufferview_type(); |
| if (cls == NULL) { |
| return NULL; |
| } |
| PyObject *obj = xibufferview_from_xid(cls, data); |
| if (obj == NULL) { |
| return NULL; |
| } |
| return PyMemoryView_FromObject(obj); |
| } |
| |
| static int |
| _memoryview_shared(PyThreadState *tstate, PyObject *obj, |
| _PyCrossInterpreterData *data) |
| { |
| Py_buffer *view = PyMem_RawMalloc(sizeof(Py_buffer)); |
| if (view == NULL) { |
| return -1; |
| } |
| if (PyObject_GetBuffer(obj, view, PyBUF_FULL_RO) < 0) { |
| PyMem_RawFree(view); |
| return -1; |
| } |
| _PyCrossInterpreterData_Init(data, tstate->interp, view, NULL, |
| _memoryview_from_xid); |
| return 0; |
| } |
| |
| static int |
| register_memoryview_xid(PyObject *mod, PyTypeObject **p_state) |
| { |
| // XIBufferView |
| assert(*p_state == NULL); |
| PyTypeObject *cls = (PyTypeObject *)PyType_FromModuleAndSpec( |
| mod, &XIBufferViewType_spec, NULL); |
| if (cls == NULL) { |
| return -1; |
| } |
| if (PyModule_AddType(mod, cls) < 0) { |
| Py_DECREF(cls); |
| return -1; |
| } |
| *p_state = cls; |
| |
| // Register XID for the builtin memoryview type. |
| if (_PyCrossInterpreterData_RegisterClass( |
| &PyMemoryView_Type, _memoryview_shared) < 0) { |
| return -1; |
| } |
| // We don't ever bother un-registering memoryview. |
| |
| return 0; |
| } |
| |
| |
| |
| /* module state *************************************************************/ |
| |
| typedef struct { |
| int _notused; |
| |
| /* heap types */ |
| PyTypeObject *XIBufferViewType; |
| } module_state; |
| |
| static inline module_state * |
| get_module_state(PyObject *mod) |
| { |
| assert(mod != NULL); |
| module_state *state = PyModule_GetState(mod); |
| assert(state != NULL); |
| return state; |
| } |
| |
| static module_state * |
| _get_current_module_state(void) |
| { |
| PyObject *mod = _get_current_module(); |
| if (mod == NULL) { |
| // XXX import it? |
| PyErr_SetString(PyExc_RuntimeError, |
| MODULE_NAME " module not imported yet"); |
| return NULL; |
| } |
| module_state *state = get_module_state(mod); |
| Py_DECREF(mod); |
| return state; |
| } |
| |
| static int |
| traverse_module_state(module_state *state, visitproc visit, void *arg) |
| { |
| /* heap types */ |
| Py_VISIT(state->XIBufferViewType); |
| |
| return 0; |
| } |
| |
| static int |
| clear_module_state(module_state *state) |
| { |
| /* heap types */ |
| Py_CLEAR(state->XIBufferViewType); |
| |
| return 0; |
| } |
| |
| |
| static PyTypeObject * |
| _get_current_xibufferview_type(void) |
| { |
| module_state *state = _get_current_module_state(); |
| if (state == NULL) { |
| return NULL; |
| } |
| return state->XIBufferViewType; |
| } |
| |
| |
| /* Python code **************************************************************/ |
| |
| static const char * |
| check_code_str(PyUnicodeObject *text) |
| { |
| assert(text != NULL); |
| if (PyUnicode_GET_LENGTH(text) == 0) { |
| return "too short"; |
| } |
| |
| // XXX Verify that it parses? |
| |
| return NULL; |
| } |
| |
| static const char * |
| check_code_object(PyCodeObject *code) |
| { |
| assert(code != NULL); |
| if (code->co_argcount > 0 |
| || code->co_posonlyargcount > 0 |
| || code->co_kwonlyargcount > 0 |
| || code->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) |
| { |
| return "arguments not supported"; |
| } |
| if (code->co_ncellvars > 0) { |
| return "closures not supported"; |
| } |
| // We trust that no code objects under co_consts have unbound cell vars. |
| |
| if (code->co_executors != NULL |
| || code->_co_instrumentation_version > 0) |
| { |
| return "only basic functions are supported"; |
| } |
| if (code->_co_monitoring != NULL) { |
| return "only basic functions are supported"; |
| } |
| if (code->co_extra != NULL) { |
| return "only basic functions are supported"; |
| } |
| |
| return NULL; |
| } |
| |
| #define RUN_TEXT 1 |
| #define RUN_CODE 2 |
| |
| static const char * |
| get_code_str(PyObject *arg, Py_ssize_t *len_p, PyObject **bytes_p, int *flags_p) |
| { |
| const char *codestr = NULL; |
| Py_ssize_t len = -1; |
| PyObject *bytes_obj = NULL; |
| int flags = 0; |
| |
| if (PyUnicode_Check(arg)) { |
| assert(PyUnicode_CheckExact(arg) |
| && (check_code_str((PyUnicodeObject *)arg) == NULL)); |
| codestr = PyUnicode_AsUTF8AndSize(arg, &len); |
| if (codestr == NULL) { |
| return NULL; |
| } |
| if (strlen(codestr) != (size_t)len) { |
| PyErr_SetString(PyExc_ValueError, |
| "source code string cannot contain null bytes"); |
| return NULL; |
| } |
| flags = RUN_TEXT; |
| } |
| else { |
| assert(PyCode_Check(arg) |
| && (check_code_object((PyCodeObject *)arg) == NULL)); |
| flags = RUN_CODE; |
| |
| // Serialize the code object. |
| bytes_obj = PyMarshal_WriteObjectToString(arg, Py_MARSHAL_VERSION); |
| if (bytes_obj == NULL) { |
| return NULL; |
| } |
| codestr = PyBytes_AS_STRING(bytes_obj); |
| len = PyBytes_GET_SIZE(bytes_obj); |
| } |
| |
| *flags_p = flags; |
| *bytes_p = bytes_obj; |
| *len_p = len; |
| return codestr; |
| } |
| |
| |
| /* interpreter-specific code ************************************************/ |
| |
| static int |
| _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags) |
| { |
| PyObject *result = NULL; |
| if (flags & RUN_TEXT) { |
| result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); |
| } |
| else if (flags & RUN_CODE) { |
| PyObject *code = PyMarshal_ReadObjectFromString(codestr, codestrlen); |
| if (code != NULL) { |
| result = PyEval_EvalCode(code, ns, ns); |
| Py_DECREF(code); |
| } |
| } |
| else { |
| Py_UNREACHABLE(); |
| } |
| if (result == NULL) { |
| return -1; |
| } |
| Py_DECREF(result); // We throw away the result. |
| return 0; |
| } |
| |
| static int |
| _run_in_interpreter(PyInterpreterState *interp, |
| const char *codestr, Py_ssize_t codestrlen, |
| PyObject *shareables, int flags, |
| PyObject **p_excinfo) |
| { |
| assert(!PyErr_Occurred()); |
| _PyXI_session session = {0}; |
| |
| // Prep and switch interpreters. |
| if (_PyXI_Enter(&session, interp, shareables) < 0) { |
| assert(!PyErr_Occurred()); |
| PyObject *excinfo = _PyXI_ApplyError(session.error); |
| if (excinfo != NULL) { |
| *p_excinfo = excinfo; |
| } |
| assert(PyErr_Occurred()); |
| return -1; |
| } |
| |
| // Run the script. |
| int res = _run_script(session.main_ns, codestr, codestrlen, flags); |
| |
| // Clean up and switch back. |
| _PyXI_Exit(&session); |
| |
| // Propagate any exception out to the caller. |
| assert(!PyErr_Occurred()); |
| if (res < 0) { |
| PyObject *excinfo = _PyXI_ApplyCapturedException(&session); |
| if (excinfo != NULL) { |
| *p_excinfo = excinfo; |
| } |
| } |
| else { |
| assert(!_PyXI_HasCapturedException(&session)); |
| } |
| |
| return res; |
| } |
| |
| |
| /* module level code ********************************************************/ |
| |
| static PyObject * |
| interp_create(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| |
| static char *kwlist[] = {"isolated", NULL}; |
| int isolated = 1; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$i:create", kwlist, |
| &isolated)) { |
| 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; |
| |
| // 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 *exc = PyErr_GetRaisedException(); |
| 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; |
| } |
| |
| 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\ |
| \n\ |
| Create a new interpreter and return a unique generated ID.\n\ |
| \n\ |
| The caller is responsible for destroying the interpreter before exiting."); |
| |
| |
| static PyObject * |
| interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", NULL}; |
| PyObject *id; |
| // XXX Use "L" for id? |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "O:destroy", kwlist, &id)) { |
| return NULL; |
| } |
| |
| // Look up the interpreter. |
| PyInterpreterState *interp = look_up_interp(id); |
| if (interp == NULL) { |
| return NULL; |
| } |
| |
| // Ensure we don't try to destroy the current interpreter. |
| PyInterpreterState *current = _get_current_interp(); |
| if (current == NULL) { |
| return NULL; |
| } |
| if (interp == current) { |
| PyErr_SetString(PyExc_RuntimeError, |
| "cannot destroy the current interpreter"); |
| return NULL; |
| } |
| |
| // Ensure the interpreter isn't running. |
| /* XXX We *could* support destroying a running interpreter but |
| aren't going to worry about it for now. */ |
| if (_PyInterpreterState_IsRunningMain(interp)) { |
| PyErr_Format(PyExc_RuntimeError, "interpreter running"); |
| return NULL; |
| } |
| |
| // Destroy the interpreter. |
| PyThreadState *tstate = PyThreadState_New(interp); |
| tstate->_whence = _PyThreadState_WHENCE_INTERP; |
| // XXX Possible GILState issues? |
| PyThreadState *save_tstate = PyThreadState_Swap(tstate); |
| Py_EndInterpreter(tstate); |
| PyThreadState_Swap(save_tstate); |
| |
| Py_RETURN_NONE; |
| } |
| |
| PyDoc_STRVAR(destroy_doc, |
| "destroy(id)\n\ |
| \n\ |
| Destroy the identified interpreter.\n\ |
| \n\ |
| Attempting to destroy the current interpreter results in a RuntimeError.\n\ |
| So does an unrecognized ID."); |
| |
| |
| static PyObject * |
| interp_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) |
| { |
| PyObject *ids, *id; |
| PyInterpreterState *interp; |
| |
| ids = PyList_New(0); |
| if (ids == NULL) { |
| return NULL; |
| } |
| |
| interp = PyInterpreterState_Head(); |
| while (interp != NULL) { |
| id = get_interpid_obj(interp); |
| if (id == NULL) { |
| Py_DECREF(ids); |
| return NULL; |
| } |
| // insert at front of list |
| int res = PyList_Insert(ids, 0, id); |
| Py_DECREF(id); |
| if (res < 0) { |
| Py_DECREF(ids); |
| return NULL; |
| } |
| |
| interp = PyInterpreterState_Next(interp); |
| } |
| |
| return ids; |
| } |
| |
| PyDoc_STRVAR(list_all_doc, |
| "list_all() -> [ID]\n\ |
| \n\ |
| Return a list containing the ID of every existing interpreter."); |
| |
| |
| static PyObject * |
| interp_get_current(PyObject *self, PyObject *Py_UNUSED(ignored)) |
| { |
| PyInterpreterState *interp =_get_current_interp(); |
| if (interp == NULL) { |
| return NULL; |
| } |
| return get_interpid_obj(interp); |
| } |
| |
| PyDoc_STRVAR(get_current_doc, |
| "get_current() -> ID\n\ |
| \n\ |
| Return the ID of current interpreter."); |
| |
| |
| static PyObject * |
| interp_get_main(PyObject *self, PyObject *Py_UNUSED(ignored)) |
| { |
| // Currently, 0 is always the main interpreter. |
| int64_t id = 0; |
| return PyLong_FromLongLong(id); |
| } |
| |
| PyDoc_STRVAR(get_main_doc, |
| "get_main() -> ID\n\ |
| \n\ |
| Return the ID of main interpreter."); |
| |
| static PyObject * |
| interp_set___main___attrs(PyObject *self, PyObject *args) |
| { |
| PyObject *id, *updates; |
| if (!PyArg_ParseTuple(args, "OO:" MODULE_NAME ".set___main___attrs", |
| &id, &updates)) |
| { |
| return NULL; |
| } |
| |
| // Look up the interpreter. |
| PyInterpreterState *interp = PyInterpreterID_LookUp(id); |
| if (interp == NULL) { |
| return NULL; |
| } |
| |
| // Check the updates. |
| if (updates != Py_None) { |
| Py_ssize_t size = PyObject_Size(updates); |
| if (size < 0) { |
| return NULL; |
| } |
| if (size == 0) { |
| PyErr_SetString(PyExc_ValueError, |
| "arg 2 must be a non-empty mapping"); |
| return NULL; |
| } |
| } |
| |
| _PyXI_session session = {0}; |
| |
| // Prep and switch interpreters, including apply the updates. |
| if (_PyXI_Enter(&session, interp, updates) < 0) { |
| if (!PyErr_Occurred()) { |
| _PyXI_ApplyCapturedException(&session); |
| assert(PyErr_Occurred()); |
| } |
| else { |
| assert(!_PyXI_HasCapturedException(&session)); |
| } |
| return NULL; |
| } |
| |
| // Clean up and switch back. |
| _PyXI_Exit(&session); |
| |
| Py_RETURN_NONE; |
| } |
| |
| PyDoc_STRVAR(set___main___attrs_doc, |
| "set___main___attrs(id, ns)\n\ |
| \n\ |
| Bind the given attributes in the interpreter's __main__ module."); |
| |
| static PyUnicodeObject * |
| convert_script_arg(PyObject *arg, const char *fname, const char *displayname, |
| const char *expected) |
| { |
| PyUnicodeObject *str = NULL; |
| if (PyUnicode_CheckExact(arg)) { |
| str = (PyUnicodeObject *)Py_NewRef(arg); |
| } |
| else if (PyUnicode_Check(arg)) { |
| // XXX str = PyUnicode_FromObject(arg); |
| str = (PyUnicodeObject *)Py_NewRef(arg); |
| } |
| else { |
| _PyArg_BadArgument(fname, displayname, expected, arg); |
| return NULL; |
| } |
| |
| const char *err = check_code_str(str); |
| if (err != NULL) { |
| Py_DECREF(str); |
| PyErr_Format(PyExc_ValueError, |
| "%.200s(): bad script text (%s)", fname, err); |
| return NULL; |
| } |
| |
| return str; |
| } |
| |
| static PyCodeObject * |
| convert_code_arg(PyObject *arg, const char *fname, const char *displayname, |
| const char *expected) |
| { |
| const char *kind = NULL; |
| PyCodeObject *code = NULL; |
| if (PyFunction_Check(arg)) { |
| if (PyFunction_GetClosure(arg) != NULL) { |
| PyErr_Format(PyExc_ValueError, |
| "%.200s(): closures not supported", fname); |
| return NULL; |
| } |
| code = (PyCodeObject *)PyFunction_GetCode(arg); |
| if (code == NULL) { |
| if (PyErr_Occurred()) { |
| // This chains. |
| PyErr_Format(PyExc_ValueError, |
| "%.200s(): bad func", fname); |
| } |
| else { |
| PyErr_Format(PyExc_ValueError, |
| "%.200s(): func.__code__ missing", fname); |
| } |
| return NULL; |
| } |
| Py_INCREF(code); |
| kind = "func"; |
| } |
| else if (PyCode_Check(arg)) { |
| code = (PyCodeObject *)Py_NewRef(arg); |
| kind = "code object"; |
| } |
| else { |
| _PyArg_BadArgument(fname, displayname, expected, arg); |
| return NULL; |
| } |
| |
| const char *err = check_code_object(code); |
| if (err != NULL) { |
| Py_DECREF(code); |
| PyErr_Format(PyExc_ValueError, |
| "%.200s(): bad %s (%s)", fname, kind, err); |
| return NULL; |
| } |
| |
| return code; |
| } |
| |
| static int |
| _interp_exec(PyObject *self, |
| PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg, |
| PyObject **p_excinfo) |
| { |
| // Look up the interpreter. |
| PyInterpreterState *interp = look_up_interp(id_arg); |
| if (interp == NULL) { |
| return -1; |
| } |
| |
| // Extract code. |
| Py_ssize_t codestrlen = -1; |
| PyObject *bytes_obj = NULL; |
| int flags = 0; |
| const char *codestr = get_code_str(code_arg, |
| &codestrlen, &bytes_obj, &flags); |
| if (codestr == NULL) { |
| return -1; |
| } |
| |
| // Run the code in the interpreter. |
| int res = _run_in_interpreter(interp, codestr, codestrlen, |
| shared_arg, flags, p_excinfo); |
| Py_XDECREF(bytes_obj); |
| if (res < 0) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static PyObject * |
| interp_exec(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", "code", "shared", NULL}; |
| PyObject *id, *code; |
| PyObject *shared = NULL; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "OO|O:" MODULE_NAME ".exec", kwlist, |
| &id, &code, &shared)) { |
| return NULL; |
| } |
| |
| const char *expected = "a string, a function, or a code object"; |
| if (PyUnicode_Check(code)) { |
| code = (PyObject *)convert_script_arg(code, MODULE_NAME ".exec", |
| "argument 2", expected); |
| } |
| else { |
| code = (PyObject *)convert_code_arg(code, MODULE_NAME ".exec", |
| "argument 2", expected); |
| } |
| if (code == NULL) { |
| return NULL; |
| } |
| |
| PyObject *excinfo = NULL; |
| int res = _interp_exec(self, id, code, shared, &excinfo); |
| Py_DECREF(code); |
| if (res < 0) { |
| assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); |
| return excinfo; |
| } |
| Py_RETURN_NONE; |
| } |
| |
| PyDoc_STRVAR(exec_doc, |
| "exec(id, code, shared=None)\n\ |
| \n\ |
| Execute the provided code in the identified interpreter.\n\ |
| This is equivalent to running the builtin exec() under the target\n\ |
| interpreter, using the __dict__ of its __main__ module as both\n\ |
| globals and locals.\n\ |
| \n\ |
| \"code\" may be a string containing the text of a Python script.\n\ |
| \n\ |
| Functions (and code objects) are also supported, with some restrictions.\n\ |
| The code/function must not take any arguments or be a closure\n\ |
| (i.e. have cell vars). Methods and other callables are not supported.\n\ |
| \n\ |
| If a function is provided, its code object is used and all its state\n\ |
| is ignored, including its __globals__ dict."); |
| |
| static PyObject * |
| interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", "script", "shared", NULL}; |
| PyObject *id, *script; |
| PyObject *shared = NULL; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "OU|O:" MODULE_NAME ".run_string", kwlist, |
| &id, &script, &shared)) { |
| return NULL; |
| } |
| |
| script = (PyObject *)convert_script_arg(script, MODULE_NAME ".exec", |
| "argument 2", "a string"); |
| if (script == NULL) { |
| return NULL; |
| } |
| |
| PyObject *excinfo = NULL; |
| int res = _interp_exec(self, id, script, shared, &excinfo); |
| Py_DECREF(script); |
| if (res < 0) { |
| assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); |
| return excinfo; |
| } |
| Py_RETURN_NONE; |
| } |
| |
| PyDoc_STRVAR(run_string_doc, |
| "run_string(id, script, shared=None)\n\ |
| \n\ |
| Execute the provided string in the identified interpreter.\n\ |
| \n\ |
| (See " MODULE_NAME ".exec()."); |
| |
| static PyObject * |
| interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", "func", "shared", NULL}; |
| PyObject *id, *func; |
| PyObject *shared = NULL; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "OO|O:" MODULE_NAME ".run_func", kwlist, |
| &id, &func, &shared)) { |
| return NULL; |
| } |
| |
| PyCodeObject *code = convert_code_arg(func, MODULE_NAME ".exec", |
| "argument 2", |
| "a function or a code object"); |
| if (code == NULL) { |
| return NULL; |
| } |
| |
| PyObject *excinfo = NULL; |
| int res = _interp_exec(self, id, (PyObject *)code, shared, &excinfo); |
| Py_DECREF(code); |
| if (res < 0) { |
| assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); |
| return excinfo; |
| } |
| Py_RETURN_NONE; |
| } |
| |
| PyDoc_STRVAR(run_func_doc, |
| "run_func(id, func, shared=None)\n\ |
| \n\ |
| Execute the body of the provided function in the identified interpreter.\n\ |
| Code objects are also supported. In both cases, closures and args\n\ |
| are not supported. Methods and other callables are not supported either.\n\ |
| \n\ |
| (See " MODULE_NAME ".exec()."); |
| |
| |
| static PyObject * |
| object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"obj", NULL}; |
| PyObject *obj; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "O:is_shareable", kwlist, &obj)) { |
| return NULL; |
| } |
| |
| if (_PyObject_CheckCrossInterpreterData(obj) == 0) { |
| Py_RETURN_TRUE; |
| } |
| PyErr_Clear(); |
| Py_RETURN_FALSE; |
| } |
| |
| PyDoc_STRVAR(is_shareable_doc, |
| "is_shareable(obj) -> bool\n\ |
| \n\ |
| Return True if the object's data may be shared between interpreters and\n\ |
| False otherwise."); |
| |
| |
| static PyObject * |
| interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", NULL}; |
| PyObject *id; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "O:is_running", kwlist, &id)) { |
| return NULL; |
| } |
| |
| PyInterpreterState *interp = look_up_interp(id); |
| if (interp == NULL) { |
| return NULL; |
| } |
| if (_PyInterpreterState_IsRunningMain(interp)) { |
| Py_RETURN_TRUE; |
| } |
| Py_RETURN_FALSE; |
| } |
| |
| PyDoc_STRVAR(is_running_doc, |
| "is_running(id) -> bool\n\ |
| \n\ |
| Return whether or not the identified interpreter is running."); |
| |
| |
| static PyObject * |
| interp_incref(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", NULL}; |
| PyObject *id; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "O:_incref", kwlist, &id)) { |
| return NULL; |
| } |
| |
| PyInterpreterState *interp = look_up_interp(id); |
| if (interp == NULL) { |
| return NULL; |
| } |
| if (_PyInterpreterState_IDInitref(interp) < 0) { |
| return NULL; |
| } |
| _PyInterpreterState_IDIncref(interp); |
| |
| Py_RETURN_NONE; |
| } |
| |
| |
| static PyObject * |
| interp_decref(PyObject *self, PyObject *args, PyObject *kwds) |
| { |
| static char *kwlist[] = {"id", NULL}; |
| PyObject *id; |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, |
| "O:_incref", kwlist, &id)) { |
| return NULL; |
| } |
| |
| PyInterpreterState *interp = look_up_interp(id); |
| if (interp == NULL) { |
| return NULL; |
| } |
| _PyInterpreterState_IDDecref(interp); |
| |
| Py_RETURN_NONE; |
| } |
| |
| |
| static PyMethodDef module_functions[] = { |
| {"create", _PyCFunction_CAST(interp_create), |
| METH_VARARGS | METH_KEYWORDS, create_doc}, |
| {"destroy", _PyCFunction_CAST(interp_destroy), |
| METH_VARARGS | METH_KEYWORDS, destroy_doc}, |
| {"list_all", interp_list_all, |
| METH_NOARGS, list_all_doc}, |
| {"get_current", interp_get_current, |
| METH_NOARGS, get_current_doc}, |
| {"get_main", interp_get_main, |
| METH_NOARGS, get_main_doc}, |
| |
| {"is_running", _PyCFunction_CAST(interp_is_running), |
| METH_VARARGS | METH_KEYWORDS, is_running_doc}, |
| {"exec", _PyCFunction_CAST(interp_exec), |
| METH_VARARGS | METH_KEYWORDS, exec_doc}, |
| {"run_string", _PyCFunction_CAST(interp_run_string), |
| METH_VARARGS | METH_KEYWORDS, run_string_doc}, |
| {"run_func", _PyCFunction_CAST(interp_run_func), |
| METH_VARARGS | METH_KEYWORDS, run_func_doc}, |
| |
| {"set___main___attrs", _PyCFunction_CAST(interp_set___main___attrs), |
| METH_VARARGS, set___main___attrs_doc}, |
| {"is_shareable", _PyCFunction_CAST(object_is_shareable), |
| METH_VARARGS | METH_KEYWORDS, is_shareable_doc}, |
| |
| {"_incref", _PyCFunction_CAST(interp_incref), |
| METH_VARARGS | METH_KEYWORDS, NULL}, |
| {"_decref", _PyCFunction_CAST(interp_decref), |
| METH_VARARGS | METH_KEYWORDS, NULL}, |
| |
| {NULL, NULL} /* sentinel */ |
| }; |
| |
| |
| /* initialization function */ |
| |
| PyDoc_STRVAR(module_doc, |
| "This module provides primitive operations to manage Python interpreters.\n\ |
| The 'interpreters' module provides a more convenient interface."); |
| |
| static int |
| module_exec(PyObject *mod) |
| { |
| module_state *state = get_module_state(mod); |
| |
| // exceptions |
| if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterError) < 0) { |
| goto error; |
| } |
| if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterNotFoundError) < 0) { |
| goto error; |
| } |
| |
| if (register_memoryview_xid(mod, &state->XIBufferViewType) < 0) { |
| goto error; |
| } |
| |
| return 0; |
| |
| error: |
| return -1; |
| } |
| |
| static struct PyModuleDef_Slot module_slots[] = { |
| {Py_mod_exec, module_exec}, |
| {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, |
| {0, NULL}, |
| }; |
| |
| static int |
| module_traverse(PyObject *mod, visitproc visit, void *arg) |
| { |
| module_state *state = get_module_state(mod); |
| assert(state != NULL); |
| traverse_module_state(state, visit, arg); |
| return 0; |
| } |
| |
| static int |
| module_clear(PyObject *mod) |
| { |
| module_state *state = get_module_state(mod); |
| assert(state != NULL); |
| clear_module_state(state); |
| return 0; |
| } |
| |
| static void |
| module_free(void *mod) |
| { |
| module_state *state = get_module_state(mod); |
| assert(state != NULL); |
| clear_module_state(state); |
| } |
| |
| static struct PyModuleDef moduledef = { |
| .m_base = PyModuleDef_HEAD_INIT, |
| .m_name = MODULE_NAME, |
| .m_doc = module_doc, |
| .m_size = sizeof(module_state), |
| .m_methods = module_functions, |
| .m_slots = module_slots, |
| .m_traverse = module_traverse, |
| .m_clear = module_clear, |
| .m_free = (freefunc)module_free, |
| }; |
| |
| PyMODINIT_FUNC |
| PyInit__xxsubinterpreters(void) |
| { |
| return PyModuleDef_Init(&moduledef); |
| } |