diff options
author | Xuehai Pan <XuehaiPan@pku.edu.cn> | 2023-10-04 16:47:41 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-04 16:47:41 (GMT) |
commit | 3bbe3b7c822091caac90c00ee937848bc4de80eb (patch) | |
tree | f1978f0b37025a3cfce65091adba57616da4dac4 /Objects | |
parent | 9561648f4a5d8486b67ee4bbe24a239b2a93212c (diff) | |
download | cpython-3bbe3b7c822091caac90c00ee937848bc4de80eb.zip cpython-3bbe3b7c822091caac90c00ee937848bc4de80eb.tar.gz cpython-3bbe3b7c822091caac90c00ee937848bc4de80eb.tar.bz2 |
gh-110222: Add support of PyStructSequence in copy.replace() (GH-110223)
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/structseq.c | 76 |
1 files changed, 75 insertions, 1 deletions
diff --git a/Objects/structseq.c b/Objects/structseq.c index 2c98288..e4a4b45 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -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 |