Skip to content
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

gh-76785: Add an Internal ExceptionSnapshot Type #111572

14 changes: 3 additions & 11 deletions Include/internal/pycore_crossinterp.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,6 @@ extern void _PyXI_Fini(PyInterpreterState *interp);
/* short-term data sharing */
/***************************/

// 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;


typedef enum error_code {
_PyXI_ERR_NO_ERROR = 0,
_PyXI_ERR_UNCAUGHT_EXCEPTION = -1,
Expand Down Expand Up @@ -269,6 +258,9 @@ PyAPI_FUNC(void) _PyXI_Exit(_PyXI_session *session);
PyAPI_FUNC(void) _PyXI_ApplyCapturedException(
_PyXI_session *session,
PyObject *excwrapper);
PyAPI_FUNC(PyObject *) _PyXI_ResolveCapturedException(
_PyXI_session *session,
PyObject *excwrapper);
PyAPI_FUNC(int) _PyXI_HasCapturedException(_PyXI_session *session);


Expand Down
16 changes: 14 additions & 2 deletions Include/internal/pycore_exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif

#include "pycore_pyerrors.h"


/* runtime lifecycle */

extern PyStatus _PyExc_InitState(PyInterpreterState *);
extern PyStatus _PyExc_InitGlobalObjects(PyInterpreterState *);
extern int _PyExc_InitTypes(PyInterpreterState *);
extern void _PyExc_FiniHeapObjects(PyInterpreterState *);
extern void _PyExc_FiniTypes(PyInterpreterState *);
extern void _PyExc_Fini(PyInterpreterState *);


/* other API */
/* runtime state */

struct _Py_exc_state {
// The dict mapping from errno codes to OSError subclasses
Expand All @@ -26,9 +30,17 @@ struct _Py_exc_state {
int memerrors_numfree;
// The ExceptionGroup type
PyObject *PyExc_ExceptionGroup;

PyTypeObject *ExceptionSnapshotType;
};

extern void _PyExc_ClearExceptionGroupType(PyInterpreterState *);

/* other API */

PyAPI_FUNC(PyTypeObject *) _PyExc_GetExceptionSnapshotType(
PyInterpreterState *interp);

extern PyObject * PyExceptionSnapshot_FromInfo(_Py_excinfo *info);


#ifdef __cplusplus
Expand Down
24 changes: 24 additions & 0 deletions 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;

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


/* other API */

static inline PyObject* _PyErr_Occurred(PyThreadState *tstate)
Expand Down
23 changes: 22 additions & 1 deletion Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#include "Python.h"
#include "pycore_crossinterp.h" // struct _xid
#include "pycore_pyerrors.h" // _Py_excinfo
#include "pycore_exceptions.h" // PyExceptionSnapshot_FromInfo()
#include "pycore_initconfig.h" // _PyErr_SetFromPyStatus()
#include "pycore_modsupport.h" // _PyArg_BadArgument()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
Expand Down Expand Up @@ -51,6 +51,9 @@ add_new_exception(PyObject *mod, const char *name, PyObject *base)
/* module state *************************************************************/

typedef struct {
/* heap types */
PyTypeObject *ExceptionSnapshotType;

/* exceptions */
PyObject *RunFailedError;
} module_state;
Expand All @@ -67,6 +70,9 @@ get_module_state(PyObject *mod)
static int
traverse_module_state(module_state *state, visitproc visit, void *arg)
{
/* heap types */
Py_VISIT(state->ExceptionSnapshotType);

/* exceptions */
Py_VISIT(state->RunFailedError);

Expand All @@ -76,6 +82,9 @@ traverse_module_state(module_state *state, visitproc visit, void *arg)
static int
clear_module_state(module_state *state)
{
/* heap types */
Py_CLEAR(state->ExceptionSnapshotType);

/* exceptions */
Py_CLEAR(state->RunFailedError);

Expand Down Expand Up @@ -759,6 +768,12 @@ The 'interpreters' module provides a more convenient interface.");
static int
module_exec(PyObject *mod)
{
PyInterpreterState *interp = PyInterpreterState_Get();
module_state *state = get_module_state(mod);
if (state == NULL) {
goto error;
}

/* Add exception types */
if (exceptions_init(mod) != 0) {
goto error;
Expand All @@ -769,6 +784,12 @@ module_exec(PyObject *mod)
goto error;
}

// ExceptionSnapshot
state->ExceptionSnapshotType = _PyExc_GetExceptionSnapshotType(interp);
if (PyModule_AddType(mod, state->ExceptionSnapshotType) < 0) {
goto error;
}

return 0;

error:
Expand Down
Loading
Loading