]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-109174: Add support of SimpleNamespace in copy.replace() (GH-109175)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 10 Sep 2023 05:09:25 +0000 (08:09 +0300)
committerGitHub <noreply@github.com>
Sun, 10 Sep 2023 05:09:25 +0000 (08:09 +0300)
Doc/library/types.rst
Lib/test/test_types.py
Misc/NEWS.d/next/Library/2023-09-09-09-05-41.gh-issue-109174.OJea5s.rst [new file with mode: 0644]
Objects/namespaceobject.c

index 82300afef0641e60cfb78a8b74b112c28829fc9c..875916be1049a31794f628070e0fe61070405a53 100644 (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
index f2efee90dc02401d3bf8863fbbf67c6d282dbe0a..c6bff79f9038283d902209768d190c4c1e8282fc 100644 (file)
@@ -1900,6 +1900,33 @@ class SimpleNamespaceTests(unittest.TestCase):
 
             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.
diff --git a/Misc/NEWS.d/next/Library/2023-09-09-09-05-41.gh-issue-109174.OJea5s.rst b/Misc/NEWS.d/next/Library/2023-09-09-09-05-41.gh-issue-109174.OJea5s.rst
new file mode 100644 (file)
index 0000000..63461fa
--- /dev/null
@@ -0,0 +1 @@
+Add support of :class:`types.SimpleNamespace` in :func:`copy.replace`.
index 11cf859add3ab8bf7c66c8f6ce762c45ec90f2a2..204c114fd9de2d93d7cd4e4228bd0a2a81d3fee8 100644 (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
 };