• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

llnl / dftracer-utils / 27052412546

06 Jun 2026 04:20AM UTC coverage: 50.862% (+1.0%) from 49.905%
27052412546

Pull #73

github

web-flow
Merge 734572730 into 88a3c8457
Pull Request #73: add portable dependencies wheel support

31801 of 79859 branches covered (39.82%)

Branch coverage included in aggregate %.

32491 of 46545 relevant lines covered (69.81%)

9947.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

22.16
/src/dftracer/utils/python/json.cpp
1
#define PY_SSIZE_T_CLEAN
2
#include <Python.h>
3
#include <dftracer/utils/python/json.h>
4

5
using dftracer::utils::utilities::composites::dft::ArgsValueProxy;
6

7
PyObject *args_value_to_pyobject(const ArgsValue &v) {
180✔
8
    return std::visit(
180✔
9
        [](const auto &val) -> PyObject * {
180✔
10
            using T = std::decay_t<decltype(val)>;
11
            if constexpr (std::is_same_v<T, std::monostate>) {
12
                Py_RETURN_NONE;
×
13
            } else if constexpr (std::is_same_v<T, std::string>) {
14
                return PyUnicode_FromStringAndSize(val.data(), val.size());
80✔
15
            } else if constexpr (std::is_same_v<T, std::uint64_t>) {
16
                return PyLong_FromUnsignedLongLong(val);
100✔
17
            } else if constexpr (std::is_same_v<T, std::int64_t>) {
18
                return PyLong_FromLongLong(val);
×
19
            } else if constexpr (std::is_same_v<T, double>) {
20
                return PyFloat_FromDouble(val);
×
21
            } else if constexpr (std::is_same_v<T, bool>) {
22
                return PyBool_FromLong(val ? 1 : 0);
×
23
            } else {
24
                Py_RETURN_NONE;
25
            }
26
        },
27
        v);
180✔
28
}
29

30
static const ArgsMap &get_map(JsonDictValueObject *self) {
80✔
31
    auto &ev = self->batch->events[self->event_index];
80✔
32
    return self->is_args ? ev.args : ev.top;
80!
33
}
34

35
static void JsonDictValue_dealloc(JsonDictValueObject *self) {
240✔
36
    self->batch.reset();
240✔
37
    Py_TYPE(self)->tp_free((PyObject *)self);
240✔
38
}
240✔
39

40
static Py_ssize_t JsonDictValue_length(JsonDictValueObject *self) {
×
41
    const auto &map = get_map(self);
×
42
    Py_ssize_t count = 0;
×
43
    map.for_each_member([&](std::string_view, ArgsValueProxy) { ++count; });
×
44
    if (!self->is_args && get_map(self).exists()) {
×
45
        auto &ev = self->batch->events[self->event_index];
×
46
        if (ev.args.exists()) ++count;
×
47
    }
×
48
    return count;
×
49
}
50

51
static PyObject *JsonDictValue_subscript(JsonDictValueObject *self,
×
52
                                         PyObject *key) {
53
    const char *key_str = PyUnicode_AsUTF8(key);
×
54
    if (!key_str) return NULL;
×
55

56
    std::string_view k(key_str);
×
57

58
    if (!self->is_args && k == "args") {
×
59
        auto &ev = self->batch->events[self->event_index];
×
60
        if (!ev.args.exists()) {
×
61
            Py_RETURN_NONE;
×
62
        }
63
        JsonDictValueObject *obj =
×
64
            (JsonDictValueObject *)JsonDictValueType.tp_alloc(
×
65
                &JsonDictValueType, 0);
66
        if (!obj) return NULL;
×
67
        new (&obj->batch) std::shared_ptr<JsonDictBatch>(self->batch);
×
68
        obj->event_index = self->event_index;
×
69
        obj->is_args = true;
×
70
        return (PyObject *)obj;
×
71
    }
72

73
    const auto &map = get_map(self);
×
74
    auto proxy = map[k];
×
75
    if (!proxy.exists()) {
×
76
        PyErr_SetObject(PyExc_KeyError, key);
×
77
        return NULL;
×
78
    }
79

80
    const auto &raw = map.raw();
×
81
    auto it = raw.find(k);
×
82
    if (it == raw.end()) {
×
83
        PyErr_SetObject(PyExc_KeyError, key);
×
84
        return NULL;
×
85
    }
86
    return args_value_to_pyobject(it->second);
×
87
}
×
88

