|
8 | 8 | */
|
9 | 9 |
|
10 | 10 | #include "Python.h"
|
| 11 | +#include "pycore_dict.h" // _PyDict_Pop(() |
11 | 12 | #include "pycore_tuple.h" // _PyTuple_FromArray()
|
12 | 13 | #include "pycore_object.h" // _PyObject_GC_TRACK()
|
13 | 14 |
|
@@ -365,9 +366,99 @@ structseq_reduce(PyStructSequence* self, PyObject *Py_UNUSED(ignored))
|
365 | 366 | return NULL;
|
366 | 367 | }
|
367 | 368 |
|
| 369 | + |
| 370 | +static PyObject * |
| 371 | +structseq_replace(PyStructSequence *self, PyObject *args, PyObject *kwargs) |
| 372 | +{ |
| 373 | + PyObject *tup = NULL; |
| 374 | + PyObject *dict = NULL; |
| 375 | + PyObject *result; |
| 376 | + Py_ssize_t n_fields, n_visible_fields, n_unnamed_fields, i; |
| 377 | + |
| 378 | + if (!_PyArg_NoPositional("__replace__", args)) { |
| 379 | + return NULL; |
| 380 | + } |
| 381 | + |
| 382 | + n_fields = REAL_SIZE(self); |
| 383 | + if (n_fields < 0) { |
| 384 | + return NULL; |
| 385 | + } |
| 386 | + n_visible_fields = VISIBLE_SIZE(self); |
| 387 | + n_unnamed_fields = UNNAMED_FIELDS(self); |
| 388 | + if (n_unnamed_fields < 0) { |
| 389 | + return NULL; |
| 390 | + } |
| 391 | + tup = _PyTuple_FromArray(self->ob_item, n_visible_fields); |
| 392 | + if (!tup) { |
| 393 | + goto error; |
| 394 | + } |
| 395 | + |
| 396 | + dict = PyDict_New(); |
| 397 | + if (!dict) { |
| 398 | + goto error; |
| 399 | + } |
| 400 | + |
| 401 | + if (kwargs != NULL) { |
| 402 | + for (i = 0; i < n_visible_fields; i++) { |
| 403 | + const char *name = Py_TYPE(self)->tp_members[i].name; |
| 404 | + PyObject *key = PyUnicode_FromString(name); |
| 405 | + if (!key) { |
| 406 | + goto error; |
| 407 | + } |
| 408 | + PyObject *ob = _PyDict_Pop(kwargs, key, self->ob_item[i]); |
| 409 | + Py_DECREF(key); |
| 410 | + if (!ob) { |
| 411 | + goto error; |
| 412 | + } |
| 413 | + PyTuple_SetItem(tup, i, ob); |
| 414 | + } |
| 415 | + for (i = n_visible_fields; i < n_fields; i++) { |
| 416 | + const char *name = Py_TYPE(self)->tp_members[i - n_unnamed_fields].name; |
| 417 | + PyObject *key = PyUnicode_FromString(name); |
| 418 | + if (!key) { |
| 419 | + goto error; |
| 420 | + } |
| 421 | + PyObject *ob = _PyDict_Pop(kwargs, key, self->ob_item[i]); |
| 422 | + if (PyDict_SetItem(dict, key, ob) < 0) { |
| 423 | + goto error; |
| 424 | + } |
| 425 | + } |
| 426 | + if (PyDict_Size(kwargs) > 0) { |
| 427 | + PyObject *names = PyDict_Keys(kwargs); |
| 428 | + if (names) { |
| 429 | + PyErr_Format(PyExc_ValueError, "Got unexpected field name(s): %R", names); |
| 430 | + Py_DECREF(names); |
| 431 | + } |
| 432 | + goto error; |
| 433 | + } |
| 434 | + } |
| 435 | + else |
| 436 | + { |
| 437 | + for (i = n_visible_fields; i < n_fields; i++) { |
| 438 | + const char *name = Py_TYPE(self)->tp_members[i - n_unnamed_fields].name; |
| 439 | + if (PyDict_SetItemString(dict, name, self->ob_item[i]) < 0) { |
| 440 | + goto error; |
| 441 | + } |
| 442 | + } |
| 443 | + } |
| 444 | + |
| 445 | + result = structseq_new_impl(Py_TYPE(self), tup, dict); |
| 446 | + |
| 447 | + Py_DECREF(tup); |
| 448 | + Py_DECREF(dict); |
| 449 | + |
| 450 | + return result; |
| 451 | + |
| 452 | +error: |
| 453 | + Py_XDECREF(tup); |
| 454 | + Py_XDECREF(dict); |
| 455 | + return NULL; |
| 456 | +} |
| 457 | + |
368 | 458 | static PyMethodDef structseq_methods[] = {
|
369 | 459 | {"__reduce__", (PyCFunction)structseq_reduce, METH_NOARGS, NULL},
|
370 |
| - {NULL, NULL} |
| 460 | + {"__replace__", _PyCFunction_CAST(structseq_replace), METH_VARARGS | METH_KEYWORDS, NULL}, |
| 461 | + {NULL, NULL} // sentinel |
371 | 462 | };
|
372 | 463 |
|
373 | 464 | static Py_ssize_t
|
|
0 commit comments