Coverage Report

Created: 2025-07-11 06:59

/src/Python-3.8.3/Modules/atexitmodule.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  atexit - allow programmer to define multiple exit functions to be executed
3
 *  upon normal program termination.
4
 *
5
 *   Translated from atexit.py by Collin Winter.
6
 +   Copyright 2007 Python Software Foundation.
7
 */
8
9
#include "Python.h"
10
11
/* Forward declaration (for atexit_cleanup) */
12
static PyObject *atexit_clear(PyObject*, PyObject*);
13
/* Forward declaration of module object */
14
static struct PyModuleDef atexitmodule;
15
16
/* ===================================================================== */
17
/* Callback machinery. */
18
19
typedef struct {
20
    PyObject *func;
21
    PyObject *args;
22
    PyObject *kwargs;
23
} atexit_callback;
24
25
typedef struct {
26
    atexit_callback **atexit_callbacks;
27
    int ncallbacks;
28
    int callback_len;
29
} atexitmodule_state;
30
31
2
#define GET_ATEXIT_STATE(mod) ((atexitmodule_state*)PyModule_GetState(mod))
32
33
34
static void
35
atexit_delete_cb(atexitmodule_state *modstate, int i)
36
0
{
37
0
    atexit_callback *cb;
38
39
0
    cb = modstate->atexit_callbacks[i];
40
0
    modstate->atexit_callbacks[i] = NULL;
41
0
    Py_DECREF(cb->func);
42
0
    Py_DECREF(cb->args);
43
0
    Py_XDECREF(cb->kwargs);
44
0
    PyMem_Free(cb);
45
0
}
46
47
/* Clear all callbacks without calling them */
48
static void
49
atexit_cleanup(atexitmodule_state *modstate)
50
0
{
51
0
    atexit_callback *cb;
52
0
    int i;
53
0
    for (i = 0; i < modstate->ncallbacks; i++) {
54
0
        cb = modstate->atexit_callbacks[i];
55
0
        if (cb == NULL)
56
0
            continue;
57
58
0
        atexit_delete_cb(modstate, i);
59
0
    }
60
0
    modstate->ncallbacks = 0;
61
0
}
62
63
/* Installed into pylifecycle.c's atexit mechanism */
64
65
static void
66
atexit_callfuncs(PyObject *module)
67
0
{
68
0
    PyObject *exc_type = NULL, *exc_value, *exc_tb, *r;
69
0
    atexit_callback *cb;
70
0
    atexitmodule_state *modstate;
71
0
    int i;
72
73
0
    if (module == NULL)
74
0
        return;
75
0
    modstate = GET_ATEXIT_STATE(module);
76
77
0
    if (modstate->ncallbacks == 0)
78
0
        return;
79
80
81
0
    for (i = modstate->ncallbacks - 1; i >= 0; i--)
82
0
    {
83
0
        cb = modstate->atexit_callbacks[i];
84
0
        if (cb == NULL)
85
0
            continue;
86
87
0
        r = PyObject_Call(cb->func, cb->args, cb->kwargs);
88
0
        Py_XDECREF(r);
89
0
        if (r == NULL) {
90
            /* Maintain the last exception, but don't leak if there are
91
               multiple exceptions. */
92
0
            if (exc_type) {
93
0
                Py_DECREF(exc_type);
94
0
                Py_XDECREF(exc_value);
95
0
                Py_XDECREF(exc_tb);
96
0
            }
97
0
            PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
98
0
            if (!PyErr_GivenExceptionMatches(exc_type, PyExc_SystemExit)) {
99
0
                PySys_WriteStderr("Error in atexit._run_exitfuncs:\n");
100
0
                PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb);
101
0
                PyErr_Display(exc_type, exc_value, exc_tb);
102
0
            }
103
0
        }
104
0
    }
105
106
0
    atexit_cleanup(modstate);
107
108
0
    if (exc_type)
109
0
        PyErr_Restore(exc_type, exc_value, exc_tb);
