Coverage Report

Created: 2026-03-23 06:45

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