mirror of
https://github.com/python/cpython
synced 2024-10-14 11:58:12 +00:00
gh-76785: Move _Py_excinfo Functions Out of the Internal C-API (gh-111715)
I added _Py_excinfo to the internal API (and added its functions in Python/errors.c) in gh-111530 (9322ce9
). Since then I've had a nagging sense that I should have added the type and functions in its own PR. While I do plan on using _Py_excinfo outside crossinterp.c very soon (see gh-111572/gh-111573), I'd still feel more comfortable if the _Py_excinfo stuff went in as its own PR. Hence, here we are.
(FWIW, I may combine that with gh-111572, which I may, in turn, combine with gh-111573. We'll see.)
This commit is contained in:
parent
836e0a75d5
commit
d4426e8d00
|
@ -164,6 +164,17 @@ 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,
|
||||
|
|
|
@ -68,30 +68,6 @@ 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)
|
||||
|
|
|
@ -800,6 +800,17 @@ _xidregistry_fini(struct _xidregistry *registry)
|
|||
/* convenience utilities */
|
||||
/*************************/
|
||||
|
||||
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 const char *
|
||||
_copy_string_obj_raw(PyObject *strobj)
|
||||
{
|
||||
|
@ -835,6 +846,118 @@ _release_xid_data(_PyCrossInterpreterData *data, int rawfree)
|
|||
}
|
||||
|
||||
|
||||
/* exception snapshots */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static 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 };
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************/
|
||||
/* short-term data sharing */
|
||||
/***************************/
|
||||
|
|
175
Python/errors.c
175
Python/errors.c
|
@ -1934,178 +1934,3 @@ 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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue