| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e924ddb commit b5c8f92
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -212,6 +212,8 @@ Standard names are defined for the following types: | |||
| 212 | 212 | keys = sorted(self.__dict__) | |
| 213 | 213 | items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) | |
| 214 | 214 | return "{}({})".format(type(self).__name__, ", ".join(items)) | |
| 215 | + def __eq__(self, other): | ||
| 216 | + return self.__dict__ == other.__dict__ | ||
| 215 | 217 | ||
| 216 | 218 | ``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``. | |
| 217 | 219 | However, for a structured record type use :func:`~collections.namedtuple` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | from test.support import run_unittest, run_with_locale | |
| 4 | 4 | import collections | |
| 5 | + import pickle | ||
| 5 | 6 | import locale | |
| 6 | 7 | import sys | |
| 7 | 8 | import types | |
@@ -1077,9 +1078,19 @@ def test_repr(self): | |||
| 1077 | 1078 | ns2 = types.SimpleNamespace() | |
| 1078 | 1079 | ns2.x = "spam" | |
| 1079 | 1080 | ns2._y = 5 | |
| 1081 | + name = "namespace" | ||
| 1080 | 1082 | ||
| 1081 | - self.assertEqual(repr(ns1), "namespace(w=3, x=1, y=2)") | ||
| 1082 | - self.assertEqual(repr(ns2), "namespace(_y=5, x='spam')") | ||
| 1083 | + self.assertEqual(repr(ns1), "{name}(w=3, x=1, y=2)".format(name=name)) | ||
| 1084 | + self.assertEqual(repr(ns2), "{name}(_y=5, x='spam')".format(name=name)) | ||
| 1085 | + | ||
| 1086 | + def test_equal(self): | ||
| 1087 | + ns1 = types.SimpleNamespace(x=1) | ||
| 1088 | + ns2 = types.SimpleNamespace() | ||
| 1089 | + ns2.x = 1 | ||
| 1090 | + | ||
| 1091 | + self.assertEqual(types.SimpleNamespace(), types.SimpleNamespace()) | ||
| 1092 | + self.assertEqual(ns1, ns2) | ||
| 1093 | + self.assertNotEqual(ns2, types.SimpleNamespace()) | ||
| 1083 | 1094 | ||
| 1084 | 1095 | def test_nested(self): | |
| 1085 | 1096 | ns1 = types.SimpleNamespace(a=1, b=2) | |
@@ -1117,11 +1128,12 @@ def test_recursive_repr(self): | |||
| 1117 | 1128 | ns1.spam = ns1 | |
| 1118 | 1129 | ns2.spam = ns3 | |
| 1119 | 1130 | ns3.spam = ns2 | |
| 1131 | + name = "namespace" | ||
| 1132 | + repr1 = "{name}(c='cookie', spam={name}(...))".format(name=name) | ||
| 1133 | + repr2 = "{name}(spam={name}(spam={name}(...), x=1))".format(name=name) | ||
| 1120 | 1134 | ||
| 1121 | - self.assertEqual(repr(ns1), | ||
| 1122 | - "namespace(c='cookie', spam=namespace(...))") | ||
| 1123 | - self.assertEqual(repr(ns2), | ||
| 1124 | - "namespace(spam=namespace(spam=namespace(...), x=1))") | ||
| 1135 | + self.assertEqual(repr(ns1), repr1) | ||
| 1136 | + self.assertEqual(repr(ns2), repr2) | ||
| 1125 | 1137 | ||
| 1126 | 1138 | def test_as_dict(self): | |
| 1127 | 1139 | ns = types.SimpleNamespace(spam='spamspamspam') | |
@@ -1144,6 +1156,14 @@ class Spam(types.SimpleNamespace): | |||
| 1144 | 1156 | self.assertIs(type(spam), Spam) | |
| 1145 | 1157 | self.assertEqual(vars(spam), {'ham': 8, 'eggs': 9}) | |
| 1146 | 1158 | ||
| 1159 | + def test_pickle(self): | ||
| 1160 | + ns = types.SimpleNamespace(breakfast="spam", lunch="spam") | ||
| 1161 | + | ||
| 1162 | + ns_pickled = pickle.dumps(ns) | ||
| 1163 | + ns_roundtrip = pickle.loads(ns_pickled) | ||
| 1164 | + | ||
| 1165 | + self.assertEqual(ns, ns_roundtrip) | ||
| 1166 | + | ||
| 1147 | 1167 | ||
| 1148 | 1168 | def test_main(): | |
| 1149 | 1169 | run_unittest(TypesTests, MappingProxyTests, ClassCreationTests, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -252,6 +252,8 @@ Core and Builtins | |||
| 252 | 252 | - Issue #15111: __import__ should propagate ImportError when raised as a | |
| 253 | 253 | side-effect of a module triggered from using fromlist. | |
| 254 | 254 | ||
| 255 | + - Issue #15022: Add pickle and comparison support to types.SimpleNamespace. | ||
| 256 | + | ||
| 255 | 257 | Library | |
| 256 | 258 | ------- | |
| 257 | 259 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -66,16 +66,20 @@ namespace_dealloc(_PyNamespaceObject *ns) | |||
| 66 | 66 | ||
| 67 | 67 | ||
| 68 | 68 | static PyObject * | |
| 69 | - namespace_repr(_PyNamespaceObject *ns) | ||
| 69 | + namespace_repr(PyObject *ns) | ||
| 70 | 70 | { | |
| 71 | 71 | int i, loop_error = 0; | |
| 72 | 72 | PyObject *pairs = NULL, *d = NULL, *keys = NULL, *keys_iter = NULL; | |
| 73 | 73 | PyObject *key; | |
| 74 | 74 | PyObject *separator, *pairsrepr, *repr = NULL; | |
| 75 | + const char * name; | ||
| 75 | 76 | ||
| 76 | - i = Py_ReprEnter((PyObject *)ns); | ||
| 77 | + name = (Py_TYPE(ns) == &_PyNamespace_Type) ? "namespace" | ||
| 78 | + : ns->ob_type->tp_name; | ||
| 79 | + | ||
| 80 | + i = Py_ReprEnter(ns); | ||
| 77 | 81 | if (i != 0) { | |
| 78 | - return i > 0 ? PyUnicode_FromString("namespace(...)") : NULL; | ||
| 82 | + return i > 0 ? PyUnicode_FromFormat("%s(...)", name) : NULL; | ||
| 79 | 83 | } | |
| 80 | 84 | ||
| 81 | 85 | pairs = PyList_New(0); | |
@@ -127,16 +131,15 @@ namespace_repr(_PyNamespaceObject *ns) | |||
| 127 | 131 | if (pairsrepr == NULL) | |
| 128 | 132 | goto error; | |
| 129 | 133 | ||
| 130 | - repr = PyUnicode_FromFormat("%s(%S)", | ||
| 131 | - ((PyObject *)ns)->ob_type->tp_name, pairsrepr); | ||
| 134 | + repr = PyUnicode_FromFormat("%s(%S)", name, pairsrepr); | ||
| 132 | 135 | Py_DECREF(pairsrepr); | |
| 133 | 136 | ||
| 134 | 137 | error: | |
| 135 | 138 | Py_XDECREF(pairs); | |
| 136 | 139 | Py_XDECREF(d); | |
| 137 | 140 | Py_XDECREF(keys); | |
| 138 | 141 | Py_XDECREF(keys_iter); | |
| 139 | - Py_ReprLeave((PyObject *)ns); | ||
| 142 | + Py_ReprLeave(ns); | ||
| 140 | 143 | ||
| 141 | 144 | return repr; | |
| 142 | 145 | } | |
@@ -158,14 +161,26 @@ namespace_clear(_PyNamespaceObject *ns) | |||
| 158 | 161 | } | |
| 159 | 162 | ||
| 160 | 163 | ||
| 164 | + static PyObject * | ||
| 165 | + namespace_richcompare(PyObject *self, PyObject *other, int op) | ||
| 166 | + { | ||
| 167 | + if (PyObject_IsInstance(self, (PyObject *)&_PyNamespace_Type) && | ||
| 168 | + PyObject_IsInstance(other, (PyObject *)&_PyNamespace_Type)) | ||
| 169 | + return PyObject_RichCompare(((_PyNamespaceObject *)self)->ns_dict, | ||
| 170 | + ((_PyNamespaceObject *)other)->ns_dict, op); | ||
| 171 | + Py_INCREF(Py_NotImplemented); | ||
| 172 | + return Py_NotImplemented; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + | ||
| 161 | 176 | PyDoc_STRVAR(namespace_doc, | |
| 162 | 177 | "A simple attribute-based namespace.\n\ | |
| 163 | 178 | \n\ | |
| 164 | - namespace(**kwargs)"); | ||
| 179 | + SimpleNamespace(**kwargs)"); | ||
| 165 | 180 | ||
| 166 | 181 | PyTypeObject _PyNamespace_Type = { | |
| 167 | 182 | PyVarObject_HEAD_INIT(&PyType_Type, 0) | |
| 168 | - "namespace", /* tp_name */ | ||
| 183 | + "types.SimpleNamespace", /* tp_name */ | ||
| 169 | 184 | sizeof(_PyNamespaceObject), /* tp_size */ | |
| 170 | 185 | 0, /* tp_itemsize */ | |
| 171 | 186 | (destructor)namespace_dealloc, /* tp_dealloc */ | |
@@ -188,7 +203,7 @@ PyTypeObject _PyNamespace_Type = { | |||
| 188 | 203 | namespace_doc, /* tp_doc */ | |
| 189 | 204 | (traverseproc)namespace_traverse, /* tp_traverse */ | |
| 190 | 205 | (inquiry)namespace_clear, /* tp_clear */ | |
| 191 | - 0, /* tp_richcompare */ | ||
| 206 | + namespace_richcompare, /* tp_richcompare */ | ||
| 192 | 207 | 0, /* tp_weaklistoffset */ | |
| 193 | 208 | 0, /* tp_iter */ | |
| 194 | 209 | 0, /* tp_iternext */ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments