gh-76785: Update test.support.interpreters to Align With PEP 734 (gh-115566)

This brings the code under test.support.interpreters, and the corresponding extension modules, in line with recent updates to PEP 734.

(Note: PEP 734 has not been accepted at this time.  However, we are using an internal copy of the implementation in the test suite to exercise the existing subinterpreters feature.)
This commit is contained in:
Eric Snow 2024-02-28 16:08:08 -07:00 committed by GitHub
parent 67c19e57b5
commit e80abd57a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 622 additions and 155 deletions

View file

@ -294,6 +294,8 @@ handle_queue_error(int err, PyObject *mod, int64_t qid)
case ERR_QUEUES_ALLOC:
PyErr_NoMemory();
break;
case -1:
return -1;
default:
state = get_module_state(mod);
assert(state->QueueError != NULL);
@ -320,14 +322,17 @@ struct _queueitem;
typedef struct _queueitem {
_PyCrossInterpreterData *data;
int fmt;
struct _queueitem *next;
} _queueitem;
static void
_queueitem_init(_queueitem *item, _PyCrossInterpreterData *data)
_queueitem_init(_queueitem *item,
_PyCrossInterpreterData *data, int fmt)
{
*item = (_queueitem){
.data = data,
.fmt = fmt,
};
}
@ -344,14 +349,14 @@ _queueitem_clear(_queueitem *item)
}
static _queueitem *
_queueitem_new(_PyCrossInterpreterData *data)
_queueitem_new(_PyCrossInterpreterData *data, int fmt)
{
_queueitem *item = GLOBAL_MALLOC(_queueitem);
if (item == NULL) {
PyErr_NoMemory();
return NULL;
}
_queueitem_init(item, data);
_queueitem_init(item, data, fmt);
return item;
}
@ -373,9 +378,11 @@ _queueitem_free_all(_queueitem *item)
}
static void
_queueitem_popped(_queueitem *item, _PyCrossInterpreterData **p_data)
_queueitem_popped(_queueitem *item,
_PyCrossInterpreterData **p_data, int *p_fmt)
{
*p_data = item->data;
*p_fmt = item->fmt;
// We clear them here, so they won't be released in _queueitem_clear().
item->data = NULL;
_queueitem_free(item);
@ -393,10 +400,11 @@ typedef struct _queue {
_queueitem *first;
_queueitem *last;
} items;
int fmt;
} _queue;
static int
_queue_init(_queue *queue, Py_ssize_t maxsize)
_queue_init(_queue *queue, Py_ssize_t maxsize, int fmt)
{
PyThread_type_lock mutex = PyThread_allocate_lock();
if (mutex == NULL) {
@ -408,6 +416,7 @@ _queue_init(_queue *queue, Py_ssize_t maxsize)
.items = {
.maxsize = maxsize,
},
.fmt = fmt,
};
return 0;
}
@ -486,7 +495,7 @@ _queue_unlock(_queue *queue)
}
static int
_queue_add(_queue *queue, _PyCrossInterpreterData *data)
_queue_add(_queue *queue, _PyCrossInterpreterData *data, int fmt)
{
int err = _queue_lock(queue);
if (err < 0) {
@ -502,7 +511,7 @@ _queue_add(_queue *queue, _PyCrossInterpreterData *data)
return ERR_QUEUE_FULL;
}
_queueitem *item = _queueitem_new(data);
_queueitem *item = _queueitem_new(data, fmt);
if (item == NULL) {
_queue_unlock(queue);
return -1;
@ -522,7 +531,8 @@ _queue_add(_queue *queue, _PyCrossInterpreterData *data)
}
static int
_queue_next(_queue *queue, _PyCrossInterpreterData **p_data)
_queue_next(_queue *queue,
_PyCrossInterpreterData **p_data, int *p_fmt)
{
int err = _queue_lock(queue);
if (err < 0) {
@ -541,7 +551,7 @@ _queue_next(_queue *queue, _PyCrossInterpreterData **p_data)
}
queue->items.count -= 1;
_queueitem_popped(item, p_data);
_queueitem_popped(item, p_data, p_fmt);
_queue_unlock(queue);
return 0;
@ -843,18 +853,26 @@ finally:
PyThread_release_lock(queues->mutex);
}
static int64_t *
struct queue_id_and_fmt {
int64_t id;
int fmt;
};
static struct queue_id_and_fmt *
_queues_list_all(_queues *queues, int64_t *count)
{
int64_t *qids = NULL;
struct queue_id_and_fmt *qids = NULL;
PyThread_acquire_lock(queues->mutex, WAIT_LOCK);
int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(queues->count));
struct queue_id_and_fmt *ids = PyMem_NEW(struct queue_id_and_fmt,
(Py_ssize_t)(queues->count));
if (ids == NULL) {
goto done;
}
_queueref *ref = queues->head;
for (int64_t i=0; ref != NULL; ref = ref->next, i++) {
ids[i] = ref->qid;
ids[i].id = ref->qid;
assert(ref->queue != NULL);
ids[i].fmt = ref->queue->fmt;
}
*count = queues->count;
@ -890,13 +908,13 @@ _queue_free(_queue *queue)
// Create a new queue.
static int64_t
queue_create(_queues *queues, Py_ssize_t maxsize)
queue_create(_queues *queues, Py_ssize_t maxsize, int fmt)
{
_queue *queue = GLOBAL_MALLOC(_queue);
if (queue == NULL) {
return ERR_QUEUE_ALLOC;
}
int err = _queue_init(queue, maxsize);
int err = _queue_init(queue, maxsize, fmt);
if (err < 0) {
GLOBAL_FREE(queue);
return (int64_t)err;
@ -925,7 +943,7 @@ queue_destroy(_queues *queues, int64_t qid)
// Push an object onto the queue.
static int
queue_put(_queues *queues, int64_t qid, PyObject *obj)
queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt)
{
// Look up the queue.
_queue *queue = NULL;
@ -948,7 +966,7 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj)
}
// Add the data to the queue.
int res = _queue_add(queue, data);
int res = _queue_add(queue, data, fmt);
_queue_unmark_waiter(queue, queues->mutex);
if (res != 0) {
// We may chain an exception here:
@ -963,7 +981,7 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj)
// Pop the next object off the queue. Fail if empty.
// XXX Support a "wait" mutex?
static int
queue_get(_queues *queues, int64_t qid, PyObject **res)
queue_get(_queues *queues, int64_t qid, PyObject **res, int *p_fmt)
{
int err;
*res = NULL;
@ -979,7 +997,7 @@ queue_get(_queues *queues, int64_t qid, PyObject **res)
// Pop off the next item from the queue.
_PyCrossInterpreterData *data = NULL;
err = _queue_next(queue, &data);
err = _queue_next(queue, &data, p_fmt);
_queue_unmark_waiter(queue, queues->mutex);
if (err != 0) {
return err;
@ -1267,14 +1285,15 @@ qidarg_converter(PyObject *arg, void *ptr)
static PyObject *
queuesmod_create(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"maxsize", NULL};
Py_ssize_t maxsize = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|n:create", kwlist,
&maxsize)) {
static char *kwlist[] = {"maxsize", "fmt", NULL};
Py_ssize_t maxsize;
int fmt;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ni:create", kwlist,
&maxsize, &fmt)) {
return NULL;
}
int64_t qid = queue_create(&_globals.queues, maxsize);
int64_t qid = queue_create(&_globals.queues, maxsize, fmt);
if (qid < 0) {
(void)handle_queue_error((int)qid, self, qid);
return NULL;
@ -1329,7 +1348,7 @@ static PyObject *
queuesmod_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))
{
int64_t count = 0;
int64_t *qids = _queues_list_all(&_globals.queues, &count);
struct queue_id_and_fmt *qids = _queues_list_all(&_globals.queues, &count);
if (qids == NULL) {
if (count == 0) {
return PyList_New(0);
@ -1340,14 +1359,14 @@ queuesmod_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))
if (ids == NULL) {
goto finally;
}
int64_t *cur = qids;
struct queue_id_and_fmt *cur = qids;
for (int64_t i=0; i < count; cur++, i++) {
PyObject *qidobj = PyLong_FromLongLong(*cur);
if (qidobj == NULL) {
PyObject *item = Py_BuildValue("Li", cur->id, cur->fmt);
if (item == NULL) {
Py_SETREF(ids, NULL);
break;
}
PyList_SET_ITEM(ids, (Py_ssize_t)i, qidobj);
PyList_SET_ITEM(ids, (Py_ssize_t)i, item);
}
finally:
@ -1363,17 +1382,18 @@ Return the list of IDs for all queues.");
static PyObject *
queuesmod_put(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"qid", "obj", NULL};
static char *kwlist[] = {"qid", "obj", "fmt", NULL};
qidarg_converter_data qidarg;
PyObject *obj;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O:put", kwlist,
qidarg_converter, &qidarg, &obj)) {
int fmt;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&Oi:put", kwlist,
qidarg_converter, &qidarg, &obj, &fmt)) {
return NULL;
}
int64_t qid = qidarg.id;
/* Queue up the object. */
int err = queue_put(&_globals.queues, qid, obj);
int err = queue_put(&_globals.queues, qid, obj, fmt);
if (handle_queue_error(err, self, qid)) {
return NULL;
}
@ -1382,7 +1402,7 @@ queuesmod_put(PyObject *self, PyObject *args, PyObject *kwds)
}
PyDoc_STRVAR(queuesmod_put_doc,
"put(qid, obj)\n\
"put(qid, obj, sharedonly=False)\n\
\n\
Add the object's data to the queue.");
@ -1399,7 +1419,8 @@ queuesmod_get(PyObject *self, PyObject *args, PyObject *kwds)
int64_t qid = qidarg.id;
PyObject *obj = NULL;
int err = queue_get(&_globals.queues, qid, &obj);
int fmt;
int err = queue_get(&_globals.queues, qid, &obj, &fmt);
if (err == ERR_QUEUE_EMPTY && dflt != NULL) {
assert(obj == NULL);
obj = Py_NewRef(dflt);
@ -1407,7 +1428,10 @@ queuesmod_get(PyObject *self, PyObject *args, PyObject *kwds)
else if (handle_queue_error(err, self, qid)) {
return NULL;
}
return obj;
PyObject *res = Py_BuildValue("Oi", obj, fmt);
Py_DECREF(obj);
return res;
}
PyDoc_STRVAR(queuesmod_get_doc,
@ -1499,6 +1523,33 @@ PyDoc_STRVAR(queuesmod_get_maxsize_doc,
\n\
Return the maximum number of items in the queue.");
static PyObject *
queuesmod_get_default_fmt(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"qid", NULL};
qidarg_converter_data qidarg;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"O&:get_default_fmt", kwlist,
qidarg_converter, &qidarg)) {
return NULL;
}
int64_t qid = qidarg.id;
_queue *queue = NULL;
int err = _queues_lookup(&_globals.queues, qid, &queue);
if (handle_queue_error(err, self, qid)) {
return NULL;
}
int fmt = queue->fmt;
_queue_unmark_waiter(queue, _globals.queues.mutex);
return PyLong_FromLong(fmt);
}
PyDoc_STRVAR(queuesmod_get_default_fmt_doc,
"get_default_fmt(qid)\n\
\n\
Return the default format to use for the queue.");
static PyObject *
queuesmod_is_full(PyObject *self, PyObject *args, PyObject *kwds)
{
@ -1593,6 +1644,8 @@ static PyMethodDef module_functions[] = {
METH_VARARGS | METH_KEYWORDS, queuesmod_release_doc},
{"get_maxsize", _PyCFunction_CAST(queuesmod_get_maxsize),
METH_VARARGS | METH_KEYWORDS, queuesmod_get_maxsize_doc},
{"get_default_fmt", _PyCFunction_CAST(queuesmod_get_default_fmt),
METH_VARARGS | METH_KEYWORDS, queuesmod_get_default_fmt_doc},
{"is_full", _PyCFunction_CAST(queuesmod_is_full),
METH_VARARGS | METH_KEYWORDS, queuesmod_is_full_doc},
{"get_count", _PyCFunction_CAST(queuesmod_get_count),

View file

@ -902,6 +902,56 @@ The code/function must not take any arguments or be a closure\n\
If a function is provided, its code object is used and all its state\n\
is ignored, including its __globals__ dict.");
static PyObject *
interp_call(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwlist[] = {"id", "callable", "args", "kwargs", NULL};
PyObject *id, *callable;
PyObject *args_obj = NULL;
PyObject *kwargs_obj = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"OO|OO:" MODULE_NAME_STR ".call", kwlist,
&id, &callable, &args_obj, &kwargs_obj)) {
return NULL;
}
if (args_obj != NULL) {
PyErr_SetString(PyExc_ValueError, "got unexpected args");
return NULL;
}
if (kwargs_obj != NULL) {
PyErr_SetString(PyExc_ValueError, "got unexpected kwargs");
return NULL;
}
PyObject *code = (PyObject *)convert_code_arg(callable, MODULE_NAME_STR ".call",
"argument 2", "a function");
if (code == NULL) {
return NULL;
}
PyObject *excinfo = NULL;
int res = _interp_exec(self, id, code, NULL, &excinfo);
Py_DECREF(code);
if (res < 0) {
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
return excinfo;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(call_doc,
"call(id, callable, args=None, kwargs=None)\n\
\n\
Call the provided object in the identified interpreter.\n\
Pass the given args and kwargs, if possible.\n\
\n\
\"callable\" may be a plain function with no free vars that takes\n\
no arguments.\n\
\n\
The function's code object is used and all its state\n\
is ignored, including its __globals__ dict.");
static PyObject *
interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
{
@ -1085,6 +1135,8 @@ static PyMethodDef module_functions[] = {
METH_VARARGS | METH_KEYWORDS, is_running_doc},
{"exec", _PyCFunction_CAST(interp_exec),
METH_VARARGS | METH_KEYWORDS, exec_doc},
{"call", _PyCFunction_CAST(interp_call),
METH_VARARGS | METH_KEYWORDS, call_doc},
{"run_string", _PyCFunction_CAST(interp_run_string),
METH_VARARGS | METH_KEYWORDS, run_string_doc},
{"run_func", _PyCFunction_CAST(interp_run_func),
@ -1113,6 +1165,7 @@ 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);
// exceptions
@ -1122,6 +1175,11 @@ module_exec(PyObject *mod)
if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterNotFoundError) < 0) {
goto error;
}
PyObject *PyExc_NotShareableError = \
_PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError;
if (PyModule_AddType(mod, (PyTypeObject *)PyExc_NotShareableError) < 0) {
goto error;
}
if (register_memoryview_xid(mod, &state->XIBufferViewType) < 0) {
goto error;