Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-76785: Crossinterp utils additions #111530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
67a88c2
Factor out _Py_excinfo.
ericsnowcurrently Oct 24, 2023
a9ea9ac
Extract _PyXI_errcode.
ericsnowcurrently Oct 25, 2023
33d91af
Extract _PyXI_exception_info.
ericsnowcurrently Oct 25, 2023
2424e33
Extract _PyXI_namespace.
ericsnowcurrently Oct 25, 2023
23d6959
Factor out _enter_interpreter(), _exit_interpreter(), etc.
ericsnowcurrently Oct 23, 2023
b08249f
Move enter/exit to crossinterp.c.
ericsnowcurrently Oct 27, 2023
773f5ab
Factor out _sharednsitem_set_value().
ericsnowcurrently Oct 23, 2023
cf7354e
Add _PyXI_NamespaceFromNames().
ericsnowcurrently Oct 31, 2023
6b43620
Add a default arg to _PyXI_ApplyNamespace().
ericsnowcurrently Oct 31, 2023
caef717
Allocate xid dynamically when in target interpreter.
ericsnowcurrently Oct 31, 2023
0bd42e0
Add _PyXI_FillNamespaceFromDict().
ericsnowcurrently Oct 31, 2023
a230f77
Add xid_state structs and lifecycle funcs.
ericsnowcurrently Oct 31, 2023
5675f86
Add PyExc_NotShareableError.
ericsnowcurrently Oct 31, 2023
45488f2
Propagate the ValueError when a value is not shareable.
ericsnowcurrently Oct 31, 2023
6f07364
Propagate errors in _PyXI_Enter() directly.
ericsnowcurrently Oct 31, 2023
0201b7f
Factor out _init_not_shareable_error_type() and _fini_not_shareable_e…
ericsnowcurrently Nov 1, 2023
d32a918
Drop some duplicate lines.
ericsnowcurrently Nov 1, 2023
1d4fc87
Fix a comment.
ericsnowcurrently Nov 1, 2023
88c9d54
Call _PyXI_Fini() *before* the interpreter is cleared.
ericsnowcurrently Nov 1, 2023
2edcb49
Fix init/fini.
ericsnowcurrently Nov 1, 2023
8e53752
Add _get_not_shareable_error_type().
ericsnowcurrently Nov 1, 2023
53764c1
Export fewer symbols.
ericsnowcurrently Nov 1, 2023
cacf969
Merge branch 'main' into crossinterp-utils-additions
ericsnowcurrently Nov 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Factor out _Py_excinfo.
  • Loading branch information
ericsnowcurrently committed Oct 30, 2023
commit 67a88c264257f0404aa7bb2d275b95253de1d0d5
24 changes: 24 additions & 0 deletions 24 Include/internal/pycore_pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,30 @@ extern PyStatus _PyErr_InitTypes(PyInterpreterState *);
extern void _PyErr_FiniTypes(PyInterpreterState *);


/* exception snapshots */

// Ultimately we'd like to preserve enough information about the
// exception and traceback that we could re-constitute (or at least
// simulate, a la traceback.TracebackException), and even chain, a copy
// of the exception in the calling interpreter.

typedef struct _excinfo {
const char *type;
const char *msg;
} _Py_excinfo;

PyAPI_FUNC(void) _Py_excinfo_Clear(_Py_excinfo *info);
PyAPI_FUNC(int) _Py_excinfo_Copy(_Py_excinfo *dest, _Py_excinfo *src);
PyAPI_FUNC(const char *) _Py_excinfo_InitFromException(
_Py_excinfo *info,
PyObject *exc);
PyAPI_FUNC(void) _Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype);
PyAPI_FUNC(const char *) _Py_excinfo_AsUTF8(
_Py_excinfo *info,
char *buf,
size_t bufsize);


/* other API */

static inline PyObject* _PyErr_Occurred(PyThreadState *tstate)
Expand Down
211 changes: 81 additions & 130 deletions 211 Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "Python.h"
#include "pycore_crossinterp.h" // struct _xid
#include "pycore_pyerrors.h" // _Py_excinfo
#include "pycore_initconfig.h" // _PyErr_SetFromPyStatus()
#include "pycore_modsupport.h" // _PyArg_BadArgument()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
Expand Down Expand Up @@ -113,6 +114,85 @@ clear_module_state(module_state *state)
}


/* exception info ***********************************************************/

#define ERR_NOT_SET 0
#define ERR_UNCAUGHT_EXCEPTION 1
#define ERR_NO_MEMORY 2
#define ERR_ALREADY_RUNNING 3

static const char *
_excinfo_bind(PyObject *exc, _Py_excinfo *info, int *p_code)
{
assert(exc != NULL);

const char *failure = _Py_excinfo_InitFromException(info, exc);
if (failure != NULL) {
PyErr_Clear();
*p_code = ERR_NO_MEMORY;
return failure;
}

assert(!PyErr_Occurred());
*p_code = ERR_UNCAUGHT_EXCEPTION;
return NULL;
}

typedef struct _sharedexception {
PyInterpreterState *interp;
int code;
_Py_excinfo uncaught;
} _sharedexception;

