summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorXuehai Pan <XuehaiPan@pku.edu.cn>2023-10-04 16:47:41 (GMT)
committerGitHub <noreply@github.com>2023-10-04 16:47:41 (GMT)
commit3bbe3b7c822091caac90c00ee937848bc4de80eb (patch)
treef1978f0b37025a3cfce65091adba57616da4dac4 /Objects
parent9561648f4a5d8486b67ee4bbe24a239b2a93212c (diff)
downloadcpython-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.c76
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