89
static PyObject *JsonDictValue_keys(JsonDictValueObject *self,
×
90
                                    PyObject *Py_UNUSED(ignored)) {
91
    PyObject *list = PyList_New(0);
×
92
    if (!list) return NULL;
×
93

94
    const auto &map = get_map(self);
×
95
    map.for_each_member([&](std::string_view k, ArgsValueProxy) {
×
96
        PyObject *key = PyUnicode_FromStringAndSize(k.data(), k.size());
×
97
        if (key) {
×
98
            PyList_Append(list, key);
×
99
            Py_DECREF(key);
×
100
        }
×
101
    });
×
102

103
    if (!self->is_args) {
×
104
        auto &ev = self->batch->events[self->event_index];
×
105
        if (ev.args.exists()) {
×
106
            PyObject *args_key = PyUnicode_InternFromString("args");
×
107
            if (args_key) {
×
108
                PyList_Append(list, args_key);
×
109
                Py_DECREF(args_key);
×
110
            }
×
111
        }
×
112
    }
×
113

114
    return list;
×
115
}
×
116

117
static PyObject *JsonDictValue_values(JsonDictValueObject *self,
×
118
                                      PyObject *Py_UNUSED(ignored)) {
119
    PyObject *list = PyList_New(0);
×
120
    if (!list) return NULL;
×
121

122
    const auto &map = get_map(self);
×
123
    for (const auto &[k, v] : map.raw()) {
×
124
        PyObject *val = args_value_to_pyobject(v);
×
125
        if (val) {
×
126
            PyList_Append(list, val);
×
127
            Py_DECREF(val);
×
128
        }
×
129
    }
130

131
    if (!self->is_args) {
×
132
        auto &ev = self->batch->events[self->event_index];
×
133
        if (ev.args.exists()) {
×
134
            JsonDictValueObject *args_obj =
×
135
                (JsonDictValueObject *)JsonDictValueType.tp_alloc(
×
136
                    &JsonDictValueType, 0);
137
            if (args_obj) {
×
138
                new (&args_obj->batch)
×
139
                    std::shared_ptr<JsonDictBatch>(self->batch);
×
140
                args_obj->event_index = self->event_index;
×
141
                args_obj->is_args = true;
×
142
                PyList_Append(list, (PyObject *)args_obj);
×
143
                Py_DECREF(args_obj);
×
144
            }
×
145
        }
×
146
    }
×
147

148
    return list;
×
149
}
×
150

151
static PyObject *JsonDictValue_items(JsonDictValueObject *self,
×
152
                                     PyObject *Py_UNUSED(ignored)) {
153
    PyObject *list = PyList_New(0);
×
154
    if (!list) return NULL;
×
155

156
    const auto &map = get_map(self);
×
157
    for (const auto &[k, v] : map.raw()) {
×
158
        PyObject *key = PyUnicode_FromStringAndSize(k.data(), k.size());
×
159
        PyObject *val = args_value_to_pyobject(v);
×
160
        if (key && val) {
×
161
            PyObject *tuple = PyTuple_Pack(2, key, val);
×
162
            if (tuple) {
×
163
                PyList_Append(list, tuple);
×
164
                Py_DECREF(tuple);
×
165
            }
×
166
        }
×
167
        Py_XDECREF(key);
×
168
        Py_XDECREF(val);
×
169
    }
170

171
    if (!self->is_args) {
×
172
        auto &ev = self->batch->events[self->event_index];
×
173
        if (ev.args.exists()) {
×
174
            PyObject *args_key = PyUnicode_InternFromString("args");
×
175
            JsonDictValueObject *args_obj =
×
176
                (JsonDictValueObject *)JsonDictValueType.tp_alloc(
×
177
                    &JsonDictValueType, 0);
178
            if (args_key && args_obj) {
×
179
                new (&args_obj->batch)
×
180
                    std::shared_ptr<JsonDictBatch>(self->batch);
×
181
                args_obj->event_index = self->event_index;
×
182
                args_obj->is_args = true;
×
183
                PyObject *tuple =
×
184
                    PyTuple_Pack(2, args_key, (PyObject *)args_obj);
×
185
                if (tuple) {
×
186
                    PyList_Append(list, tuple);
×
187
                    Py_DECREF(tuple);
×
188
                }
×
189
            }
×
190
            Py_XDECREF(args_key);
×
191
            Py_XDECREF((PyObject *)args_obj);
×
192
        }
×
193
    }
×
194

195
    return list;
×
196
}
×
197

