mirror of
https://github.com/python/cpython.git
synced 2025-07-15 23:35:23 +00:00
gh-76785: Crossinterp utils additions (gh-111530)
This moves several general internal APIs out of _xxsubinterpretersmodule.c and into the new Python/crossinterp.c (and the corresponding internal headers). Specifically: * _Py_excinfo, etc.: the initial implementation for non-object exception snapshots (in pycore_pyerrors.h and Python/errors.c) * _PyXI_exception_info, etc.: helpers for passing an exception beween interpreters (wraps _Py_excinfo) * _PyXI_namespace, etc.: helpers for copying a dict of attrs between interpreters * _PyXI_Enter(), _PyXI_Exit(): functions that abstract out the transitions between one interpreter and a second that will do some work temporarily Again, these were all abstracted out of _xxsubinterpretersmodule.c as generalizations. I plan on proposing these as public API at some point.
This commit is contained in:
parent
cde1071b2a
commit
9322ce90ac
11 changed files with 1309 additions and 472 deletions
File diff suppressed because it is too large
Load diff
175
Python/errors.c
175
Python/errors.c
|
@ -1945,3 +1945,178 @@ PyErr_ProgramTextObject(PyObject *filename, int lineno)
|
|||
{
|
||||
return _PyErr_ProgramDecodedTextObject(filename, lineno, NULL);
|
||||
}
|
||||
|
||||
|
||||
/***********************/
|
||||
/* exception snapshots */
|
||||
/***********************/
|
||||
|
||||
static const char *
|
||||
_copy_raw_string(const char *str)
|
||||
{
|
||||
char *copied = PyMem_RawMalloc(strlen(str)+1);
|
||||
if (copied == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(copied, str);
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int
|
||||
_exc_type_name_as_utf8(PyObject *exc, const char **p_typename)
|
||||
{
|
||||
// XXX Use PyObject_GetAttrString(Py_TYPE(exc), '__name__')?
|
||||
PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name);
|
||||
if (nameobj == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
*p_typename = "unable to format exception type name";
|
||||
return -1;
|
||||
}
|
||||
const char *name = PyUnicode_AsUTF8(nameobj);
|
||||
if (name == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
Py_DECREF(nameobj);
|
||||
*p_typename = "unable to encode exception type name";
|
||||
return -1;
|
||||
}
|
||||
name = _copy_raw_string(name);
|
||||
Py_DECREF(nameobj);
|
||||
if (name == NULL) {
|
||||
*p_typename = "out of memory copying exception type name";
|
||||
return -1;
|
||||
}
|
||||
*p_typename = name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_exc_msg_as_utf8(PyObject *exc, const char **p_msg)
|
||||
{
|
||||
PyObject *msgobj = PyObject_Str(exc);
|
||||
if (msgobj == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
*p_msg = "unable to format exception message";
|
||||
return -1;
|
||||
}
|
||||
const char *msg = PyUnicode_AsUTF8(msgobj);
|
||||
if (msg == NULL) {
|
||||
assert(PyErr_Occurred());
|
||||
Py_DECREF(msgobj);
|
||||
*p_msg = "unable to encode exception message";
|
||||
return -1;
|
||||
}
|
||||
msg = _copy_raw_string(msg);
|
||||
Py_DECREF(msgobj);
|
||||
if (msg == NULL) {
|
||||
assert(PyErr_ExceptionMatches(PyExc_MemoryError));
|
||||
*p_msg = "out of memory copying exception message";
|
||||
return -1;
|
||||
}
|
||||
*p_msg = msg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_Py_excinfo_Clear(_Py_excinfo *info)
|
||||
{
|
||||
if (info->type != NULL) {
|
||||
PyMem_RawFree((void *)info->type);
|
||||
}
|
||||
if (info->msg != NULL) {
|
||||
PyMem_RawFree((void *)info->msg);
|
||||
}
|
||||
*info = (_Py_excinfo){ NULL };
|
||||
}
|
||||
|
||||
int
|
||||
_Py_excinfo_Copy(_Py_excinfo *dest, _Py_excinfo *src)
|
||||
{
|
||||
// XXX Clear dest first?
|
||||
|
||||
if (src->type == NULL) {
|
||||
dest->type = NULL;
|
||||
}
|
||||
else {
|
||||
dest->type = _copy_raw_string(src->type);
|
||||
if (dest->type == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (src->msg == NULL) {
|
||||
dest->msg = NULL;
|
||||
}
|
||||
else {
|
||||
dest->msg = _copy_raw_string(src->msg);
|
||||
if (dest->msg == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *
|
||||
_Py_excinfo_InitFromException(_Py_excinfo *info, PyObject *exc)
|
||||
{
|
||||
assert(exc != NULL);
|
||||
|
||||
// Extract the exception type name.
|
||||
const char *typename = NULL;
|
||||
if (_exc_type_name_as_utf8(exc, &typename) < 0) {
|
||||
assert(typename != NULL);
|
||||
return typename;
|
||||
}
|
||||
|
||||
// Extract the exception message.
|
||||
const char *msg = NULL;
|
||||
if (_exc_msg_as_utf8(exc, &msg) < 0) {
|
||||
assert(msg != NULL);
|
||||
return msg;
|
||||
}
|
||||
|
||||
info->type = typename;
|
||||
info->msg = msg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype)
|
||||
{
|
||||
if (info->type != NULL) {
|
||||
if (info->msg != NULL) {
|
||||
PyErr_Format(exctype, "%s: %s", info->type, info->msg);
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(exctype, info->type);
|
||||
}
|
||||
}
|
||||
else if (info->msg != NULL) {
|
||||
PyErr_SetString(exctype, info->msg);
|
||||
}
|
||||
else {
|
||||
PyErr_SetNone(exctype);
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
_Py_excinfo_AsUTF8(_Py_excinfo *info, char *buf, size_t bufsize)
|
||||
{
|
||||
// XXX Dynamically allocate if no buf provided?
|
||||
assert(buf != NULL);
|
||||
if (info->type != NULL) {
|
||||
if (info->msg != NULL) {
|
||||
snprintf(buf, bufsize, "%s: %s", info->type, info->msg);
|
||||
return buf;
|
||||
}
|
||||
else {
|
||||
return info->type;
|
||||
}
|
||||
}
|
||||
else if (info->msg != NULL) {
|
||||
return info->msg;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -738,6 +738,7 @@ pycore_init_types(PyInterpreterState *interp)
|
|||
if (_PyStatus_EXCEPTION(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return _PyStatus_OK();
|
||||
}
|
||||
|
||||
|
@ -854,6 +855,11 @@ pycore_interp_init(PyThreadState *tstate)
|
|||
goto done;
|
||||
}
|
||||
|
||||
status = _PyXI_Init(interp);
|
||||
if (_PyStatus_EXCEPTION(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
const PyConfig *config = _PyInterpreterState_GetConfig(interp);
|
||||
|
||||
status = _PyImport_InitCore(tstate, sysmod, config->_install_importlib);
|
||||
|
@ -1772,6 +1778,7 @@ finalize_interp_clear(PyThreadState *tstate)
|
|||
{
|
||||
int is_main_interp = _Py_IsMainInterpreter(tstate->interp);
|
||||
|
||||
_PyXI_Fini(tstate->interp);
|
||||
_PyExc_ClearExceptionGroupType(tstate->interp);
|
||||
_Py_clear_generic_types(tstate->interp);
|
||||
|
||||
|
|
|
@ -382,7 +382,7 @@ _Py_COMP_DIAG_POP
|
|||
#define LOCKS_INIT(runtime) \
|
||||
{ \
|
||||
&(runtime)->interpreters.mutex, \
|
||||
&(runtime)->xidregistry.mutex, \
|
||||
&(runtime)->xi.registry.mutex, \
|
||||
&(runtime)->getargs.mutex, \
|
||||
&(runtime)->unicode_state.ids.lock, \
|
||||
&(runtime)->imports.extensions.mutex, \
|
||||
|
@ -494,9 +494,6 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
|||
return _PyStatus_OK();
|
||||
}
|
||||
|
||||
// This is defined in crossinterp.c (for now).
|
||||
extern void _Py_xidregistry_clear(struct _xidregistry *);
|
||||
|
||||
void
|
||||
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
||||
{
|
||||
|
@ -505,8 +502,6 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
|||
assert(runtime->object_state.interpreter_leaks == 0);
|
||||
#endif
|
||||
|
||||
_Py_xidregistry_clear(&runtime->xidregistry);
|
||||
|
||||
if (gilstate_tss_initialized(runtime)) {
|
||||
gilstate_tss_fini(runtime);
|
||||
}
|
||||
|
@ -552,11 +547,6 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
|
|||
for (int i = 0; i < NUMLOCKS; i++) {
|
||||
reinit_err += _PyThread_at_fork_reinit(lockptrs[i]);
|
||||
}
|
||||
/* PyOS_AfterFork_Child(), which calls this function, later calls
|
||||
_PyInterpreterState_DeleteExceptMain(), so we only need to update
|
||||
the main interpreter here. */
|
||||
assert(runtime->interpreters.main != NULL);
|
||||
runtime->interpreters.main->xidregistry.mutex = runtime->xidregistry.mutex;
|
||||
|
||||
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||
|
||||
|
@ -720,9 +710,6 @@ init_interpreter(PyInterpreterState *interp,
|
|||
}
|
||||
interp->f_opcode_trace_set = false;
|
||||
|
||||
assert(runtime->xidregistry.mutex != NULL);
|
||||
interp->xidregistry.mutex = runtime->xidregistry.mutex;
|
||||
|
||||
interp->_initialized = 1;
|
||||
return _PyStatus_OK();
|
||||
}
|
||||
|
@ -948,10 +935,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
|||
Py_CLEAR(interp->sysdict);
|
||||
Py_CLEAR(interp->builtins);
|
||||
|
||||
_Py_xidregistry_clear(&interp->xidregistry);
|
||||
/* The lock is owned by the runtime, so we don't free it here. */
|
||||
interp->xidregistry.mutex = NULL;
|
||||
|
||||
if (tstate->interp == interp) {
|
||||
/* We are now safe to fix tstate->_status.cleared. */
|
||||
// XXX Do this (much) earlier?
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue