blob: b8a8bf65624a43d1b1b0589d169a9dc3cc01032d [file] [log] [blame]
Collin Winter670e6922007-03-21 02:57:17 +00001/*
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/* ===================================================================== */
12/* Callback machinery. */
13
14typedef struct {
15 PyObject *func;
16 PyObject *args;
17 PyObject *kwargs;
18} atexit_callback;
19
20atexit_callback **atexit_callbacks;
21int ncallbacks = 0;
22int callback_len = 32;
23
24/* Installed into pythonrun.c's atexit mechanism */
25
26void
27atexit_callfuncs(void)
28{
29 PyObject *exc_type = NULL, *exc_value, *exc_tb, *r;
30 atexit_callback *cb;
31 int i;
32
33 if (ncallbacks == 0)
34 return;
35
36 for(i = ncallbacks - 1; i >= 0; i--)
37 {
38 cb = atexit_callbacks[i];
39 if (cb == NULL)
40 continue;
41
42 r = PyObject_Call(cb->func, cb->args, cb->kwargs);
43 Py_XDECREF(r);
44 if (r == NULL) {
45 if (exc_type) {
46 Py_DECREF(exc_type);
47 Py_DECREF(exc_value);
48 Py_DECREF(exc_tb);
49 }
50 PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
51 if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
52 PySys_WriteStderr("Error in atexit._run_exitfuncs:\n");
53 PyErr_Display(exc_type, exc_value, exc_tb);
54 }
55 }
56 }
57
58 if (exc_type)
59 PyErr_Restore(exc_type, exc_value, exc_tb);
60}
61
62void
63atexit_delete_cb(int i)
64{
65 atexit_callback *cb = atexit_callbacks[i];
66 atexit_callbacks[i] = NULL;
67 Py_DECREF(cb->func);
68 Py_DECREF(cb->args);
69 Py_XDECREF(cb->kwargs);
70 PyMem_Free(cb);
71}
72
73/* ===================================================================== */
74/* Module methods. */
75
76PyDoc_STRVAR(atexit_register__doc__,
77"register(func, *args, **kwargs) -> func\n\
78\n\
79Register a function to be executed upon normal program termination\n\
80\n\
81 func - function to be called at exit\n\
82 args - optional arguments to pass to func\n\
83 kwargs - optional keyword arguments to pass to func\n\
84\n\
85 func is returned to facilitate usage as a decorator.");
86
87static PyObject *
88atexit_register(PyObject *self, PyObject *args, PyObject *kwargs)
89{
90 atexit_callback *new_callback;
91 PyObject *func = NULL;
92
93 if (ncallbacks >= callback_len) {
94 callback_len += 16;
95 atexit_callbacks = PyMem_Realloc(atexit_callbacks,
96 sizeof(atexit_callback*) * callback_len);
97
98 }
99
100 if (PyTuple_GET_SIZE(args) == 0) {
101 PyErr_SetString(PyExc_TypeError,
102 "register() takes at least 1 argument (0 given)");
103 return NULL;
104 }
105
106 func = PyTuple_GET_ITEM(args, 0);
107 if (!PyCallable_Check(func)) {
108 PyErr_SetString(PyExc_TypeError,
109 "the first argument must be callable");
110 return NULL;
111 }
112
113 new_callback = PyMem_Malloc(sizeof(atexit_callback));
114 if (new_callback == NULL)
115 return PyErr_NoMemory();
116
117 new_callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
118 if (new_callback->args == NULL) {
119 PyMem_Free(new_callback);
120 return NULL;
121 }
122 new_callback->func = func;
123 new_callback->kwargs = kwargs;
124 Py_INCREF(func);
125 Py_XINCREF(kwargs);
126
127 atexit_callbacks[ncallbacks++] = new_callback;
128
129 Py_INCREF(func);
130 return func;
131}
132
133static PyObject *
134atexit_run_exitfuncs(PyObject *self)
135{
136 atexit_callfuncs();
137 if (PyErr_Occurred())
138 return NULL;
139 Py_RETURN_NONE;
140}
141
142static PyObject *
143atexit_clear(PyObject *self)
144{
145 atexit_callback *cb;
146 int i;
147
148 for(i = 0; i < ncallbacks; i++)
149 {
150 cb = atexit_callbacks[i];
151 if (cb == NULL)
152 continue;
153
154 atexit_delete_cb(i);
155 }
156 ncallbacks = 0;
157 Py_RETURN_NONE;
158}
159
160static PyObject *
161atexit_unregister(PyObject *self, PyObject *func)
162{
163 atexit_callback *cb;
164 int i, eq;
165
166 for(i = 0; i < ncallbacks; i++)
167 {
168 cb = atexit_callbacks[i];
169 if (cb == NULL)
170 continue;
171
172 eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
173 if (eq < 0)
174 return NULL;
175 if (eq)
176 atexit_delete_cb(i);
177 }
178 Py_RETURN_NONE;
179}
180
181static PyMethodDef atexit_methods[] = {
182 {"register", (PyCFunction) atexit_register, METH_VARARGS|METH_KEYWORDS,
183 atexit_register__doc__},
184 {"_clear", (PyCFunction) atexit_clear, METH_NOARGS,
185 NULL},
186 {"unregister", (PyCFunction) atexit_unregister, METH_O,
187 NULL},
188 {"_run_exitfuncs", (PyCFunction) atexit_run_exitfuncs, METH_NOARGS,
189 NULL},
190 {NULL, NULL} /* sentinel */
191};
192
193/* ===================================================================== */
194/* Initialization function. */
195
196PyDoc_STRVAR(atexit__doc__,
197"atexit.py - allow programmer to define multiple exit functions to be executed\
198upon normal program termination.\n\
199\n\
200One public function, register, is defined.\n\
201");
202
203PyMODINIT_FUNC
204initatexit(void)
205{
206 PyObject *m;
207
208 atexit_callbacks = PyMem_New(atexit_callback*, callback_len);
209 if (atexit_callbacks == NULL)
210 return;
211
212 m = Py_InitModule3("atexit", atexit_methods, atexit__doc__);
213 if (m == NULL)
214 return;
215
216 _Py_PyAtExit(atexit_callfuncs);
217}