mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1681 lines
		
	
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1681 lines
		
	
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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_code.h"          // _PyCode_HAS_EXECUTORS()
 | |
| #include "pycore_crossinterp.h"   // _PyXIData_t
 | |
| #include "pycore_pyerrors.h"      // _PyErr_GetRaisedException()
 | |
| #include "pycore_interp.h"        // _PyInterpreterState_IDIncref()
 | |
| #include "pycore_modsupport.h"    // _PyArg_BadArgument()
 | |
| #include "pycore_namespace.h"     // _PyNamespace_New()
 | |
| #include "pycore_pybuffer.h"      // _PyBuffer_ReleaseInInterpreterAndRawFree()
 | |
| #include "pycore_pylifecycle.h"   // _PyInterpreterConfig_AsDict()
 | |
| #include "pycore_pystate.h"       // _PyInterpreterState_IsRunningMain()
 | |
| 
 | |
| #include "marshal.h"              // PyMarshal_ReadObjectFromString()
 | |
| 
 | |
| #include "_interpreters_common.h"
 | |
| 
 | |
| #include "clinic/_interpretersmodule.c.h"
 | |
| 
 | |
| #define MODULE_NAME _interpreters
 | |
| #define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME)
 | |
| #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME)
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| module _interpreters
 | |
| [clinic start generated code]*/
 | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=bfd967980a0de892]*/
 | |
| 
 | |
| 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();
 | |
| }
 | |
| 
 | |
| #define look_up_interp _PyInterpreterState_LookUpIDObject
 | |
| 
 | |
| 
 | |
| static PyObject *
 | |
| _get_current_module(void)
 | |