198
static PyObject *JsonDictValue_get(JsonDictValueObject *self, PyObject *args) {
×
199
    PyObject *key;
200
    PyObject *default_val = Py_None;
×
201
    if (!PyArg_ParseTuple(args, "O|O", &key, &default_val)) return NULL;
×
202

203
    const char *key_str = PyUnicode_AsUTF8(key);
×
204
    if (!key_str) return NULL;
×
205

206
    std::string_view k(key_str);
×
207

208
    if (!self->is_args && k == "args") {
×
209
        auto &ev = self->batch->events[self->event_index];
×
210
        if (!ev.args.exists()) {
×
211
            Py_INCREF(default_val);
×
212
            return default_val;
×
213
        }
214
        JsonDictValueObject *obj =
×
215
            (JsonDictValueObject *)JsonDictValueType.tp_alloc(
×
216
                &JsonDictValueType, 0);
217
        if (!obj) return NULL;
×
218
        new (&obj->batch) std::shared_ptr<JsonDictBatch>(self->batch);
×
219
        obj->event_index = self->event_index;
×
220
        obj->is_args = true;
×
221
        return (PyObject *)obj;
×
222
    }
223

224
    const auto &map = get_map(self);
×
225
    auto it = map.raw().find(k);
×
226
    if (it == map.raw().end()) {
×
227
        Py_INCREF(default_val);
×
228
        return default_val;
×
229
    }
230
    return args_value_to_pyobject(it->second);
×
231
}
×
232

233
static int JsonDictValue_contains(JsonDictValueObject *self, PyObject *key) {
60✔
234
    const char *key_str = PyUnicode_AsUTF8(key);
60✔
235
    if (!key_str) return -1;
60!
236

237
    std::string_view k(key_str);
60✔
238

239
    if (!self->is_args && k == "args") {
60!
240
        auto &ev = self->batch->events[self->event_index];
×
241
        return ev.args.exists() ? 1 : 0;
×
242
    }
243

244
    const auto &map = get_map(self);
60✔
245
    return map[k].exists() ? 1 : 0;
60✔
246
}
60✔
247

248
static PyObject *JsonDictValue_to_dict(JsonDictValueObject *self,
20✔
249
                                       PyObject *Py_UNUSED(ignored)) {
250
    PyObject *dict = PyDict_New();
20✔
251
    if (!dict) return NULL;
20!
252

253
    const auto &map = get_map(self);
20✔
254
    for (const auto &[k, v] : map.raw()) {
160✔
255
        PyObject *key = PyUnicode_FromStringAndSize(k.data(), k.size());
280✔
256
        PyObject *val = args_value_to_pyobject(v);
140✔
257
        if (!key || !val) {
140!
258
            Py_XDECREF(key);
×
259
            Py_XDECREF(val);
×
260
            Py_DECREF(dict);
×
261
            return NULL;
×
262
        }
263
        PyDict_SetItem(dict, key, val);
140✔
264
        Py_DECREF(key);
140✔
265
        Py_DECREF(val);
140✔
266
    }
267

268
    if (!self->is_args) {
20!
269
        auto &ev = self->batch->events[self->event_index];
20✔
270
        if (ev.args.exists()) {
20!
271
            PyObject *args_dict = PyDict_New();
20✔
272
            if (!args_dict) {
20!
273
                Py_DECREF(dict);
×
274
                return NULL;
×
275
            }
276
            for (const auto &[k, v] : ev.args.raw()) {
60✔
277
                PyObject *key = PyUnicode_FromStringAndSize(k.data(), k.size());
80✔
278
                PyObject *val = args_value_to_pyobject(v);
40✔
279
                if (!key || !val) {
40!
280
                    Py_XDECREF(key);
×
281
                    Py_XDECREF(val);
×
282
                    Py_DECREF(args_dict);
×
283
                    Py_DECREF(dict);
×
284
                    return NULL;
×
285
                }
286
                PyDict_SetItem(args_dict, key, val);
40✔
287
                Py_DECREF(key);
40✔
288
                Py_DECREF(val);
40✔
289
            }
290
            PyDict_SetItemString(dict, "args", args_dict);
20✔
291
            Py_DECREF(args_dict);
20✔
292
        }
20✔
293
    }
20✔
294

295
    return dict;
20✔
296
}
20✔
297

