Skip to content

Commit d4426e8

Browse files
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.)
1 parent 836e0a7 commit d4426e8

File tree

4 files changed

+134
-199
lines changed

4 files changed

+134
-199
lines changed

Include/internal/pycore_crossinterp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,17 @@ extern void _PyXI_Fini(PyInterpreterState *interp);
164164
/* short-term data sharing */
165165
/***************************/
166166

167+
// Ultimately we'd like to preserve enough information about the
168+
// exception and traceback that we could re-constitute (or at least
169+
// simulate, a la traceback.TracebackException), and even chain, a copy
170+
// of the exception in the calling interpreter.
171+
172+
typedef struct _excinfo {
173+
const char *type;
174+
const char *msg;
175+
} _Py_excinfo;
176+
177+
167178
typedef enum error_code {
168179
_PyXI_ERR_NO_ERROR = 0,
169180
_PyXI_ERR_UNCAUGHT_EXCEPTION = -1,

Include/internal/pycore_pyerrors.h

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -68,30 +68,6 @@ extern PyStatus _PyErr_InitTypes(PyInterpreterState *);
6868
extern void _PyErr_FiniTypes(PyInterpreterState *);
6969

7070

71-
/* exception snapshots */
72-
73-
// Ultimately we'd like to preserve enough information about the
74-
// exception and traceback that we could re-constitute (or at least
75-
// simulate, a la traceback.TracebackException), and even chain, a copy
76-
// of the exception in the calling interpreter.
77-
78-
typedef struct _excinfo {
79-
const char *type;
80-
const char *msg;
81-
} _Py_excinfo;
82-
83-
extern void _Py_excinfo_Clear(_Py_excinfo *info);
84-
extern int _Py_excinfo_Copy(_Py_excinfo *dest, _Py_excinfo *src);
85-
extern const char * _Py_excinfo_InitFromException(
86-
_Py_excinfo *info,
87-
PyObject *exc);
88-
extern void _Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype);
89-
extern const char * _Py_excinfo_AsUTF8(
90-
_Py_excinfo *info,
91-
char *buf,
92-
size_t bufsize);
93-
94-
9571
/* other API */
9672

9773
static inline PyObject* _PyErr_Occurred(PyThreadState *tstate)

Python/crossinterp.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,17 @@ _xidregistry_fini(struct _xidregistry *registry)
800800
/* convenience utilities */
801801
/*************************/
802802

803+
static const char *
804+
_copy_raw_string(const char *str)
805+
{
806+
char *copied = PyMem_RawMalloc(strlen(str)+1);
807+
if (copied == NULL) {
808+
return NULL;
809+
}
810+
strcpy(copied, str);
811+
return copied;
812+
}
813+
803814
static const char *
804815
_copy_string_obj_raw(PyObject *strobj)
805816
{
@@ -835,6 +846,118 @@ _release_xid_data(_PyCrossInterpreterData *data, int rawfree)
835846
}
836847

837848

