gh-110222: Add support of PyStructSequence in copy.replace() (GH-110223)

This commit is contained in:
Xuehai Pan 2023-10-05 00:47:41 +08:00 committed by GitHub
parent 9561648f4a
commit 3bbe3b7c82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 155 additions and 1 deletions

View file

@ -8,6 +8,7 @@
*/
#include "Python.h"
#include "pycore_dict.h" // _PyDict_Pop()
#include "pycore_tuple.h" // _PyTuple_FromArray()
#include "pycore_object.h" // _PyObject_GC_TRACK()
@ -380,9 +381,82 @@ error:
return NULL;
}
static PyObject *
structseq_replace(PyStructSequence *self, PyObject *args, PyObject *kwargs)
{
PyStructSequence *result = NULL;
Py_ssize_t n_fields, n_unnamed_fields, i;
if (!_PyArg_NoPositional("__replace__", args)) {
return NULL;
}
n_fields = REAL_SIZE(self);
if (n_fields < 0) {
return NULL;
}
n_unnamed_fields = UNNAMED_FIELDS(self);
if (n_unnamed_fields < 0) {
return NULL;
}
if (n_unnamed_fields > 0) {
PyErr_Format(PyExc_TypeError,
"__replace__() is not supported for %.500s "
"because it has unnamed field(s)",
Py_TYPE(self)->tp_name);
return NULL;
}
result = (PyStructSequence *) PyStructSequence_New(Py_TYPE(self));
if (!result) {
return NULL;
}
if (kwargs != NULL) {
// We do not support types with unnamed fields, so we can iterate over
// i >= n_visible_fields case without slicing with (i - n_unnamed_fields).
for (i = 0; i < n_fields; ++i) {
PyObject *key = PyUnicode_FromString(Py_TYPE(self)->tp_members[i].name);
if (!key) {
goto error;
}
PyObject *ob = _PyDict_Pop(kwargs, key, self->ob_item[i]);
Py_DECREF(key);
if (!ob) {
goto error;
}
result->ob_item[i] = ob;
}
// Check if there are any unexpected fields.
if (PyDict_GET_SIZE(kwargs) > 0) {
PyObject *names = PyDict_Keys(kwargs);
if (names) {
PyErr_Format(PyExc_TypeError, "Got unexpected field name(s): %R", names);
Py_DECREF(names);
}
goto error;
}
}
else
{
// Just create a copy of the original.
for (i = 0; i < n_fields; ++i) {
result->ob_item[i] = Py_NewRef(self->ob_item[i]);
}
}
return (PyObject *)result;
error:
Py_DECREF(result);
return NULL;
}
static PyMethodDef structseq_methods[] = {
{"__reduce__", (PyCFunction)structseq_reduce, METH_NOARGS, NULL},
{NULL, NULL}
{"__replace__", _PyCFunction_CAST(structseq_replace), METH_VARARGS | METH_KEYWORDS, NULL},
{NULL, NULL} // sentinel
};
static Py_ssize_t