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:
Eric Snow 2023-11-01 17:36:40 -06:00 committed by GitHub
parent cde1071b2a
commit 9322ce90ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1309 additions and 472 deletions

View file

@ -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;
}
}