/src/cpython/Objects/namespaceobject.c
Line | Count | Source (jump to first uncovered line) |
1 | | // namespace object implementation |
2 | | |
3 | | #include "Python.h" |
4 | | #include "pycore_modsupport.h" // _PyArg_NoPositional() |
5 | | #include "pycore_namespace.h" // _PyNamespace_Type |
6 | | |
7 | | #include <stddef.h> // offsetof() |
8 | | |
9 | | |
10 | | typedef struct { |
11 | | PyObject_HEAD |
12 | | PyObject *ns_dict; |
13 | | } _PyNamespaceObject; |
14 | | |
15 | 83.2k | #define _PyNamespace_CAST(op) _Py_CAST(_PyNamespaceObject*, (op)) |
16 | | |
17 | | |
18 | | static PyMemberDef namespace_members[] = { |
19 | | {"__dict__", _Py_T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), Py_READONLY}, |
20 | | {NULL} |
21 | | }; |
22 | | |
23 | | |
24 | | // Methods |
25 | | |
26 | | static PyObject * |
27 | | namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds) |
28 | 250 | { |
29 | 250 | PyObject *self; |
30 | | |
31 | 250 | assert(type != NULL && type->tp_alloc != NULL); |
32 | 250 | self = type->tp_alloc(type, 0); |
33 | 250 | if (self != NULL) { |
34 | 250 | _PyNamespaceObject *ns = (_PyNamespaceObject *)self; |
35 | 250 | ns->ns_dict = PyDict_New(); |
36 | 250 | if (ns->ns_dict == NULL) { |
37 | 0 | Py_DECREF(ns); |
38 | 0 | return NULL; |
39 | 0 | } |
40 | 250 | } |
41 | 250 | return self; |
42 | 250 | } |
43 | | |
44 | | |
45 | | static int |
46 | | namespace_init(PyObject *op, PyObject *args, PyObject *kwds) |
47 | 202 | { |
48 | 202 | _PyNamespaceObject *ns = _PyNamespace_CAST(op); |
49 | 202 | PyObject *arg = NULL; |
50 | 202 | if (!PyArg_UnpackTuple(args, _PyType_Name(Py_TYPE(ns)), 0, 1, &arg)) { |
51 | 0 | return -1; |
52 | 0 | } |
53 | 202 | if (arg != NULL) { |
54 | 0 | PyObject *dict; |
55 | 0 | if (PyDict_CheckExact(arg)) { |
56 | 0 | dict = Py_NewRef(arg); |
57 | 0 | } |
58 | 0 | else { |
59 | 0 | dict = PyObject_CallOneArg((PyObject *)&PyDict_Type, arg); |
60 | 0 | if (dict == NULL) { |
61 | 0 | return -1; |
62 | 0 | } |
63 | 0 | } |
64 | 0 | int err = (!PyArg_ValidateKeywordArguments(dict) || |
65 | 0 | PyDict_Update(ns->ns_dict, dict) < 0); |
66 | 0 | Py_DECREF(dict); |
67 | 0 | if (err) { |
68 | 0 | return -1; |
69 | 0 | } |
70 | 0 | } |
71 | 202 | if (kwds == NULL) { |
72 | 0 | return 0; |
73 | 0 | } |
74 | 202 | if (!PyArg_ValidateKeywordArguments(kwds)) { |
75 | 0 | return -1; |
76 | 0 | } |
77 | 202 | return PyDict_Update(ns->ns_dict, kwds); |
78 | 202 | } |
79 | | |
80 | | |
81 | | static void |
82 | | namespace_dealloc(PyObject *op) |
83 | 16 | { |
84 | 16 | _PyNamespaceObject *ns = _PyNamespace_CAST(op); |
85 | 16 | PyObject_GC_UnTrack(ns); |
86 | 16 | Py_CLEAR(ns->ns_dict); |
87 | 16 | Py_TYPE(ns)->tp_free((PyObject *)ns); |
88 | 16 | } |
89 | | |
90 | | |
91 | | static PyObject * |
92 | | namespace_repr(PyObject *ns) |
93 | 0 | { |
94 | 0 | int i, loop_error = 0; |
95 | 0 | PyObject *pairs = NULL, *d = NULL, *keys = NULL, *keys_iter = NULL; |
96 | 0 | PyObject *key; |
97 | 0 | PyObject *separator, *pairsrepr, *repr = NULL; |
98 | 0 | const char * name; |
99 | |
|
100 | 0 | name = Py_IS_TYPE(ns, &_PyNamespace_Type) ? "namespace" |
101 | 0 | : Py_TYPE(ns)->tp_name; |
102 | |
|
103 | 0 | i = Py_ReprEnter(ns); |
104 | 0 | if (i != 0) { |
105 | 0 | return i > 0 ? PyUnicode_FromFormat("%s(...)", name) : NULL; |
106 | 0 | } |
107 | | |
108 | 0 | pairs = PyList_New(0); |
109 | 0 | if (pairs == NULL) |
110 | 0 | goto error; |
111 | | |
112 | 0 | assert(((_PyNamespaceObject *)ns)->ns_dict != NULL); |
113 | 0 | d = Py_NewRef(((_PyNamespaceObject *)ns)->ns_dict); |
114 | |
|
115 | 0 | keys = PyDict_Keys(d); |
116 | 0 | if (keys == NULL) |
117 | 0 | goto error; |
118 | | |
119 | 0 | keys_iter = PyObject_GetIter(keys); |
120 | 0 | if (keys_iter == NULL) |
121 | 0 | goto error; |
122 | | |
123 | 0 | while ((key = PyIter_Next(keys_iter)) != NULL) { |
124 | 0 | if (PyUnicode_Check(key) && PyUnicode_GET_LENGTH(key) > 0) { |
125 | 0 | PyObject *value, *item; |
126 | |
|
127 | 0 | int has_key = PyDict_GetItemRef(d, key, &value); |
128 | 0 | if (has_key == 1) { |
129 | 0 | item = PyUnicode_FromFormat("%U=%R", key, value); |
130 | 0 | Py_DECREF(value); |
131 | 0 | if (item == NULL) { |
132 | 0 | loop_error = 1; |
133 | 0 | } |
134 | 0 | else { |
135 | 0 | loop_error = PyList_Append(pairs, item); |
136 | 0 | Py_DECREF(item); |
137 | 0 | } |
138 | 0 | } |
139 | 0 | else if (has_key < 0) { |
140 | 0 | loop_error = 1; |
141 | 0 | } |
142 | 0 | } |
143 | |
|
144 | 0 | Py_DECREF(key); |
145 | 0 | if (loop_error) |
146 | 0 | goto error; |
147 | 0 | } |
148 | | |
149 | 0 | if (PyErr_Occurred()) { |
150 | 0 | goto error; |
151 | 0 | } |
152 | | |
153 | 0 | separator = PyUnicode_FromString(", "); |
154 | 0 | if (separator == NULL) |
155 | 0 | goto error; |
156 | | |
157 | 0 | pairsrepr = PyUnicode_Join(separator, pairs); |
158 | 0 | Py_DECREF(separator); |
159 | 0 | if (pairsrepr == NULL) |
160 | 0 | goto error; |
161 | | |
162 | 0 | repr = PyUnicode_FromFormat("%s(%S)", name, pairsrepr); |
163 | 0 | Py_DECREF(pairsrepr); |
164 | |
|
165 | 0 | error: |
166 | 0 | Py_XDECREF(pairs); |
167 | 0 | Py_XDECREF(d); |
168 | 0 | Py_XDECREF(keys); |
169 | 0 | Py_XDECREF(keys_iter); |
170 | 0 | Py_ReprLeave(ns); |
171 | |
|
172 | 0 | return repr; |
173 | 0 | } |
174 | | |
175 | | |
176 | | static int |
177 | | namespace_traverse(PyObject *op, visitproc visit, void *arg) |
178 | 83.0k | { |
179 | 83.0k | _PyNamespaceObject *ns = _PyNamespace_CAST(op); |
180 | 83.0k | Py_VISIT(ns->ns_dict); |
181 | 83.0k | return 0; |
182 | 83.0k | } |
183 | | |
184 | | |
185 | | static int |
186 | | namespace_clear(PyObject *op) |
187 | 0 | { |
188 | 0 | _PyNamespaceObject *ns = _PyNamespace_CAST(op); |
189 | 0 | Py_CLEAR(ns->ns_dict); |
190 | 0 | return 0; |
191 | 0 | } |
192 | | |
193 | | |
194 | | static PyObject * |
195 | | namespace_richcompare(PyObject *self, PyObject *other, int op) |
196 | 0 | { |
197 | 0 | if ( |
198 | 0 | (op == Py_EQ || op == Py_NE) && |
199 | 0 | PyObject_TypeCheck(self, &_PyNamespace_Type) && |
200 | 0 | PyObject_TypeCheck(other, &_PyNamespace_Type) |
201 | 0 | ) { |
202 | 0 | return PyObject_RichCompare(((_PyNamespaceObject *)self)->ns_dict, |
203 | 0 | ((_PyNamespaceObject *)other)->ns_dict, op); |
204 | 0 | } |
205 | 0 | Py_RETURN_NOTIMPLEMENTED; |
206 | 0 | } |
207 | | |
208 | | |
209 | | PyDoc_STRVAR(namespace_reduce__doc__, "Return state information for pickling"); |
210 | | |
211 | | static PyObject * |
212 | | namespace_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) |
213 | 0 | { |
214 | 0 | _PyNamespaceObject *ns = (_PyNamespaceObject*)op; |
215 | 0 | PyObject *result, *args = PyTuple_New(0); |
216 | |
|
217 | 0 | if (!args) |
218 | 0 | return NULL; |
219 | | |
220 | 0 | result = PyTuple_Pack(3, (PyObject *)Py_TYPE(ns), args, ns->ns_dict); |
221 | 0 | Py_DECREF(args); |
222 | 0 | return result; |
223 | 0 | } |
224 | | |
225 | | |
226 | | static PyObject * |
227 | | namespace_replace(PyObject *self, PyObject *args, PyObject *kwargs) |
228 | 0 | { |
229 | 0 | if (!_PyArg_NoPositional("__replace__", args)) { |
230 | 0 | return NULL; |
231 | 0 | } |
232 | | |
233 | 0 | PyObject *result = PyObject_CallNoArgs((PyObject *)Py_TYPE(self)); |
234 | 0 | if (!result) { |
235 | 0 | return NULL; |
236 | 0 | } |
237 | 0 | if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict, |
238 | 0 | ((_PyNamespaceObject*)self)->ns_dict) < 0) |
239 | 0 | { |
240 | 0 | Py_DECREF(result); |
241 | 0 | return NULL; |
242 | 0 | } |
243 | 0 | if (kwargs) { |
244 | 0 | if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict, kwargs) < 0) { |
245 | 0 | Py_DECREF(result); |
246 | 0 | return NULL; |
247 | 0 | } |
248 | 0 | } |
249 | 0 | return result; |
250 | 0 | } |
251 | | |
252 | | |
253 | | static PyMethodDef namespace_methods[] = { |
254 | | {"__reduce__", namespace_reduce, METH_NOARGS, |
255 | | namespace_reduce__doc__}, |
256 | | {"__replace__", _PyCFunction_CAST(namespace_replace), METH_VARARGS|METH_KEYWORDS, |
257 | | PyDoc_STR("__replace__($self, /, **changes)\n--\n\n" |
258 | | "Return a copy of the namespace object with new values for the specified attributes.")}, |
259 | | {NULL, NULL} // sentinel |
260 | | }; |
261 | | |
262 | | |
263 | | PyDoc_STRVAR(namespace_doc, |
264 | | "SimpleNamespace(mapping_or_iterable=(), /, **kwargs)\n\ |
265 | | --\n\n\ |
266 | | A simple attribute-based namespace."); |
267 | | |
268 | | PyTypeObject _PyNamespace_Type = { |
269 | | PyVarObject_HEAD_INIT(&PyType_Type, 0) |
270 | | "types.SimpleNamespace", /* tp_name */ |
271 | | sizeof(_PyNamespaceObject), /* tp_basicsize */ |
272 | | 0, /* tp_itemsize */ |
273 | | namespace_dealloc, /* tp_dealloc */ |
274 | | 0, /* tp_vectorcall_offset */ |
275 | | 0, /* tp_getattr */ |
276 | | 0, /* tp_setattr */ |
277 | | 0, /* tp_as_async */ |
278 | | namespace_repr, /* tp_repr */ |
279 | | 0, /* tp_as_number */ |
280 | | 0, /* tp_as_sequence */ |
281 | | 0, /* tp_as_mapping */ |
282 | | 0, /* tp_hash */ |
283 | | 0, /* tp_call */ |
284 | | 0, /* tp_str */ |
285 | | PyObject_GenericGetAttr, /* tp_getattro */ |
286 | | PyObject_GenericSetAttr, /* tp_setattro */ |
287 | | 0, /* tp_as_buffer */ |
288 | | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | |
289 | | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
290 | | namespace_doc, /* tp_doc */ |
291 | | namespace_traverse, /* tp_traverse */ |
292 | | namespace_clear, /* tp_clear */ |
293 | | namespace_richcompare, /* tp_richcompare */ |
294 | | 0, /* tp_weaklistoffset */ |
295 | | 0, /* tp_iter */ |
296 | | 0, /* tp_iternext */ |
297 | | namespace_methods, /* tp_methods */ |
298 | | namespace_members, /* tp_members */ |
299 | | 0, /* tp_getset */ |
300 | | 0, /* tp_base */ |
301 | | 0, /* tp_dict */ |
302 | | 0, /* tp_descr_get */ |
303 | | 0, /* tp_descr_set */ |
304 | | offsetof(_PyNamespaceObject, ns_dict), /* tp_dictoffset */ |
305 | | namespace_init, /* tp_init */ |
306 | | PyType_GenericAlloc, /* tp_alloc */ |
307 | | namespace_new, /* tp_new */ |
308 | | PyObject_GC_Del, /* tp_free */ |
309 | | }; |
310 | | |
311 | | |
312 | | PyObject * |
313 | | _PyNamespace_New(PyObject *kwds) |
314 | 48 | { |
315 | 48 | PyObject *ns = namespace_new(&_PyNamespace_Type, NULL, NULL); |
316 | 48 | if (ns == NULL) |
317 | 0 | return NULL; |
318 | | |
319 | 48 | if (kwds == NULL) |
320 | 16 | return ns; |
321 | 32 | if (PyDict_Update(((_PyNamespaceObject *)ns)->ns_dict, kwds) != 0) { |
322 | 0 | Py_DECREF(ns); |
323 | 0 | return NULL; |
324 | 0 | } |
325 | | |
326 | 32 | return (PyObject *)ns; |
327 | 32 | } |