| {
 | |
|     PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
 | |
|     if (name == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject *mod = PyImport_GetModule(name);
 | |
|     Py_DECREF(name);
 | |
|     if (mod == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     assert(mod != Py_None);
 | |
|     return mod;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| is_running_main(PyInterpreterState *interp)
 | |
| {
 | |
|     if (_PyInterpreterState_IsRunningMain(interp)) {
 | |
|         return 1;
 | |
|     }
 | |
|     // Unlike with the general C-API, we can be confident that someone
 | |
|     // using this module for the main interpreter is doing so through
 | |
|     // the main program.  Thus we can make this extra check.  This benefits
 | |
|     // applications that embed Python but haven't been updated yet
 | |
|     // to call _PyInterpreterState_SetRunningMain().
 | |
|     if (_Py_IsMainInterpreter(interp)) {
 | |
|         return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static inline int
 | |
| is_notshareable_raised(PyThreadState *tstate)
 | |
| {
 | |
|     PyObject *exctype = _PyXIData_GetNotShareableErrorType(tstate);
 | |
|     return _PyErr_ExceptionMatches(tstate, exctype);
 | |
| }
 | |
| 
 | |
| static void
 | |
| unwrap_not_shareable(PyThreadState *tstate, _PyXI_failure *failure)
 | |
| {
 | |
|     if (_PyXI_UnwrapNotShareableError(tstate, failure) < 0) {
 | |
|         _PyErr_Clear(tstate);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Cross-interpreter Buffer Views *******************************************/
 | |
| 
 | |
| /* When a memoryview object is "shared" between interpreters,
 | |
|  * its underlying "buffer" memory is actually shared, rather than just
 | |
|  * copied.  This facilitates efficient use of that data where otherwise
 | |
|  * interpreters are strictly isolated.  However, this also means that
 | |
|  * the underlying data is subject to the complexities of thread-safety,
 | |
|  * which the user must manage carefully.
 | |
|  *
 | |
|  * When the memoryview is "shared", it is essentially copied in the same
 | |
|  * way as PyMemory_FromObject() does, but in another interpreter.
 | |
|  * The Py_buffer value is copied like normal, including the "buf" pointer,
 | |
|  * with one key exception.
 | |
|  *
 | |
|  * When a Py_buffer is released and it holds a reference to an object,
 | |
|  * that object gets a chance to call its bf_releasebuffer() (if any)
 | |
|  * before the object is decref'ed.  The same is true with the memoryview
 | |
|  * tp_dealloc, which essentially calls PyBuffer_Release().
 | |
|  *
 | |
|  * The problem for a Py_buffer shared between two interpreters is that
 | |
|  * the naive approach breaks interpreter isolation.  Operations on an
 | |
|  * object must only happen while that object's interpreter is active.
 | |
|  * If the copied mv->view.obj pointed to the original memoryview then
 | |
|  * the PyBuffer_Release() would happen under the wrong interpreter.
 | |
|  *
 | |
|  * To work around this, we set mv->view.obj on the copied memoryview
 | |
|  * to a wrapper object with the only job of releasing the original
 | |
|  * buffer in a cross-interpreter-safe way.
 | |
|  */
 | |
| 
 | |
| // XXX Note that there is still an issue to sort out, where the original
 | |
| // interpreter is destroyed but code in another interpreter is still
 | |
| // using dependent buffers.  Using such buffers segfaults.  This will
 | |
| // require a careful fix.  In the meantime, users will have to be
 | |
| // diligent about avoiding the problematic situation.
 | |
| 
 | |
| typedef struct {
 | |
|     PyObject base;
 | |
|     Py_buffer *view;
 | |
|     int64_t interpid;
 | |
| } xibufferview;
 | |
| 
 | |
| static PyObject *
 | |
| xibufferview_from_buffer(PyTypeObject *cls, Py_buffer *view, int64_t interpid)
 | |
| {
 | |
|     assert(interpid >= 0);
 | |
| 
 | |
|     Py_buffer *copied = PyMem_RawMalloc(sizeof(Py_buffer));
 | |
|     if (copied == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     /* This steals the view->obj reference  */
 | |
|     *copied = *view;
 | |
| 
 | |
|     xibufferview *self = PyObject_Malloc(sizeof(xibufferview));
 | |
|     if (self == NULL) {
 | |
|         PyMem_RawFree(copied);
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject_Init(&self->base, cls);
 | |
|     *self = (xibufferview){
 | |
|         .base = self->base,
 | |
|         .view = copied,
 | |
|         .interpid = interpid,
 | |
|     };
 | |
|     return (PyObject *)self;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xibufferview_dealloc(PyObject *op)
 | |
| {
 | |
|     xibufferview *self = (xibufferview *)op;
 | |
|     if (self->view != NULL) {
 | |
|         PyInterpreterState *interp =
 | |
|                         _PyInterpreterState_LookUpID(self->interpid);
 | |
|         if (interp == NULL) {
 | |
|             /* The interpreter is no longer alive. */
 | |
|             PyErr_Clear();
 | |
|             PyMem_RawFree(self->view);
 | |
|         }
 | |
|         else {
 | |
|             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(PyObject *op, Py_buffer *view, int flags)
 | |
| {
 | |
|     /* Only PyMemoryView_FromObject() should ever call this,
 | |
|        via _memoryview_from_xid() below. */
 | |
|     xibufferview *self = (xibufferview *)op;
 | |
|     *view = *self->view;
 | |
|     /* This is the workaround mentioned earlier. */
 | |
|     view->obj = op;
 | |
|     // XXX Should we leave it alone?
 | |
|     view->internal = NULL;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static PyType_Slot XIBufferViewType_slots[] = {
 | |
|     {Py_tp_dealloc, xibufferview_dealloc},
 | |
|     {Py_bf_getbuffer, 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_STR ".CrossInterpreterBufferView",
 | |
|     .basicsize = sizeof(xibufferview),
 | |
|     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
 | |
|               Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
 | |
|     .slots = XIBufferViewType_slots,
 | |
| };
 | |
| 
 | |
| 
 | |
| static PyTypeObject * _get_current_xibufferview_type(void);
 | |
| 
 | |
| 
 | |
| struct xibuffer {
 | |
|     Py_buffer view;
 | |
|     int used;
 | |
| };
 | |
| 
 | |
| static PyObject *
 | |
| _memoryview_from_xid(_PyXIData_t *data)
 | |
| {
 | |
|     assert(_PyXIData_DATA(data) != NULL);
 | |
|     assert(_PyXIData_OBJ(data) == NULL);
 | |
|     assert(_PyXIData_INTERPID(data) >= 0);
 | |
|     struct xibuffer *view = (struct xibuffer *)_PyXIData_DATA(data);
 | |
|     assert(!view->used);
 | |
| 
 | |
|     PyTypeObject *cls = _get_current_xibufferview_type();
 | |
|     if (cls == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     PyObject *obj = xibufferview_from_buffer(
 | |
|                         cls, &view->view, _PyXIData_INTERPID(data));
 | |
|     if (obj == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject *res = PyMemoryView_FromObject(obj);
 | |
|     if (res == NULL) {
 | |
|         Py_DECREF(obj);
 | |
|         return NULL;
 | |
|     }
 | |
|     view->used = 1;
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| static void
 | |
| _pybuffer_shared_free(void* data)
 | |
| {
 | |
|     struct xibuffer *view = (struct xibuffer *)data;
 | |
|     if (!view->used) {
 | |
|         PyBuffer_Release(&view->view);
 | |
|     }
 | |
|     PyMem_RawFree(data);
 | |
| }
 | |
| 
 | |
| static int
 | |
| _pybuffer_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
 | |
| {
 | |
|     struct xibuffer *view = PyMem_RawMalloc(sizeof(struct xibuffer));
 | |
|     if (view == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
|     view->used = 0;
 | |
|     /* This will increment the memoryview's export count, which won't get
 | |
|      * decremented until the view sent to other interpreters is released. */
 | |
|     if (PyObject_GetBuffer(obj, &view->view, PyBUF_FULL_RO) < 0) {
 | |
|         PyMem_RawFree(view);
 | |
|         return -1;
 | |
|     }
 | |
|     /* The view holds a reference to the object, so we don't worry
 | |
|      * about also tracking it on the cross-interpreter data. */
 | |
|     _PyXIData_Init(data, tstate->interp, view, NULL, _memoryview_from_xid);
 | |
|     data->free = _pybuffer_shared_free;
 | |
|     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 (ensure_xid_class(&PyMemoryView_Type, GETDATA(_pybuffer_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) {
 | |
|         mod = PyImport_ImportModule(MODULE_NAME_STR);
 | |
|         if (mod == NULL) {
 | |
|             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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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)) {
 | |
|         const char *utf8name = PyUnicode_AsUTF8(configobj);
 | |
|         if (utf8name == NULL) {
 | |
|             return -1;
 | |
|         }
 | |
|         if (init_named_config(config, utf8name) < 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;
 | |
| }
 | |
| 
 | |
| 
 | |
| struct interp_call {
 | |
|     _PyXIData_t *func;
 | |
|     _PyXIData_t *args;
 | |
|     _PyXIData_t *kwargs;
 | |
|     struct {
 | |
|         _PyXIData_t func;
 | |
|         _PyXIData_t args;
 | |
|         _PyXIData_t kwargs;
 | |
|     } _preallocated;
 | |
| };
 | |
| 
 | |
| static void
 | |
| _interp_call_clear(struct interp_call *call)
 | |
| {
 | |
|     if (call->func != NULL) {
 | |
|         _PyXIData_Clear(NULL, call->func);
 | |
|     }
 | |
|     if (call->args != NULL) {
 | |
|         _PyXIData_Clear(NULL, call->args);
 | |
|     }
 | |
|     if (call->kwargs != NULL) {
 | |
|         _PyXIData_Clear(NULL, call->kwargs);
 | |
|     }
 | |
|     *call = (struct interp_call){0};
 | |
| }
 | |
| 
 | |
| static int
 | |
| _interp_call_pack(PyThreadState *tstate, struct interp_call *call,
 | |
|                   PyObject *func, PyObject *args, PyObject *kwargs)
 | |
| {
 | |
|     xidata_fallback_t fallback = _PyXIDATA_FULL_FALLBACK;
 | |
|     assert(call->func == NULL);
 | |
|     assert(call->args == NULL);
 | |
|     assert(call->kwargs == NULL);
 | |
|     // Handle the func.
 | |
|     if (!PyCallable_Check(func)) {
 | |
|         _PyErr_Format(tstate, PyExc_TypeError,
 | |
|                       "expected a callable, got %R", func);
 | |
|         return -1;
 | |
|     }
 | |
|     if (_PyFunction_GetXIData(tstate, func, &call->_preallocated.func) < 0) {
 | |
|         PyObject *exc = _PyErr_GetRaisedException(tstate);
 | |
|         if (_PyPickle_GetXIData(tstate, func, &call->_preallocated.func) < 0) {
 | |
|             _PyErr_SetRaisedException(tstate, exc);
 | |
|             return -1;
 | |
|         }
 | |
|         Py_DECREF(exc);
 | |
|     }
 | |
|     call->func = &call->_preallocated.func;
 | |
|     // Handle the args.
 | |
|     if (args == NULL || args == Py_None) {
 | |
|         // Leave it empty.
 | |
|     }
 | |
|     else {
 | |
|         assert(PyTuple_Check(args));
 | |
|         if (PyTuple_GET_SIZE(args) > 0) {
 | |
|             if (_PyObject_GetXIData(
 | |
|                     tstate, args, fallback, &call->_preallocated.args) < 0)
 | |
|             {
 | |
|                 _interp_call_clear(call);
 | |
|                 return -1;
 | |
|             }
 | |
|             call->args = &call->_preallocated.args;
 | |
|         }
 | |
|     }
 | |
|     // Handle the kwargs.
 | |
|     if (kwargs == NULL || kwargs == Py_None) {
 | |
|         // Leave it empty.
 | |
|     }
 | |
|     else {
 | |
|         assert(PyDict_Check(kwargs));
 | |
|         if (PyDict_GET_SIZE(kwargs) > 0) {
 | |
|             if (_PyObject_GetXIData(
 | |
|                     tstate, kwargs, fallback, &call->_preallocated.kwargs) < 0)
 | |
|             {
 | |
|                 _interp_call_clear(call);
 | |
|                 return -1;
 | |
|             }
 | |
|             call->kwargs = &call->_preallocated.kwargs;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wrap_notshareable(PyThreadState *tstate, const char *label)
 | |
| {
 | |
|     if (!is_notshareable_raised(tstate)) {
 | |
|         return;
 | |
|     }
 | |
|     assert(label != NULL && strlen(label) > 0);
 | |
|     PyObject *cause = _PyErr_GetRaisedException(tstate);
 | |
|     _PyXIData_FormatNotShareableError(tstate, "%s not shareable", label);
 | |
|     PyObject *exc = _PyErr_GetRaisedException(tstate);
 | |
|     PyException_SetCause(exc, cause);
 | |
|     _PyErr_SetRaisedException(tstate, exc);
 | |
| }
 | |
| 
 | |
| static int
 | |
| _interp_call_unpack(struct interp_call *call,
 | |
|                     PyObject **p_func, PyObject **p_args, PyObject **p_kwargs)
 | |
| {
 | |
|     PyThreadState *tstate = PyThreadState_Get();
 | |
| 
 | |
|     // Unpack the func.
 | |
|     PyObject *func = _PyXIData_NewObject(call->func);
 | |
|     if (func == NULL) {
 | |
|         wrap_notshareable(tstate, "func");
 | |
|         return -1;
 | |
|     }
 | |
|     // Unpack the args.
 | |
|     PyObject *args;
 | |
|     if (call->args == NULL) {
 | |
|         args = PyTuple_New(0);
 | |
|         if (args == NULL) {
 | |
|             Py_DECREF(func);
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         args = _PyXIData_NewObject(call->args);
 | |
|         if (args == NULL) {
 | |
|             wrap_notshareable(tstate, "args");
 | |
|             Py_DECREF(func);
 | |
|             return -1;
 | |
|         }
 | |
|         assert(PyTuple_Check(args));
 | |
|     }
 | |
|     // Unpack the kwargs.
 | |
|     PyObject *kwargs = NULL;
 | |
|     if (call->kwargs != NULL) {
 | |
|         kwargs = _PyXIData_NewObject(call->kwargs);
 | |
|         if (kwargs == NULL) {
 | |
|             wrap_notshareable(tstate, "kwargs");
 | |
|             Py_DECREF(func);
 | |
|             Py_DECREF(args);
 | |
|             return -1;
 | |
|         }
 | |
|         assert(PyDict_Check(kwargs));
 | |
|     }
 | |
|     *p_func = func;
 | |
|     *p_args = args;
 | |
|     *p_kwargs = kwargs;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _make_call(struct interp_call *call,
 | |
|            PyObject **p_result, _PyXI_failure *failure)
 | |
| {
 | |
|     assert(call != NULL && call->func != NULL);
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
| 
 | |
|     // Get the func and args.
 | |
|     PyObject *func = NULL, *args = NULL, *kwargs = NULL;
 | |
|     if (_interp_call_unpack(call, &func, &args, &kwargs) < 0) {
 | |
|         assert(func == NULL);
 | |
|         assert(args == NULL);
 | |
|         assert(kwargs == NULL);
 | |
|         _PyXI_InitFailure(failure, _PyXI_ERR_OTHER, NULL);
 | |
|         unwrap_not_shareable(tstate, failure);
 | |
|         return -1;
 | |
|     }
 | |
|     assert(!_PyErr_Occurred(tstate));
 | |
| 
 | |
|     // Make the call.
 | |
|     PyObject *resobj = PyObject_Call(func, args, kwargs);
 | |
|     Py_DECREF(func);
 | |
|     Py_XDECREF(args);
 | |
|     Py_XDECREF(kwargs);
 | |
|     if (resobj == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
|     *p_result = resobj;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _run_script(_PyXIData_t *script, PyObject *ns, _PyXI_failure *failure)
 | |
| {
 | |
|     PyObject *code = _PyXIData_NewObject(script);
 | |
|     if (code == NULL) {
 | |
|         _PyXI_InitFailure(failure, _PyXI_ERR_NOT_SHAREABLE, NULL);
 | |
|         return -1;
 | |
|     }
 | |
|     PyObject *result = PyEval_EvalCode(code, ns, ns);
 | |
|     Py_DECREF(code);
 | |
|     if (result == NULL) {
 | |
|         _PyXI_InitFailure(failure, _PyXI_ERR_UNCAUGHT_EXCEPTION, NULL);
 | |
|         return -1;
 | |
|     }
 | |
|     assert(result == Py_None);
 | |
|     Py_DECREF(result);  // We throw away the result.
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| struct run_result {
 | |
|     PyObject *result;
 | |
|     PyObject *excinfo;
 | |
| };
 | |
| 
 | |
| static void
 | |
| _run_result_clear(struct run_result *runres)
 | |
| {
 | |
|     Py_CLEAR(runres->result);
 | |
|     Py_CLEAR(runres->excinfo);
 | |
| }
 | |
| 
 | |
| static int
 | |
| _run_in_interpreter(PyThreadState *tstate, PyInterpreterState *interp,
 | |
|                      _PyXIData_t *script, struct interp_call *call,
 | |
|                      PyObject *shareables, struct run_result *runres)
 | |
| {
 | |
|     assert(!_PyErr_Occurred(tstate));
 | |
|     int res = -1;
 | |
|     _PyXI_failure *failure = _PyXI_NewFailure();
 | |
|     if (failure == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
|     _PyXI_session *session = _PyXI_NewSession();
 | |
|     if (session == NULL) {
 | |
|         _PyXI_FreeFailure(failure);
 | |
|         return -1;
 | |
|     }
 | |
|     _PyXI_session_result result = {0};
 | |
| 
 | |
|     // Prep and switch interpreters.
 | |
|     if (_PyXI_Enter(session, interp, shareables, &result) < 0) {
 | |
|         // If an error occurred at this step, it means that interp
 | |
|         // was not prepared and switched.
 | |
|         _PyXI_FreeSession(session);
 | |
|         _PyXI_FreeFailure(failure);
 | |
|         assert(result.excinfo == NULL);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     // Run in the interpreter.
 | |
|     if (script != NULL) {
 | |
|         assert(call == NULL);
 | |
|         PyObject *mainns = _PyXI_GetMainNamespace(session, failure);
 | |
|         if (mainns == NULL) {
 | |
|             goto finally;
 | |
|         }
 | |
|         res = _run_script(script, mainns, failure);
 | |
|     }
 | |
|     else {
 | |
|         assert(call != NULL);
 | |
|         PyObject *resobj;
 | |
|         res = _make_call(call, &resobj, failure);
 | |
|         if (res == 0) {
 | |
|             res = _PyXI_Preserve(session, "resobj", resobj, failure);
 | |
|             Py_DECREF(resobj);
 | |
|             if (res < 0) {
 | |
|                 goto finally;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| finally:
 | |
|     // Clean up and switch back.
 | |
|     (void)res;
 | |
|     int exitres = _PyXI_Exit(session, failure, &result);
 | |
|     assert(res == 0 || exitres != 0);
 | |
|     _PyXI_FreeSession(session);
 | |
|     _PyXI_FreeFailure(failure);
 | |
| 
 | |
|     res = exitres;
 | |
|     if (_PyErr_Occurred(tstate)) {
 | |
|         // It's a directly propagated exception.
 | |
|         assert(res < 0);
 | |
|     }
 | |
|     else if (res < 0) {
 | |
|         assert(result.excinfo != NULL);
 | |
|         runres->excinfo = Py_NewRef(result.excinfo);
 | |
|         res = -1;
 | |
|     }
 | |
|     else {
 | |
|         assert(result.excinfo == NULL);
 | |
|         runres->result = _PyXI_GetPreserved(&result, "resobj");
 | |
|         if (_PyErr_Occurred(tstate)) {
 | |
|             res = -1;
 | |
|         }
 | |
|     }
 | |
|     _PyXI_ClearResult(&result);
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* module level code ********************************************************/
 | |
| 
 | |
| static long
 | |
| get_whence(PyInterpreterState *interp)
 | |
| {
 | |
|     return _PyInterpreterState_GetWhence(interp);
 | |
| }
 | |
| 
 | |
| 
 | |
| static PyInterpreterState *
 | |
| resolve_interp(PyObject *idobj, int restricted, int reqready, const char *op)
 | |
| {
 | |
|     PyInterpreterState *interp;
 | |
|     if (idobj == NULL) {
 | |
|         interp = PyInterpreterState_Get();
 | |
|     }
 | |
|     else {
 | |
|         interp = look_up_interp(idobj);
 | |
|         if (interp == NULL) {
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (reqready && !_PyInterpreterState_IsReady(interp)) {
 | |
|         if (idobj == NULL) {
 | |
|             PyErr_Format(PyExc_InterpreterError,
 | |
|                          "cannot %s current interpreter (not ready)", op);
 | |
|         }
 | |
|         else {
 | |
|             PyErr_Format(PyExc_InterpreterError,
 | |
|                          "cannot %s interpreter %R (not ready)", op, idobj);
 | |
|         }
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (restricted && get_whence(interp) != _PyInterpreterState_WHENCE_STDLIB) {
 | |
|         if (idobj == NULL) {
 | |
|             PyErr_Format(PyExc_InterpreterError,
 | |
|                          "cannot %s unrecognized current interpreter", op);
 | |
|         }
 | |
|         else {
 | |
|             PyErr_Format(PyExc_InterpreterError,
 | |
|                          "cannot %s unrecognized interpreter %R", op, idobj);
 | |
|         }
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return interp;
 | |
| }
 | |
| 
 | |
| 
 | |
| static PyObject *
 | |
| get_summary(PyInterpreterState *interp)
 | |
| {
 | |
|     PyObject *idobj = _PyInterpreterState_GetIDObject(interp);
 | |
|     if (idobj == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject *whenceobj = PyLong_FromLong(
 | |
|                             get_whence(interp));
 | |
|     if (whenceobj == NULL) {
 | |
|         Py_DECREF(idobj);
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject *res = PyTuple_Pack(2, idobj, whenceobj);
 | |
|     Py_DECREF(idobj);
 | |
|     Py_DECREF(whenceobj);
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| // Not converted to Argument Clinic because the function uses ``**kwargs``.
 | |
| static PyObject *
 | |
| 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;
 | |
| 
 | |
|     PyInterpreterConfig config;
 | |
|     if (init_named_config(&config, name) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (overrides != NULL && PyDict_GET_SIZE(overrides) > 0) {
 | |
|         if (_PyInterpreterConfig_UpdateFromDict(&config, overrides) < 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(new_config_doc,
 | |
| "new_config($module, name='isolated', /, **overrides)\n\
 | |
| --\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.");
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.create
 | |
|     config as configobj: object(py_default="'isolated'") = NULL
 | |
|     *
 | |
|     reqrefs: bool = False
 | |
| 
 | |
| Create a new interpreter and return a unique generated ID.
 | |
| 
 | |
| The caller is responsible for destroying the interpreter before exiting,
 | |
| typically by using _interpreters.destroy().  This can be managed
 | |
| automatically by passing "reqrefs=True" and then using _incref() and
 | |
| _decref() appropriately.
 | |
| 
 | |
| "config" must be a valid interpreter config or the name of a
 | |
| predefined config ('isolated' or 'legacy').  The default
 | |
| is 'isolated'.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_create_impl(PyObject *module, PyObject *configobj, int reqrefs)
 | |
| /*[clinic end generated code: output=c1cc6835b1277c16 input=235ce396a23624d5]*/
 | |
| {
 | |
|     PyInterpreterConfig config;
 | |
|     if (config_from_object(configobj, &config) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     long whence = _PyInterpreterState_WHENCE_STDLIB;
 | |
|     PyInterpreterState *interp = \
 | |
|             _PyXI_NewInterpreter(&config, &whence, NULL, NULL);
 | |
|     if (interp == NULL) {
 | |
|         // XXX Move the chained exception to interpreters.create()?
 | |
|         PyObject *exc = PyErr_GetRaisedException();
 | |
|         assert(exc != NULL);
 | |
|         PyErr_SetString(PyExc_InterpreterError, "interpreter creation failed");
 | |
|         _PyErr_ChainExceptions1(exc);
 | |
|         return NULL;
 | |
|     }
 | |
|     assert(_PyInterpreterState_IsReady(interp));
 | |
| 
 | |
|     PyObject *idobj = _PyInterpreterState_GetIDObject(interp);
 | |
|     if (idobj == NULL) {
 | |
|         _PyXI_EndInterpreter(interp, NULL, NULL);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (reqrefs) {
 | |
|         // Decref to 0 will destroy the interpreter.
 | |
|         _PyInterpreterState_RequireIDRef(interp, 1);
 | |
|     }
 | |
| 
 | |
|     return idobj;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.destroy
 | |
|     id: object
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Destroy the identified interpreter.
 | |
| 
 | |
| Attempting to destroy the current interpreter raises InterpreterError.
 | |
| So does an unrecognized ID.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_destroy_impl(PyObject *module, PyObject *id, int restricted)
 | |
| /*[clinic end generated code: output=0bc20da8700ab4dd input=561bdd6537639d40]*/
 | |
| {
 | |
|     // Look up the interpreter.
 | |
|     int reqready = 0;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "destroy");
 | |
|     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_InterpreterError,
 | |
|                         "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 (is_running_main(interp)) {
 | |
|         PyErr_Format(PyExc_InterpreterError, "interpreter running");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Destroy the interpreter.
 | |
|     _PyXI_EndInterpreter(interp, NULL, NULL);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.list_all
 | |
|     *
 | |
|     require_ready as reqready: bool = False
 | |
| 
 | |
| Return a list containing the ID of every existing interpreter.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_list_all_impl(PyObject *module, int reqready)
 | |
| /*[clinic end generated code: output=3f21c1a7c78043c0 input=35bae91c381a2cf9]*/
 | |
| {
 | |
|     PyObject *ids = PyList_New(0);
 | |
|     if (ids == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     PyInterpreterState *interp = PyInterpreterState_Head();
 | |
|     while (interp != NULL) {
 | |
|         if (!reqready || _PyInterpreterState_IsReady(interp)) {
 | |
|             PyObject *item = get_summary(interp);
 | |
|             if (item == NULL) {
 | |
|                 Py_DECREF(ids);
 | |
|                 return NULL;
 | |
|             }
 | |
| 
 | |
|             // insert at front of list
 | |
|             int res = PyList_Insert(ids, 0, item);
 | |
|             Py_DECREF(item);
 | |
|             if (res < 0) {
 | |
|                 Py_DECREF(ids);
 | |
|                 return NULL;
 | |
|             }
 | |
|         }
 | |
|         interp = PyInterpreterState_Next(interp);
 | |
|     }
 | |
| 
 | |
|     return ids;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.get_current
 | |
| 
 | |
| Return (ID, whence) of the current interpreter.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_get_current_impl(PyObject *module)
 | |
| /*[clinic end generated code: output=03161c8fcc0136eb input=37fb2c067c14d543]*/
 | |
| {
 | |
|     PyInterpreterState *interp =_get_current_interp();
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     assert(_PyInterpreterState_IsReady(interp));
 | |
|     return get_summary(interp);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.get_main
 | |
| 
 | |
| Return (ID, whence) of the main interpreter.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_get_main_impl(PyObject *module)
 | |
| /*[clinic end generated code: output=9647288aff735557 input=b4ace23ca562146f]*/
 | |
| {
 | |
|     PyInterpreterState *interp = _PyInterpreterState_Main();
 | |
|     assert(_PyInterpreterState_IsReady(interp));
 | |
|     return get_summary(interp);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.set___main___attrs
 | |
|     id: object
 | |
|     updates: object(subclass_of='&PyDict_Type')
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Bind the given attributes in the interpreter's __main__ module.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_set___main___attrs_impl(PyObject *module, PyObject *id,
 | |
|                                       PyObject *updates, int restricted)
 | |
| /*[clinic end generated code: output=f3803010cb452bf0 input=d16ab8d81371f86a]*/
 | |
| {
 | |
|     // Look up the interpreter.
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "update __main__ for");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Check the updates.
 | |
|     Py_ssize_t size = PyDict_Size(updates);
 | |
|     if (size < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
|     if (size == 0) {
 | |
|         PyErr_SetString(PyExc_ValueError,
 | |
|                         "arg 2 must be a non-empty dict");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     _PyXI_session *session = _PyXI_NewSession();
 | |
|     if (session == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Prep and switch interpreters, including apply the updates.
 | |
|     if (_PyXI_Enter(session, interp, updates, NULL) < 0) {
 | |
|         _PyXI_FreeSession(session);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // Clean up and switch back.
 | |
|     assert(!PyErr_Occurred());
 | |
|     int res = _PyXI_Exit(session, NULL, NULL);
 | |
|     _PyXI_FreeSession(session);
 | |
|     assert(res == 0);
 | |
|     if (res < 0) {
 | |
|         // unreachable
 | |
|         if (!PyErr_Occurred()) {
 | |
|             PyErr_SetString(PyExc_RuntimeError, "unresolved error");
 | |
|         }
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| static PyObject *
 | |
| _handle_script_error(struct run_result *runres)
 | |
| {
 | |
|     assert(runres->result == NULL);
 | |
|     if (runres->excinfo == NULL) {
 | |
|         assert(PyErr_Occurred());
 | |
|         return NULL;
 | |
|     }
 | |
|     assert(!PyErr_Occurred());
 | |
|     return runres->excinfo;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.exec
 | |
|     id: object
 | |
|     code: object
 | |
|     shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {}
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Execute the provided code in the identified interpreter.
 | |
| 
 | |
| This is equivalent to running the builtin exec() under the target
 | |
| interpreter, using the __dict__ of its __main__ module as both
 | |
| globals and locals.
 | |
| 
 | |
| "code" may be a string containing the text of a Python script.
 | |
| 
 | |
| Functions (and code objects) are also supported, with some restrictions.
 | |
| The code/function must not take any arguments or be a closure
 | |
| (i.e. have cell vars).  Methods and other callables are not supported.
 | |
| 
 | |
| If a function is provided, its code object is used and all its state
 | |
| is ignored, including its __globals__ dict.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_exec_impl(PyObject *module, PyObject *id, PyObject *code,
 | |
|                         PyObject *shared, int restricted)
 | |
| /*[clinic end generated code: output=492057c4f10dc304 input=5a22c1ed0c5dbcf3]*/
 | |
| {
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "exec code for");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // We don't need the script to be "pure", which means it can use
 | |
|     // global variables.  They will be resolved against __main__.
 | |
|     _PyXIData_t xidata = {0};
 | |
|     if (_PyCode_GetScriptXIData(tstate, code, &xidata) < 0) {
 | |
|         unwrap_not_shareable(tstate, NULL);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     struct run_result runres = {0};
 | |
|     int res = _run_in_interpreter(
 | |
|                     tstate, interp, &xidata, NULL, shared, &runres);
 | |
|     _PyXIData_Release(&xidata);
 | |
|     if (res < 0) {
 | |
|         return _handle_script_error(&runres);
 | |
|     }
 | |
|     assert(runres.result == NULL);
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.run_string
 | |
|     id: object
 | |
|     script: unicode
 | |
|     shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {}
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Execute the provided string in the identified interpreter.
 | |
| 
 | |
| (See _interpreters.exec().)
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_run_string_impl(PyObject *module, PyObject *id,
 | |
|                               PyObject *script, PyObject *shared,
 | |
|                               int restricted)
 | |
| /*[clinic end generated code: output=a30a64fb9ad396a2 input=51ce549b9a8dbe21]*/
 | |
| {
 | |
| #define FUNCNAME MODULE_NAME_STR ".run_string"
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "run a string in");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (PyFunction_Check(script) || PyCode_Check(script)) {
 | |
|         _PyArg_BadArgument(FUNCNAME, "argument 2", "a string", script);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     _PyXIData_t xidata = {0};
 | |
|     if (_PyCode_GetScriptXIData(tstate, script, &xidata) < 0) {
 | |
|         unwrap_not_shareable(tstate, NULL);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     struct run_result runres = {0};
 | |
|     int res = _run_in_interpreter(
 | |
|                     tstate, interp, &xidata, NULL, shared, &runres);
 | |
|     _PyXIData_Release(&xidata);
 | |
|     if (res < 0) {
 | |
|         return _handle_script_error(&runres);
 | |
|     }
 | |
|     assert(runres.result == NULL);
 | |
|     Py_RETURN_NONE;
 | |
| #undef FUNCNAME
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.run_func
 | |
|     id: object
 | |
|     func: object
 | |
|     shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {}
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Execute the body of the provided function in the identified interpreter.
 | |
| 
 | |
| Code objects are also supported.  In both cases, closures and args
 | |
| are not supported.  Methods and other callables are not supported either.
 | |
| 
 | |
| (See _interpreters.exec().)
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_run_func_impl(PyObject *module, PyObject *id, PyObject *func,
 | |
|                             PyObject *shared, int restricted)
 | |
| /*[clinic end generated code: output=131f7202ca4a0c5e input=2d62bb9b9eaf4948]*/
 | |
| {
 | |
| #define FUNCNAME MODULE_NAME_STR ".run_func"
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "run a function in");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // We don't worry about checking globals.  They will be resolved
 | |
|     // against __main__.
 | |
|     PyObject *code;
 | |
|     if (PyFunction_Check(func)) {
 | |
|         code = PyFunction_GET_CODE(func);
 | |
|     }
 | |
|     else if (PyCode_Check(func)) {
 | |
|         code = func;
 | |
|     }
 | |
|     else {
 | |
|         _PyArg_BadArgument(FUNCNAME, "argument 2", "a function", func);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     _PyXIData_t xidata = {0};
 | |
|     if (_PyCode_GetScriptXIData(tstate, code, &xidata) < 0) {
 | |
|         unwrap_not_shareable(tstate, NULL);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     struct run_result runres = {0};
 | |
|     int res = _run_in_interpreter(
 | |
|                     tstate, interp, &xidata, NULL, shared, &runres);
 | |
|     _PyXIData_Release(&xidata);
 | |
|     if (res < 0) {
 | |
|         return _handle_script_error(&runres);
 | |
|     }
 | |
|     assert(runres.result == NULL);
 | |
|     Py_RETURN_NONE;
 | |
| #undef FUNCNAME
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.call
 | |
|     id: object
 | |
|     callable: object
 | |
|     args: object(subclass_of='&PyTuple_Type', c_default='NULL') = ()
 | |
|     kwargs: object(subclass_of='&PyDict_Type', c_default='NULL') = {}
 | |
|     *
 | |
|     preserve_exc: bool = False
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Call the provided object in the identified interpreter.
 | |
| 
 | |
| Pass the given args and kwargs, if possible.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_call_impl(PyObject *module, PyObject *id, PyObject *callable,
 | |
|                         PyObject *args, PyObject *kwargs, int preserve_exc,
 | |
|                         int restricted)
 | |
| /*[clinic end generated code: output=b7a4a27d72df3ebc input=b026d0b212a575e6]*/
 | |
| {
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "make a call in");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     struct interp_call call = {0};
 | |
|     if (_interp_call_pack(tstate, &call, callable, args, kwargs) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     PyObject *res_and_exc = NULL;
 | |
|     struct run_result runres = {0};
 | |
|     if (_run_in_interpreter(tstate, interp, NULL, &call, NULL, &runres) < 0) {
 | |
|         if (runres.excinfo == NULL) {
 | |
|             assert(_PyErr_Occurred(tstate));
 | |
|             goto finally;
 | |
|         }
 | |
|         assert(!_PyErr_Occurred(tstate));
 | |
|     }
 | |
|     assert(runres.result == NULL || runres.excinfo == NULL);
 | |
|     res_and_exc = Py_BuildValue("OO",
 | |
|                                 (runres.result ? runres.result : Py_None),
 | |
|                                 (runres.excinfo ? runres.excinfo : Py_None));
 | |
| 
 | |
| finally:
 | |
|     _interp_call_clear(&call);
 | |
|     _run_result_clear(&runres);
 | |
|     return res_and_exc;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.is_shareable
 | |
|     obj: object
 | |
| 
 | |
| Return True if the object's data may be shared between interpreters and False otherwise.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_is_shareable_impl(PyObject *module, PyObject *obj)
 | |
| /*[clinic end generated code: output=227856926a22940b input=72b9a36bdf1d2a53]*/
 | |
| {
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
|     if (_PyObject_CheckXIData(tstate, obj) == 0) {
 | |
|         Py_RETURN_TRUE;
 | |
|     }
 | |
|     PyErr_Clear();
 | |
|     Py_RETURN_FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.is_running
 | |
|     id: object
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Return whether or not the identified interpreter is running.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_is_running_impl(PyObject *module, PyObject *id, int restricted)
 | |
| /*[clinic end generated code: output=32a6225d5ded9bdb input=3291578d04231125]*/
 | |
| {
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "check if running for");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (is_running_main(interp)) {
 | |
|         Py_RETURN_TRUE;
 | |
|     }
 | |
|     Py_RETURN_FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.get_config
 | |
|     id: object
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| Return a representation of the config used to initialize the interpreter.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_get_config_impl(PyObject *module, PyObject *id, int restricted)
 | |
| /*[clinic end generated code: output=56773353b9b7224a input=59519a01c22d96d1]*/
 | |
| {
 | |
|     if (id == Py_None) {
 | |
|         id = NULL;
 | |
|     }
 | |
| 
 | |
|     int reqready = 0;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "get the config of");
 | |
|     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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.whence
 | |
|     id: object
 | |
| 
 | |
| Return an identifier for where the interpreter was created.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_whence_impl(PyObject *module, PyObject *id)
 | |
| /*[clinic end generated code: output=ef2c21ab106c2c20 input=eeede0a2fbfa2968]*/
 | |
| {
 | |
|     PyInterpreterState *interp = look_up_interp(id);
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     long whence = get_whence(interp);
 | |
|     return PyLong_FromLong(whence);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.incref
 | |
|     id: object
 | |
|     *
 | |
|     implieslink: bool = False
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_incref_impl(PyObject *module, PyObject *id, int implieslink,
 | |
|                           int restricted)
 | |
| /*[clinic end generated code: output=eccaa4e03fbe8ee2 input=a0a614748f2e348c]*/
 | |
| {
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "incref");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (implieslink) {
 | |
|         // Decref to 0 will destroy the interpreter.
 | |
|         _PyInterpreterState_RequireIDRef(interp, 1);
 | |
|     }
 | |
|     _PyInterpreterState_IDIncref(interp);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.decref
 | |
|     id: object
 | |
|     *
 | |
|     restrict as restricted: bool = False
 | |
| 
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_decref_impl(PyObject *module, PyObject *id, int restricted)
 | |
| /*[clinic end generated code: output=5c54db4b22086171 input=c4aa34f09c44e62a]*/
 | |
| {
 | |
|     int reqready = 1;
 | |
|     PyInterpreterState *interp = \
 | |
|             resolve_interp(id, restricted, reqready, "decref");
 | |
|     if (interp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     _PyInterpreterState_IDDecref(interp);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*[clinic input]
 | |
| _interpreters.capture_exception
 | |
|     exc as exc_arg: object = None
 | |
| 
 | |
| Return a snapshot of an exception.
 | |
| 
 | |
| If "exc" is None then the current exception, if any, is used (but not cleared).
 | |
| The returned snapshot is the same as what _interpreters.exec() returns.
 | |
| [clinic start generated code]*/
 | |
| 
 | |
| static PyObject *
 | |
| _interpreters_capture_exception_impl(PyObject *module, PyObject *exc_arg)
 | |
| /*[clinic end generated code: output=ef3f5393ef9c88a6 input=32045341e979bc9e]*/
 | |
| {
 | |
|     PyObject *exc = exc_arg;
 | |
|     if (exc == NULL || exc == Py_None) {
 | |
|         exc = PyErr_GetRaisedException();
 | |
|         if (exc == NULL) {
 | |
|             Py_RETURN_NONE;
 | |
|         }
 | |
|     }
 | |
|     else if (!PyExceptionInstance_Check(exc)) {
 | |
|         PyErr_Format(PyExc_TypeError, "expected exception, got %R", exc);
 | |
|         return NULL;
 | |
|     }
 | |
|     PyObject *captured = NULL;
 | |
| 
 | |
|     _PyXI_excinfo *info = _PyXI_NewExcInfo(exc);
 | |
|     if (info == NULL) {
 | |
|         goto finally;
 | |
|     }
 | |
|     captured = _PyXI_ExcInfoAsObject(info);
 | |
|     if (captured == NULL) {
 | |
|         goto finally;
 | |
|     }
 | |
| 
 | |
|     PyObject *formatted = _PyXI_FormatExcInfo(info);
 | |
|     if (formatted == NULL) {
 | |
|         Py_CLEAR(captured);
 | |
|         goto finally;
 | |
|     }
 | |
|     int res = PyObject_SetAttrString(captured, "formatted", formatted);
 | |
|     Py_DECREF(formatted);
 | |
|     if (res < 0) {
 | |
|         Py_CLEAR(captured);
 | |
|         goto finally;
 | |
|     }
 | |
| 
 | |
| finally:
 | |
|     _PyXI_FreeExcInfo(info);
 | |
|     if (exc != exc_arg) {
 | |
|         if (PyErr_Occurred()) {
 | |
|             PyErr_SetRaisedException(exc);
 | |
|         }
 | |
|         else {
 | |
|             _PyErr_ChainExceptions1(exc);
 | |
|         }
 | |
|     }
 | |
|     return captured;
 | |
| }
 | |
| 
 | |
| 
 | |
| static PyMethodDef module_functions[] = {
 | |
|     {"new_config",                _PyCFunction_CAST(interp_new_config),
 | |
|      METH_VARARGS | METH_KEYWORDS, new_config_doc},
 | |
| 
 | |
|     _INTERPRETERS_CREATE_METHODDEF
 | |
|     _INTERPRETERS_DESTROY_METHODDEF
 | |
|     _INTERPRETERS_LIST_ALL_METHODDEF
 | |
|     _INTERPRETERS_GET_CURRENT_METHODDEF
 | |
|     _INTERPRETERS_GET_MAIN_METHODDEF
 | |
| 
 | |
|     _INTERPRETERS_IS_RUNNING_METHODDEF
 | |
|     _INTERPRETERS_GET_CONFIG_METHODDEF
 | |
|     _INTERPRETERS_WHENCE_METHODDEF
 | |
|     _INTERPRETERS_EXEC_METHODDEF
 | |
|     _INTERPRETERS_CALL_METHODDEF
 | |
|     _INTERPRETERS_RUN_STRING_METHODDEF
 | |
|     _INTERPRETERS_RUN_FUNC_METHODDEF
 | |
| 
 | |
|     _INTERPRETERS_SET___MAIN___ATTRS_METHODDEF
 | |
| 
 | |
|     _INTERPRETERS_INCREF_METHODDEF
 | |
|     _INTERPRETERS_DECREF_METHODDEF
 | |
| 
 | |
|     _INTERPRETERS_IS_SHAREABLE_METHODDEF
 | |
| 
 | |
|     _INTERPRETERS_CAPTURE_EXCEPTION_METHODDEF
 | |
| 
 | |
|     {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)
 | |
| {
 | |
|     PyThreadState *tstate = _PyThreadState_GET();
 | |
|     module_state *state = get_module_state(mod);
 | |
| 
 | |
| #define ADD_WHENCE(NAME) \
 | |
|     if (PyModule_AddIntConstant(mod, "WHENCE_" #NAME,                   \
 | |
|                                 _PyInterpreterState_WHENCE_##NAME) < 0) \
 | |
|     {                                                                   \
 | |
|         goto error;                                                     \
 | |
|     }
 | |
|     ADD_WHENCE(UNKNOWN)
 | |
|     ADD_WHENCE(RUNTIME)
 | |
|     ADD_WHENCE(LEGACY_CAPI)
 | |
|     ADD_WHENCE(CAPI)
 | |
|     ADD_WHENCE(XI)
 | |
|     ADD_WHENCE(STDLIB)
 | |
| #undef ADD_WHENCE
 | |
| 
 | |
|     // exceptions
 | |
|     if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterError) < 0) {
 | |
|         goto error;
 | |
|     }
 | |
|     if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterNotFoundError) < 0) {
 | |
|         goto error;
 | |
|     }
 | |
|     PyObject *exctype = _PyXIData_GetNotShareableErrorType(tstate);
 | |
|     if (PyModule_AddType(mod, (PyTypeObject *)exctype) < 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},
 | |
|     {Py_mod_gil, Py_MOD_GIL_NOT_USED},
 | |
|     {0, NULL},
 | |
| };
 | |
| 
 | |
| static int
 | |
| module_traverse(PyObject *mod, visitproc visit, void *arg)
 | |
| {
 | |
|     module_state *state = get_module_state(mod);
 | |
|     assert(state != NULL);
 | |
|     return traverse_module_state(state, visit, arg);
 | |
| }
 | |
| 
 | |
| static int
 | |
| module_clear(PyObject *mod)
 | |
| {
 | |
|     module_state *state = get_module_state(mod);
 | |
|     assert(state != NULL);
 | |
|     return clear_module_state(state);
 | |
| }
 | |
| 
 | |
| static void
 | |
| module_free(void *mod)
 | |
| {
 | |
|     module_state *state = get_module_state((PyObject *)mod);
 | |
|     assert(state != NULL);
 | |
|     (void)clear_module_state(state);
 | |
| }
 | |
| 
 | |
| static struct PyModuleDef moduledef = {
 | |
|     .m_base = PyModuleDef_HEAD_INIT,
 | |
|     .m_name = MODULE_NAME_STR,
 | |
|     .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 = module_free,
 | |
| };
 | |
| 
 | |
| PyMODINIT_FUNC
 | |
| MODINIT_FUNC_NAME(void)
 | |
| {
 | |
|     return PyModuleDef_Init(&moduledef);
 | |
| }
 | 
