Coverage Report

Created: 2025-07-04 06:49

/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
}