849+
/* exception snapshots */
850+
851+
static int
852+
_exc_type_name_as_utf8(PyObject *exc, const char **p_typename)
853+
{
854+
// XXX Use PyObject_GetAttrString(Py_TYPE(exc), '__name__')?
855+
PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name);
856+
if (nameobj == NULL) {
857+
assert(PyErr_Occurred());
858+
*p_typename = "unable to format exception type name";
859+
return -1;
860+
}
861+
const char *name = PyUnicode_AsUTF8(nameobj);
862+
if (name == NULL) {
863+
assert(PyErr_Occurred());
864+
Py_DECREF(nameobj);
865+
*p_typename = "unable to encode exception type name";
866+
return -1;
867+
}
868+
name = _copy_raw_string(name);
869+
Py_DECREF(nameobj);
870+
if (name == NULL) {
871+
*p_typename = "out of memory copying exception type name";
872+
return -1;
873+
}
874+
*p_typename = name;
875+
return 0;
876+
}
877+
878+
static int
879+
_exc_msg_as_utf8(PyObject *exc, const char **p_msg)
880+
{
881+
PyObject *msgobj = PyObject_Str(exc);
882+
if (msgobj == NULL) {
883+
assert(PyErr_Occurred());
884+
*p_msg = "unable to format exception message";
885+
return -1;
886+
}
887+
const char *msg = PyUnicode_AsUTF8(msgobj);
888+
if (msg == NULL) {
889+
assert(PyErr_Occurred());
890+
Py_DECREF(msgobj);
891+
*p_msg = "unable to encode exception message";
892+
return -1;
893+
}
894+
msg = _copy_raw_string(msg);
895+
Py_DECREF(msgobj);
896+
if (msg == NULL) {
897+
assert(PyErr_ExceptionMatches(PyExc_MemoryError));
898+
*p_msg = "out of memory copying exception message";
899+
return -1;
900+
}
901+
*p_msg = msg;
902+
return 0;
903+
}
904+
905+
static void
906+
_Py_excinfo_Clear(_Py_excinfo *info)
907+
{
908+
if (info->type != NULL) {
909+
PyMem_RawFree((void *)info->type);
910+
}
911+
if (info->msg != NULL) {
912+
PyMem_RawFree((void *)info->msg);
913+
}
914+
*info = (_Py_excinfo){ NULL };
915+
}
916+
917+
static const char *
918+
_Py_excinfo_InitFromException(_Py_excinfo *info, PyObject *exc)
919+
{
920+
assert(exc != NULL);
921+
922+
// Extract the exception type name.
923+
const char *typename = NULL;
924+
if (_exc_type_name_as_utf8(exc, &typename) < 0) {
925+
assert(typename != NULL);
926+
return typename;
927+
}
928+
929+
// Extract the exception message.
930+
const char *msg = NULL;
931+
if (_exc_msg_as_utf8(exc, &msg) < 0) {
932+
assert(msg != NULL);
933+
return msg;
934+
}
935+
936+
info->type = typename;
937+
info->msg = msg;
938+
return NULL;
939+
}
940+
941+
static void
942+
_Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype)
943+
{
944+
if (info->type != NULL) {
945+
if (info->msg != NULL) {
946+
PyErr_Format(exctype, "%s: %s", info->type, info->msg);
947+
}
948+
else {
949+
PyErr_SetString(exctype, info->type);
950+
}
951+
}
952+
else if (info->msg != NULL) {
953+
PyErr_SetString(exctype, info->msg);
954+
}
955+
else {
956+
PyErr_SetNone(exctype);
957+
}
958+
}
959+
960+
838961
/***************************/
839962
/* short-term data sharing */
840963
/***************************/

Python/errors.c