110
0
}
111
112
/* ===================================================================== */
113
/* Module methods. */
114
115
PyDoc_STRVAR(atexit_register__doc__,
116
"register(func, *args, **kwargs) -> func\n\
117
\n\
118
Register a function to be executed upon normal program termination\n\
119
\n\
120
    func - function to be called at exit\n\
121
    args - optional arguments to pass to func\n\
122
    kwargs - optional keyword arguments to pass to func\n\
123
\n\
124
    func is returned to facilitate usage as a decorator.");
125
126
static PyObject *
127
atexit_register(PyObject *self, PyObject *args, PyObject *kwargs)
128
1
{
129
1
    atexitmodule_state *modstate;
130
1
    atexit_callback *new_callback;
131
1
    PyObject *func = NULL;
132
133
1
    modstate = GET_ATEXIT_STATE(self);
134
135
1
    if (modstate->ncallbacks >= modstate->callback_len) {
136
0
        atexit_callback **r;
137
0
        modstate->callback_len += 16;
138
0
        r = (atexit_callback**)PyMem_Realloc(modstate->atexit_callbacks,
139
0
                                      sizeof(atexit_callback*) * modstate->callback_len);
140
0
        if (r == NULL)
141
0
            return PyErr_NoMemory();
142
0
        modstate->atexit_callbacks = r;
143
0
    }
144
145
1
    if (PyTuple_GET_SIZE(args) == 0) {
146
0
        PyErr_SetString(PyExc_TypeError,
147
0
                "register() takes at least 1 argument (0 given)");
148
0
        return NULL;
149
0
    }
150
151
1
    func = PyTuple_GET_ITEM(args, 0);
152
1
    if (!PyCallable_Check(func)) {
153
0
        PyErr_SetString(PyExc_TypeError,
154
0
                "the first argument must be callable");
155
0
        return NULL;
156
0
    }
157
158
1
    new_callback = PyMem_Malloc(sizeof(atexit_callback));
159
1
    if (new_callback == NULL)
160
0
        return PyErr_NoMemory();
161
162
1
    new_callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
163
1
    if (new_callback->args == NULL) {
164
0
        PyMem_Free(new_callback);
165
0
        return NULL;
166
0
    }
167
1
    new_callback->func = func;
168
1
    new_callback->kwargs = kwargs;
169
1
    Py_INCREF(func);
170
1
    Py_XINCREF(kwargs);
171
172
1
    modstate->atexit_callbacks[modstate->ncallbacks++] = new_callback;
173
174
1
    Py_INCREF(func);
175
1
    return func;
176
1
}
177
178
PyDoc_STRVAR(atexit_run_exitfuncs__doc__,
179
"_run_exitfuncs() -> None\n\
180
\n\
181
Run all registered exit functions.");
182
183
static PyObject *
184
atexit_run_exitfuncs(PyObject *self, PyObject *unused)
185
0
{
186
0
    atexit_callfuncs(self);
187
0
    if (PyErr_Occurred())
188
0
        return NULL;
189
0
    Py_RETURN_NONE;
190
0
}
191
192
PyDoc_STRVAR(atexit_clear__doc__,
193
"_clear() -> None\n\
194
\n\
195
Clear the list of previously registered exit functions.");
196
197
static PyObject *
198
atexit_clear(PyObject *self, PyObject *unused)
199
0
{
200
0
    atexit_cleanup(GET_ATEXIT_STATE(self));
201
0
    Py_RETURN_NONE;
202
0
}
203
204
PyDoc_STRVAR(atexit_ncallbacks__doc__,
205
"_ncallbacks() -> int\n\
206
\n\
207
Return the number of registered exit functions.");
208
209
static PyObject *
210
atexit_ncallbacks(PyObject *self, PyObject *unused)
211
0
{
212
0
    atexitmodule_state *modstate;
213
214
0
    modstate = GET_ATEXIT_STATE(self);
215
216
0
    return PyLong_FromSsize_t(modstate->ncallbacks);
217
0
}
218
219
static int
220
atexit_m_traverse(PyObject *self, visitproc visit, void *arg)
221
0
{
222
0
    int i;
223
0
    atexitmodule_state *modstate;
224
225
0
    modstate = GET_ATEXIT_STATE(self);
226
0
    if (modstate != NULL) {
227
0
        for (i = 0; i < modstate->ncallbacks; i++) {
228
0
            atexit_callback *cb = modstate->atexit_callbacks[i];
229
0
            if (cb == NULL)
230
0
                continue;
231
0
            Py_VISIT(cb->func);
232
0
            Py_VISIT(cb->args);
233
0
            Py_VISIT(cb->kwargs);
234
0
        }
235
0
    }
236
0
    return 0;
237
0
}
238
239
static int
240
atexit_m_clear(PyObject *self)
241
0
{
242
0
    atexitmodule_state *modstate;
243
0
    modstate = GET_ATEXIT_STATE(self);
244
0
    if (modstate != NULL) {
245
0
        atexit_cleanup(modstate);
246
0
    }
247
0
    return 0;
248
0
}
249
250
static void
251
atexit_free(PyObject *m)
252
0
{
253
0
    atexitmodule_state *modstate;
254
0
    modstate = GET_ATEXIT_STATE(m);
255
0
    if (modstate != NULL) {
256
0
        atexit_cleanup(modstate);
257
0
        PyMem_Free(modstate->atexit_callbacks);
258
0
    }
259
0
}
260
261
PyDoc_STRVAR(atexit_unregister__doc__,
262
"unregister(func) -> None\n\
263
\n\
264
Unregister an exit function which was previously registered using\n\
265
atexit.register\n\
266
\n\
267
    func - function to be unregistered");
268
269
static PyObject *
270
atexit_unregister(PyObject *self, PyObject *func)
271
0
{
272
0
    atexitmodule_state *modstate;
273
0
    atexit_callback *cb;
274
0
    int i, eq;
275
276
0
    modstate = GET_ATEXIT_STATE(self);
277
278
0
    for (i = 0; i < modstate->ncallbacks; i++)
279
0
    {
280
0
        cb = modstate->atexit_callbacks[i];
281
0
        if (cb == NULL)
282
0
            continue;
283
284
0
        eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
285
0
        if (eq < 0)
286
0
            return NULL;
287
0
        if (eq)
288
0
            atexit_delete_cb(modstate, i);
289
0
    }
290
0
    Py_RETURN_NONE;
291
0
}
292
293
static PyMethodDef atexit_methods[] = {
294
    {"register", (PyCFunction)(void(*)(void)) atexit_register, METH_VARARGS|METH_KEYWORDS,
295
        atexit_register__doc__},
296
    {"_clear", (PyCFunction) atexit_clear, METH_NOARGS,
297
        atexit_clear__doc__},
298
    {"unregister", (PyCFunction) atexit_unregister, METH_O,
299
        atexit_unregister__doc__},
300
    {"_run_exitfuncs", (PyCFunction) atexit_run_exitfuncs, METH_NOARGS,
301
        atexit_run_exitfuncs__doc__},
302
    {"_ncallbacks", (PyCFunction) atexit_ncallbacks, METH_NOARGS,
303
        atexit_ncallbacks__doc__},
304
    {NULL, NULL}        /* sentinel */
305
};
306
307
/* ===================================================================== */
308
/* Initialization function. */
309
310
PyDoc_STRVAR(atexit__doc__,
311
"allow programmer to define multiple exit functions to be executed\
312
upon normal program termination.\n\
313
\n\
314
Two public functions, register and unregister, are defined.\n\
315
");
316
317
static int
318
1
atexit_exec(PyObject *m) {
319
1
    atexitmodule_state *modstate;
320
321
1
    modstate = GET_ATEXIT_STATE(m);
322
1
    modstate->callback_len = 32;
323
1
    modstate->ncallbacks = 0;
324
1
    modstate->atexit_callbacks = PyMem_New(atexit_callback*,
325
1
                                           modstate->callback_len);
326
1
    if (modstate->atexit_callbacks == NULL)
327
0
        return -1;
328
329
1
    _Py_PyAtExit(atexit_callfuncs, m);
330
1
    return 0;
331
1
}
332
333
static PyModuleDef_Slot atexit_slots[] = {
334
    {Py_mod_exec, atexit_exec},
335
    {0, NULL}
336
};
337
338
static struct PyModuleDef atexitmodule = {
339
    PyModuleDef_HEAD_INIT,
340
    "atexit",
341
    atexit__doc__,
342
    sizeof(atexitmodule_state),
343
    atexit_methods,
344
    atexit_slots,
345
    atexit_m_traverse,
346
    atexit_m_clear,
347
    (freefunc)atexit_free
348
};
349
350
PyMODINIT_FUNC
351
PyInit_atexit(void)
352
1
{
353
1
    return PyModuleDef_Init(&atexitmodule);
354
1
}