py api was raising SystemError exception incorrectly, this is intended for internal...
[blender.git] / source / blender / python / intern / bpy_util.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * Contributor(s): Campbell Barton
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 #include <Python.h>
26
27 #include "bpy_util.h"
28 #include "BLI_dynstr.h"
29 #include "MEM_guardedalloc.h"
30 #include "BKE_report.h"
31 #include "BKE_context.h"
32
33 #include "../generic/py_capi_utils.h"
34
35 static bContext*        __py_context = NULL;
36 bContext*       BPy_GetContext(void) { return __py_context; }
37 void            BPy_SetContext(bContext *C) { __py_context= C; }
38
39 int BPY_class_validate(const char *class_type, PyObject *class, PyObject *base_class, BPY_class_attr_check* class_attrs, PyObject **py_class_attrs)
40 {
41         PyObject *item, *fitem;
42         PyObject *py_arg_count;
43         int i, arg_count;
44
45         if (base_class) {
46                 if (!PyObject_IsSubclass(class, base_class)) {
47                         PyObject *name= PyObject_GetAttrString(base_class, "__name__");
48                         PyErr_Format(PyExc_AttributeError, "expected %s subclass of class \"%s\"", class_type, name ? _PyUnicode_AsString(name):"<UNKNOWN>");
49                         Py_XDECREF(name);
50                         return -1;
51                 }
52         }
53         
54         for(i= 0;class_attrs->name; class_attrs++, i++) {
55                 item = PyObject_GetAttrString(class, class_attrs->name);
56
57                 if (py_class_attrs)
58                         py_class_attrs[i]= item;
59                 
60                 if (item==NULL) {
61                         if ((class_attrs->flag & BPY_CLASS_ATTR_OPTIONAL)==0) {
62                                 PyErr_Format(PyExc_AttributeError, "expected %s class to have an \"%s\" attribute", class_type, class_attrs->name);
63                                 return -1;
64                         }
65
66                         PyErr_Clear();
67                 }
68                 else {
69                         Py_DECREF(item); /* no need to keep a ref, the class owns it */
70
71                         if((item==Py_None) && (class_attrs->flag & BPY_CLASS_ATTR_NONE_OK)) {
72                                 /* dont do anything, this is ok, dont bother checking other types */
73                         }
74                         else {
75                                 switch(class_attrs->type) {
76                                 case 's':
77                                         if (PyUnicode_Check(item)==0) {
78                                                 PyErr_Format(PyExc_AttributeError, "expected %s class \"%s\" attribute to be a string", class_type, class_attrs->name);
79                                                 return -1;
80                                         }
81                                         if(class_attrs->len != -1 && class_attrs->len < PyUnicode_GetSize(item)) {
82                                                 PyErr_Format(PyExc_AttributeError, "expected %s class \"%s\" attribute string to be shorter then %d", class_type, class_attrs->name, class_attrs->len);
83                                                 return -1;
84                                         }
85
86                                         break;
87                                 case 'l':
88                                         if (PyList_Check(item)==0) {
89                                                 PyErr_Format(PyExc_AttributeError, "expected %s class \"%s\" attribute to be a list", class_type, class_attrs->name);
90                                                 return -1;
91                                         }
92                                         if(class_attrs->len != -1 && class_attrs->len < PyList_GET_SIZE(item)) {
93                                                 PyErr_Format(PyExc_AttributeError, "expected %s class \"%s\" attribute list to be shorter then %d", class_type, class_attrs->name, class_attrs->len);
94                                                 return -1;
95                                         }
96                                         break;
97                                 case 'f':
98                                         if (PyMethod_Check(item))
99                                                 fitem= PyMethod_Function(item); /* py 2.x */
100                                         else
101                                                 fitem= item; /* py 3.x */
102
103                                         if (PyFunction_Check(fitem)==0) {
104                                                 PyErr_Format(PyExc_AttributeError, "expected %s class \"%s\" attribute to be a function", class_type, class_attrs->name);
105                                                 return -1;
106                                         }
107                                         if (class_attrs->arg_count >= 0) { /* -1 if we dont care*/
108                                                 py_arg_count = PyObject_GetAttrString(PyFunction_GET_CODE(fitem), "co_argcount");
109                                                 arg_count = PyLong_AsSsize_t(py_arg_count);
110                                                 Py_DECREF(py_arg_count);
111
112                                                 if (arg_count != class_attrs->arg_count) {
113                                                         PyErr_Format(PyExc_AttributeError, "expected %s class \"%s\" function to have %d args", class_type, class_attrs->name, class_attrs->arg_count);
114                                                         return -1;
115                                                 }
116                                         }
117                                         break;
118                                 }
119                         }
120                 }
121         }
122         return 0;
123 }
124
125
126
127 char *BPy_enum_as_string(EnumPropertyItem *item)
128 {
129         DynStr *dynstr= BLI_dynstr_new();
130         EnumPropertyItem *e;
131         char *cstring;
132
133         for (e= item; item->identifier; item++) {
134                 if(item->identifier[0])
135                         BLI_dynstr_appendf(dynstr, (e==item)?"'%s'":", '%s'", item->identifier);
136         }
137
138         cstring = BLI_dynstr_get_cstring(dynstr);
139         BLI_dynstr_free(dynstr);
140         return cstring;
141 }
142
143 short BPy_reports_to_error(ReportList *reports, const short clear)
144 {
145         char *report_str;
146
147         report_str= BKE_reports_string(reports, RPT_ERROR);
148
149         if(clear) {
150                 BKE_reports_clear(reports);
151         }
152
153         if(report_str) {
154                 PyErr_SetString(PyExc_RuntimeError, report_str);
155                 MEM_freeN(report_str);
156         }
157
158         return (report_str != NULL);
159 }
160
161
162 short BPy_errors_to_report(ReportList *reports)
163 {
164         PyObject *pystring;
165         PyObject *pystring_format= NULL; // workaround, see below
166         char *cstring;
167
168         const char *filename;
169         int lineno;
170
171         if (!PyErr_Occurred())
172                 return 1;
173         
174         /* less hassle if we allow NULL */
175         if(reports==NULL) {
176                 PyErr_Print();
177                 PyErr_Clear();
178                 return 1;
179         }
180         
181         pystring= PyC_ExceptionBuffer();
182         
183         if(pystring==NULL) {
184                 BKE_report(reports, RPT_ERROR, "unknown py-exception, could not convert");
185                 return 0;
186         }
187         
188         PyC_FileAndNum(&filename, &lineno);
189         if(filename==NULL)
190                 filename= "<unknown location>";
191         
192         cstring= _PyUnicode_AsString(pystring);
193
194 #if 0 // ARG!. workaround for a bug in blenders use of vsnprintf
195         BKE_reportf(reports, RPT_ERROR, "%s\nlocation:%s:%d\n", cstring, filename, lineno);
196 #else
197         pystring_format= PyUnicode_FromFormat("%s\nlocation:%s:%d\n", cstring, filename, lineno);
198         cstring= _PyUnicode_AsString(pystring_format);
199         BKE_report(reports, RPT_ERROR, cstring);
200 #endif
201         
202         fprintf(stderr, "%s\nlocation:%s:%d\n", cstring, filename, lineno); // not exactly needed. just for testing
203         
204         Py_DECREF(pystring);
205         Py_DECREF(pystring_format); // workaround
206         return 1;
207 }
208
209 /* array utility function */
210 int PyC_AsArray(void *array, PyObject *value, int length, PyTypeObject *type, const char *error_prefix)
211 {
212         PyObject *value_fast;
213         int value_len;
214         int i;
215
216         if(!(value_fast=PySequence_Fast(value, error_prefix))) {
217                 return -1;
218         }
219
220         value_len= PySequence_Fast_GET_SIZE(value_fast);
221
222         if(value_len != length) {
223                 Py_DECREF(value);
224                 PyErr_Format(PyExc_TypeError, "%.200s: invalid sequence length. expected %d, got %d", error_prefix, length, value_len);
225                 return -1;
226         }
227
228         /* for each type */
229         if(type == &PyFloat_Type) {
230                 float *array_float= array;
231                 for(i=0; i<length; i++) {
232                         array_float[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i));
233                 }
234         }
235         else if(type == &PyLong_Type) {
236                 int *array_int= array;
237                 for(i=0; i<length; i++) {
238                         array_int[i] = PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i));
239                 }
240         }
241         else if(type == &PyBool_Type) {
242                 int *array_bool= array;
243                 for(i=0; i<length; i++) {
244                         array_bool[i] = (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i)) != 0);
245                 }
246         }
247         else {
248                 Py_DECREF(value_fast);
249                 PyErr_Format(PyExc_TypeError, "%s: internal error %s is invalid", error_prefix, type->tp_name);
250                 return -1;
251         }
252
253         Py_DECREF(value_fast);
254
255         if(PyErr_Occurred()) {
256                 PyErr_Format(PyExc_TypeError, "%s: one or more items could not be used as a %s", error_prefix, type->tp_name);
257                 return -1;
258         }
259
260         return 0;
261 }