gh-76785: Add More Tests to test_interpreters.test_api (gh-117662)

In addition to the increase test coverage, this is a precursor to sorting out how we handle interpreters created directly via the C-API.
This commit is contained in:
Eric Snow 2024-04-10 18:37:01 -06:00 committed by GitHub
parent 0cc71bde00
commit 993c3cca16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 2015 additions and 421 deletions

View file

@ -468,7 +468,7 @@ _release_xid_data(_PyCrossInterpreterData *data, int rawfree)
/***********************/
static int
_excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
_excinfo_init_type_from_exception(struct _excinfo_type *info, PyObject *exc)
{
/* Note that this copies directly rather than into an intermediate
struct and does not clear on error. If we need that then we
@ -504,7 +504,7 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
}
info->qualname = _copy_string_obj_raw(strobj, NULL);
Py_DECREF(strobj);
if (info->name == NULL) {
if (info->qualname == NULL) {
return -1;
}
@ -515,10 +515,51 @@ _excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
}
info->module = _copy_string_obj_raw(strobj, NULL);
Py_DECREF(strobj);
if (info->module == NULL) {
return -1;
}
return 0;
}
static int
_excinfo_init_type_from_object(struct _excinfo_type *info, PyObject *exctype)
{
PyObject *strobj = NULL;
// __name__
strobj = PyObject_GetAttrString(exctype, "__name__");
if (strobj == NULL) {
return -1;
}
info->name = _copy_string_obj_raw(strobj, NULL);
Py_DECREF(strobj);
if (info->name == NULL) {
return -1;
}
// __qualname__
strobj = PyObject_GetAttrString(exctype, "__qualname__");
if (strobj == NULL) {
return -1;
}
info->qualname = _copy_string_obj_raw(strobj, NULL);
Py_DECREF(strobj);
if (info->qualname == NULL) {
return -1;
}
// __module__
strobj = PyObject_GetAttrString(exctype, "__module__");
if (strobj == NULL) {
return -1;
}
info->module = _copy_string_obj_raw(strobj, NULL);
Py_DECREF(strobj);
if (info->module == NULL) {
return -1;
}
return 0;
}
@ -584,7 +625,7 @@ _PyXI_excinfo_Clear(_PyXI_excinfo *info)
*info = (_PyXI_excinfo){{NULL}};
}
static PyObject *
PyObject *
_PyXI_excinfo_format(_PyXI_excinfo *info)
{
const char *module, *qualname;
@ -627,7 +668,7 @@ _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
}
const char *failure = NULL;
if (_excinfo_init_type(&info->type, exc) < 0) {
if (_excinfo_init_type_from_exception(&info->type, exc) < 0) {
failure = "error while initializing exception type snapshot";
goto error;
}
@ -672,6 +713,57 @@ error:
return failure;
}
static const char *
_PyXI_excinfo_InitFromObject(_PyXI_excinfo *info, PyObject *obj)
{
const char *failure = NULL;
PyObject *exctype = PyObject_GetAttrString(obj, "type");
if (exctype == NULL) {
failure = "exception snapshot missing 'type' attribute";
goto error;
}
int res = _excinfo_init_type_from_object(&info->type, exctype);
Py_DECREF(exctype);
if (res < 0) {
failure = "error while initializing exception type snapshot";
goto error;
}
// Extract the exception message.
PyObject *msgobj = PyObject_GetAttrString(obj, "msg");
if (msgobj == NULL) {
failure = "exception snapshot missing 'msg' attribute";
goto error;
}
info->msg = _copy_string_obj_raw(msgobj, NULL);
Py_DECREF(msgobj);
if (info->msg == NULL) {
failure = "error while copying exception message";
goto error;
}
// Pickle a traceback.TracebackException.
PyObject *errdisplay = PyObject_GetAttrString(obj, "errdisplay");
if (errdisplay == NULL) {
failure = "exception snapshot missing 'errdisplay' attribute";
goto error;
}
info->errdisplay = _copy_string_obj_raw(errdisplay, NULL);
Py_DECREF(errdisplay);
if (info->errdisplay == NULL) {
failure = "error while copying exception error display";
goto error;
}
return NULL;
error:
assert(failure != NULL);
_PyXI_excinfo_Clear(info);
return failure;
}
static void
_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
{
@ -825,6 +917,47 @@ error:
}
int
_PyXI_InitExcInfo(_PyXI_excinfo *info, PyObject *exc)
{
assert(!PyErr_Occurred());
if (exc == NULL || exc == Py_None) {
PyErr_SetString(PyExc_ValueError, "missing exc");
return -1;
}
const char *failure;
if (PyExceptionInstance_Check(exc) || PyExceptionClass_Check(exc)) {
failure = _PyXI_excinfo_InitFromException(info, exc);
}
else {
failure = _PyXI_excinfo_InitFromObject(info, exc);
}
if (failure != NULL) {
PyErr_SetString(PyExc_Exception, failure);
return -1;
}
return 0;
}
PyObject *
_PyXI_FormatExcInfo(_PyXI_excinfo *info)
{
return _PyXI_excinfo_format(info);
}
PyObject *
_PyXI_ExcInfoAsObject(_PyXI_excinfo *info)
{
return _PyXI_excinfo_AsObject(info);
}
void
_PyXI_ClearExcInfo(_PyXI_excinfo *info)
{
_PyXI_excinfo_Clear(info);
}
/***************************/
/* short-term data sharing */
/***************************/
@ -1682,3 +1815,95 @@ _PyXI_FiniTypes(PyInterpreterState *interp)
{
fini_exceptions(interp);
}
/*************/
/* other API */
/*************/
PyInterpreterState *
_PyXI_NewInterpreter(PyInterpreterConfig *config,
PyThreadState **p_tstate, PyThreadState **p_save_tstate)
{
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
assert(save_tstate != NULL);
PyThreadState *tstate;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, config);
if (PyStatus_Exception(status)) {
// Since no new thread state was created, there is no exception
// to propagate; raise a fresh one after swapping back in the
// old thread state.
PyThreadState_Swap(save_tstate);
_PyErr_SetFromPyStatus(status);
PyObject *exc = PyErr_GetRaisedException();
PyErr_SetString(PyExc_InterpreterError,
"sub-interpreter creation failed");
_PyErr_ChainExceptions1(exc);
return NULL;
}
assert(tstate != NULL);
PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate);
_PyInterpreterState_SetWhence(interp, _PyInterpreterState_WHENCE_XI);
if (p_tstate != NULL) {
// We leave the new thread state as the current one.
*p_tstate = tstate;
}
else {
// Throw away the initial tstate.
PyThreadState_Clear(tstate);
PyThreadState_Swap(save_tstate);
PyThreadState_Delete(tstate);
save_tstate = NULL;
}
if (p_save_tstate != NULL) {
*p_save_tstate = save_tstate;
}
return interp;
}
void
_PyXI_EndInterpreter(PyInterpreterState *interp,
PyThreadState *tstate, PyThreadState **p_save_tstate)
{
PyThreadState *save_tstate = NULL;
PyThreadState *cur_tstate = PyThreadState_GET();
if (tstate == NULL) {
if (PyThreadState_GetInterpreter(cur_tstate) == interp) {
tstate = cur_tstate;
}
else {
tstate = PyThreadState_New(interp);
_PyThreadState_SetWhence(tstate, _PyThreadState_WHENCE_INTERP);
assert(tstate != NULL);
save_tstate = PyThreadState_Swap(tstate);
}
}
else {
assert(PyThreadState_GetInterpreter(tstate) == interp);
if (tstate != cur_tstate) {
assert(PyThreadState_GetInterpreter(cur_tstate) != interp);
save_tstate = PyThreadState_Swap(tstate);
}
}
long whence = _PyInterpreterState_GetWhence(interp);
assert(whence != _PyInterpreterState_WHENCE_RUNTIME);
if (whence == _PyInterpreterState_WHENCE_UNKNOWN) {
assert(!interp->_ready);
PyThreadState *tstate = PyThreadState_New(interp);
save_tstate = PyThreadState_Swap(tstate);
_PyInterpreterState_Clear(tstate);
PyInterpreterState_Delete(interp);
}
else {
Py_EndInterpreter(tstate);
}
if (p_save_tstate != NULL) {
save_tstate = *p_save_tstate;
}
PyThreadState_Swap(save_tstate);
}