UI Code Quality: Cleanup ui_but_update_from_old_block
[blender.git] / source / blender / python / intern / bpy_interface_atexit.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup pythonintern
19  *
20  * This file inserts an exit callback into Python's 'atexit' module.
21  * Without this sys.exit() can crash because blender is not properly closing
22  * resources.
23  */
24
25 #include <Python.h>
26
27 #include "BLI_utildefines.h"
28
29 #include "bpy.h" /* own include */
30 #include "bpy_capi_utils.h"
31
32 #include "WM_api.h"
33
34 static PyObject *bpy_atexit(PyObject *UNUSED(self), PyObject *UNUSED(args), PyObject *UNUSED(kw))
35 {
36   /* close down enough of blender at least not to crash */
37   struct bContext *C = BPY_context_get();
38
39   WM_exit_ex(C, false);
40
41   Py_RETURN_NONE;
42 }
43
44 static PyMethodDef meth_bpy_atexit = {"bpy_atexit", (PyCFunction)bpy_atexit, METH_NOARGS, NULL};
45 static PyObject *func_bpy_atregister = NULL; /* borrowed referebce, atexit holds */
46
47 static void atexit_func_call(const char *func_name, PyObject *atexit_func_arg)
48 {
49   /* note - no error checking, if any of these fail we'll get a crash
50    * this is intended, but if its problematic it could be changed
51    * - campbell */
52
53   PyObject *atexit_mod = PyImport_ImportModuleLevel("atexit", NULL, NULL, NULL, 0);
54   PyObject *atexit_func = PyObject_GetAttrString(atexit_mod, func_name);
55   PyObject *args = PyTuple_New(1);
56   PyObject *ret;
57
58   PyTuple_SET_ITEM(args, 0, atexit_func_arg);
59   Py_INCREF(atexit_func_arg); /* only incref so we don't dec'ref along with 'args' */
60
61   ret = PyObject_CallObject(atexit_func, args);
62
63   Py_DECREF(atexit_mod);
64   Py_DECREF(atexit_func);
65   Py_DECREF(args);
66
67   if (ret) {
68     Py_DECREF(ret);
69   }
70   else { /* should never happen */
71     PyErr_Print();
72   }
73 }
74
75 void BPY_atexit_register(void)
76 {
77   /* atexit module owns this new function reference */
78   BLI_assert(func_bpy_atregister == NULL);
79
80   func_bpy_atregister = (PyObject *)PyCFunction_New(&meth_bpy_atexit, NULL);
81   atexit_func_call("register", func_bpy_atregister);
82 }
83
84 void BPY_atexit_unregister(void)
85 {
86   BLI_assert(func_bpy_atregister != NULL);
87
88   atexit_func_call("unregister", func_bpy_atregister);
89   func_bpy_atregister = NULL; /* don't really need to set but just in case */
90 }