Lines changed: 0 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,178 +1934,3 @@ PyErr_ProgramTextObject(PyObject *filename, int lineno)
19341934
{
19351935
return _PyErr_ProgramDecodedTextObject(filename, lineno, NULL);
19361936
}
1937-
1938-
1939-
/***********************/
1940-
/* exception snapshots */
1941-
/***********************/
1942-
1943-
static const char *
1944-
_copy_raw_string(const char *str)
1945-
{
1946-
char *copied = PyMem_RawMalloc(strlen(str)+1);
1947-
if (copied == NULL) {
1948-
return NULL;
1949-
}
1950-
strcpy(copied, str);
1951-
return copied;
1952-
}
1953-
1954-
static int
1955-
_exc_type_name_as_utf8(PyObject *exc, const char **p_typename)
1956-
{
1957-
// XXX Use PyObject_GetAttrString(Py_TYPE(exc), '__name__')?
1958-
PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name);
1959-
if (nameobj == NULL) {
1960-
assert(PyErr_Occurred());
1961-
*p_typename = "unable to format exception type name";
1962-
return -1;
1963-
}
1964-
const char *name = PyUnicode_AsUTF8(nameobj);
1965-
if (name == NULL) {
1966-
assert(PyErr_Occurred());
1967-
Py_DECREF(nameobj);
1968-
*p_typename = "unable to encode exception type name";
1969-
return -1;
1970-
}
1971-
name = _copy_raw_string(name);
1972-
Py_DECREF(nameobj);
1973-
if (name == NULL) {
1974-
*p_typename = "out of memory copying exception type name";
1975-
return -1;
1976-
}
1977-
*p_typename = name;
1978-
return 0;
1979-
}
1980-
1981-
static int
1982-
_exc_msg_as_utf8(PyObject *exc, const char **p_msg)
1983-
{
1984-
PyObject *msgobj = PyObject_Str(exc);
1985-
if (msgobj == NULL) {
1986-
assert(PyErr_Occurred());
1987-
*p_msg = "unable to format exception message";
1988-
return -1;
1989-
}
1990-
const char *msg = PyUnicode_AsUTF8(msgobj);
1991-
if (msg == NULL) {
1992-
assert(PyErr_Occurred());
1993-
Py_DECREF(msgobj);
1994-
*p_msg = "unable to encode exception message";
1995-
return -1;
1996-
}
1997-
msg = _copy_raw_string(msg);
1998-
Py_DECREF(msgobj);
1999-
if (msg == NULL) {
2000-
assert(PyErr_ExceptionMatches(PyExc_MemoryError));
2001-
*p_msg = "out of memory copying exception message";
2002-
return -1;
2003-
}
2004-
*p_msg = msg;
2005-
return 0;
2006-
}
2007-
2008-
void
2009-
_Py_excinfo_Clear(_Py_excinfo *info)
2010-
{
2011-
if (info->type != NULL) {
2012-
PyMem_RawFree((void *)info->type);
2013-
}
2014-
if (info->msg != NULL) {
2015-
PyMem_RawFree((void *)info->msg);
2016-
}
2017-
*info = (_Py_excinfo){ NULL };
2018-
}
2019-
2020-
int
2021-
_Py_excinfo_Copy(_Py_excinfo *dest, _Py_excinfo *src)
2022-
{
2023-
// XXX Clear dest first?
2024-
2025-
if (src->type == NULL) {
2026-
dest->type = NULL;
2027-
}
2028-
else {
2029-
dest->type = _copy_raw_string(src->type);
2030-
if (dest->type == NULL) {
2031-
return -1;
2032-
}
2033-
}
2034-
2035-
if (src->msg == NULL) {
2036-
dest->msg = NULL;
2037-
}
2038-
else {
2039-
dest->msg = _copy_raw_string(src->msg);
2040-
if (dest->msg == NULL) {
2041-
return -1;
2042-
}
2043-
}
2044-
2045-
return 0;
2046-
}
2047-
2048-
const char *
2049-
_Py_excinfo_InitFromException(_Py_excinfo *info, PyObject *exc)
2050-
{
2051-
assert(exc != NULL);
2052-
2053-
// Extract the exception type name.
2054-
const char *typename = NULL;
2055-
if (_exc_type_name_as_utf8(exc, &typename) < 0) {
2056-
assert(typename != NULL);
2057-
return typename;
2058-
}
2059-
2060-
// Extract the exception message.
2061-
const char *msg = NULL;
2062-
if (_exc_msg_as_utf8(exc, &msg) < 0) {
2063-
assert(msg != NULL);
2064-
return msg;
2065-
}
2066-
2067-
info->type = typename;
2068-
info->msg = msg;
2069-
return NULL;
2070-
}
2071-
2072-
void
2073-
_Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype)
2074-
{
2075-
if (info->type != NULL) {
2076-
if (info->msg != NULL) {
2077-
PyErr_Format(exctype, "%s: %s", info->type, info->msg);
2078-
}
2079-
else {
2080-
PyErr_SetString(exctype, info->type);
2081-
}
2082-
}
2083-
else if (info->msg != NULL) {
2084-
PyErr_SetString(exctype, info->msg);
2085-
}
2086-
else {
2087-
PyErr_SetNone(exctype);
2088-
}
2089-
}
2090-
2091-
const char *
2092-
_Py_excinfo_AsUTF8(_Py_excinfo *info, char *buf, size_t bufsize)
2093-
{
2094-
// XXX Dynamically allocate if no buf provided?
2095-
assert(buf != NULL);
2096-
if (info->type != NULL) {
2097-
if (info->msg != NULL) {
2098-
snprintf(buf, bufsize, "%s: %s", info->type, info->msg);
2099-
return buf;
2100-
}
2101-
else {
2102-
return info->type;
2103-
}
2104-
}
2105-
else if (info->msg != NULL) {
2106-
return info->msg;
2107-
}
2108-
else {
2109-
return NULL;
2110-
}
2111-
}

0 commit comments

Comments
 (0)