Skip to content

Commit

Permalink
pythongh-109174: Add support of SimpleNamespace in copy.replace() (py…
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka authored Sep 10, 2023
1 parent 0eab242 commit 9257891
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Doc/library/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support of :class:`types.SimpleNamespace` in :func:`copy.replace`.
28 changes: 28 additions & 0 deletions Objects/namespaceobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down

0 comments on commit 9257891

Please sign in to comment.