static const char *
_sharedexception_bind(PyObject *exc, int code, _sharedexception *sharedexc)
{
if (sharedexc->interp == NULL) {
sharedexc->interp = PyInterpreterState_Get();
}

const char *failure = NULL;
if (code == ERR_NOT_SET) {
failure = _excinfo_bind(exc, &sharedexc->uncaught, &sharedexc->code);
assert(sharedexc->code != ERR_NOT_SET);
}
else {
assert(exc == NULL);
assert(code != ERR_UNCAUGHT_EXCEPTION);
sharedexc->code = code;
_Py_excinfo_Clear(&sharedexc->uncaught);
}
return failure;
}

static void
_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass)
{
if (exc->code == ERR_UNCAUGHT_EXCEPTION) {
_Py_excinfo_Apply(&exc->uncaught, wrapperclass);
}
else {
assert(exc->code != ERR_NOT_SET);
if (exc->code == ERR_NO_MEMORY) {
PyErr_NoMemory();
}
else if (exc->code == ERR_ALREADY_RUNNING) {
assert(exc->interp != NULL);
assert(_PyInterpreterState_IsRunningMain(exc->interp));
_PyInterpreterState_FailIfRunningMain(exc->interp);
}
else {
#ifdef Py_DEBUG
Py_UNREACHABLE();
#else
PyErr_Format(PyExc_RuntimeError, "unsupported error code %d", code);
#endif
}
assert(PyErr_Occurred());
}
}


/* data-sharing-specific code ***********************************************/

struct _sharednsitem {
Expand Down Expand Up @@ -240,135 +320,6 @@ _sharedns_apply(_sharedns *shared, PyObject *ns)
return 0;
}

// Ultimately we'd like to preserve enough information about the
// exception and traceback that we could re-constitute (or at least
// simulate, a la traceback.TracebackException), and even chain, a copy
// of the exception in the calling interpreter.

typedef struct _sharedexception {
PyInterpreterState *interp;
#define ERR_NOT_SET 0
#define ERR_NO_MEMORY 1
#define ERR_ALREADY_RUNNING 2
int code;
const char *name;
const char *msg;
} _sharedexception;

static const struct _sharedexception no_exception = {
.name = NULL,
.msg = NULL,
};

static void
_sharedexception_clear(_sharedexception *exc)
{
if (exc->name != NULL) {
PyMem_RawFree((void *)exc->name);
}
if (exc->msg != NULL) {
PyMem_RawFree((void *)exc->msg);
}
}

static const char *
_sharedexception_bind(PyObject *exc, int code, _sharedexception *sharedexc)
{
if (sharedexc->interp == NULL) {
sharedexc->interp = PyInterpreterState_Get();
}

if (code != ERR_NOT_SET) {
assert(exc == NULL);
assert(code > 0);
sharedexc->code = code;
return NULL;
}

assert(exc != NULL);
const char *failure = NULL;

PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name);
if (nameobj == NULL) {
failure = "unable to format exception type name";
code = ERR_NO_MEMORY;
goto error;
}
sharedexc->name = _copy_raw_string(nameobj);
Py_DECREF(nameobj);
if (sharedexc->name == NULL) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
failure = "out of memory copying exception type name";
} else {
failure = "unable to encode and copy exception type name";
}
code = ERR_NO_MEMORY;
goto error;
}

if (exc != NULL) {
PyObject *msgobj = PyUnicode_FromFormat("%S", exc);
if (msgobj == NULL) {
failure = "unable to format exception message";
code = ERR_NO_MEMORY;
goto error;
}
sharedexc->msg = _copy_raw_string(msgobj);
Py_DECREF(msgobj);
if (sharedexc->msg == NULL) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
failure = "out of memory copying exception message";
} else {
failure = "unable to encode and copy exception message";
}
code = ERR_NO_MEMORY;
goto error;
}
}

return NULL;

error:
assert(failure != NULL);
PyErr_Clear();
_sharedexception_clear(sharedexc);
*sharedexc = (_sharedexception){
.interp = sharedexc->interp,
.code = code,
};
return failure;
}

static void
_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass)
{
if (exc->name != NULL) {
assert(exc->code == ERR_NOT_SET);
if (exc->msg != NULL) {
PyErr_Format(wrapperclass, "%s: %s", exc->name, exc->msg);
}
else {
PyErr_SetString(wrapperclass, exc->name);
}
}
else if (exc->msg != NULL) {
assert(exc->code == ERR_NOT_SET);
PyErr_SetString(wrapperclass, exc->msg);
}
else if (exc->code == ERR_NO_MEMORY) {
PyErr_NoMemory();
}
else if (exc->code == ERR_ALREADY_RUNNING) {
assert(exc->interp != NULL);
assert(_PyInterpreterState_IsRunningMain(exc->interp));
_PyInterpreterState_FailIfRunningMain(exc->interp);
}
else {
assert(exc->code == ERR_NOT_SET);
PyErr_SetNone(wrapperclass);
}
}


/* Python code **************************************************************/

Expand Down Expand Up @@ -549,7 +500,7 @@ _run_script(PyInterpreterState *interp,
}
_PyInterpreterState_SetNotRunningMain(interp);

*sharedexc = no_exception;
*sharedexc = (_sharedexception){0};
return 0;

error:
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.