298
static PyMappingMethods JsonDictValue_as_mapping = {
299
    (lenfunc)JsonDictValue_length,
300
    (binaryfunc)JsonDictValue_subscript,
301
    NULL,
302
};
303

304
static PySequenceMethods JsonDictValue_as_sequence = {
305
    NULL, NULL, NULL, NULL,
306
    NULL, NULL, NULL, (objobjproc)JsonDictValue_contains,
307
    NULL, NULL,
308
};
309

310
static PyMethodDef JsonDictValue_methods[] = {
311
    {"keys", (PyCFunction)JsonDictValue_keys, METH_NOARGS,
312
     "Return list of keys."},
313
    {"values", (PyCFunction)JsonDictValue_values, METH_NOARGS,
314
     "Return list of values."},
315
    {"items", (PyCFunction)JsonDictValue_items, METH_NOARGS,
316
     "Return list of (key, value) pairs."},
317
    {"get", (PyCFunction)JsonDictValue_get, METH_VARARGS,
318
     "Get value by key with optional default."},
319
    {"to_dict", (PyCFunction)JsonDictValue_to_dict, METH_NOARGS,
320
     "Convert to a regular Python dict."},
321
    {NULL}};
322

323
PyTypeObject JsonDictValueType = {
324
    PyVarObject_HEAD_INIT(NULL, 0) "dftracer_utils_ext.JsonDictValue",
325
    sizeof(JsonDictValueObject),       /* tp_basicsize */
326
    0,                                 /* tp_itemsize */
327
    (destructor)JsonDictValue_dealloc, /* tp_dealloc */
328
    0,                                 /* tp_vectorcall_offset */
329
    0,                                 /* tp_getattr */
330
    0,                                 /* tp_setattr */
331
    0,                                 /* tp_as_async */
332
    0,                                 /* tp_repr */
333
    0,                                 /* tp_as_number */
334
    &JsonDictValue_as_sequence,        /* tp_as_sequence */
335
    &JsonDictValue_as_mapping,         /* tp_as_mapping */
336
    0,                                 /* tp_hash */
337
    0,                                 /* tp_call */
338
    0,                                 /* tp_str */
339
    0,                                 /* tp_getattro */
340
    0,                                 /* tp_setattro */
341
    0,                                 /* tp_as_buffer */
342
    Py_TPFLAGS_DEFAULT,                /* tp_flags */
343
    "Zero-copy wrapper over a parsed DFTracer JSON event.\n"
344
    "Supports dict-like access: event['name'], event['args']['ret'].\n"
345
    "Call .to_dict() to materialize a regular Python dict.",
346
    0,                     /* tp_traverse */
347
    0,                     /* tp_clear */
348
    0,                     /* tp_richcompare */
349
    0,                     /* tp_weaklistoffset */
350
    0,                     /* tp_iter */
351
    0,                     /* tp_iternext */
352
    JsonDictValue_methods, /* tp_methods */
353
};
354

355
int init_json_dict_value(PyObject *m) {
1✔
356
    if (PyType_Ready(&JsonDictValueType) < 0) return -1;
1!
357
    Py_INCREF(&JsonDictValueType);
1✔
358
    if (PyModule_AddObject(m, "JsonDictValue", (PyObject *)&JsonDictValueType) <
1!
359
        0) {
360
        Py_DECREF(&JsonDictValueType);
×
361
        return -1;
×
362
    }
363
    return 0;
1✔
364
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc