gh-109174: Add support of SimpleNamespace in copy.replace() (GH-109175)

This commit is contained in:
Serhiy Storchaka 2023-09-10 08:09:25 +03:00 committed by GitHub
parent 0eab2427b1
commit 92578919a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 0 deletions

View file

@ -504,6 +504,8 @@ Additional Utility Classes and Functions
However, for a structured record type use :func:`~collections.namedtuple`
instead.
:class:`!SimpleNamespace` objects are supported by :func:`copy.replace`.
.. versionadded:: 3.3
.. versionchanged:: 3.9

View file

@ -1900,6 +1900,33 @@ def test_pickle(self):
self.assertEqual(ns, ns_roundtrip, pname)
def test_replace(self):
ns = types.SimpleNamespace(x=11, y=22)
ns2 = copy.replace(ns)
self.assertEqual(ns2, ns)
self.assertIsNot(ns2, ns)
self.assertIs(type(ns2), types.SimpleNamespace)
self.assertEqual(vars(ns2), {'x': 11, 'y': 22})
ns2.x = 3
self.assertEqual(ns.x, 11)
ns.x = 4
self.assertEqual(ns2.x, 3)
self.assertEqual(vars(copy.replace(ns, x=1)), {'x': 1, 'y': 22})
self.assertEqual(vars(copy.replace(ns, y=2)), {'x': 4, 'y': 2})
self.assertEqual(vars(copy.replace(ns, x=1, y=2)), {'x': 1, 'y': 2})
def test_replace_subclass(self):
class Spam(types.SimpleNamespace):
pass
spam = Spam(ham=8, eggs=9)
spam2 = copy.replace(spam, ham=5)
self.assertIs(type(spam2), Spam)
self.assertEqual(vars(spam2), {'ham': 5, 'eggs': 9})
def test_fake_namespace_compare(self):
# Issue #24257: Incorrect use of PyObject_IsInstance() caused
# SystemError.

View file

@ -0,0 +1 @@
Add support of :class:`types.SimpleNamespace` in :func:`copy.replace`.

View file

@ -189,9 +189,37 @@ namespace_reduce(_PyNamespaceObject *ns, PyObject *Py_UNUSED(ignored))
}
static PyObject *
namespace_replace(PyObject *self, PyObject *args, PyObject *kwargs)
{
if (!_PyArg_NoPositional("__replace__", args)) {
return NULL;
}
PyObject *result = PyObject_CallNoArgs((PyObject *)Py_TYPE(self));
if (!result) {
return NULL;
}
if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict,
((_PyNamespaceObject*)self)->ns_dict) < 0)
{
Py_DECREF(result);
return NULL;
}
if (kwargs) {
if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict, kwargs) < 0) {
Py_DECREF(result);
return NULL;
}
}
return result;
}
static PyMethodDef namespace_methods[] = {
{"__reduce__", (PyCFunction)namespace_reduce, METH_NOARGS,
namespace_reduce__doc__},
{"__replace__", _PyCFunction_CAST(namespace_replace), METH_VARARGS|METH_KEYWORDS, NULL},
{NULL, NULL} // sentinel
};