BGE patch #28476: Character object physics type
[blender.git] / source / blender / python / generic / py_capi_utils.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/python/generic/py_capi_utils.c
22  *  \ingroup pygen
23  *
24  * Extend upon CPython's API, filling in some gaps, these functions use PyC_
25  * prefix to distinguish them apart from CPython.
26  *
27  * \note
28  * This module should only depend on CPython, however it currently uses
29  * BLI_string_utf8() for unicode conversion.
30  */
31
32
33 #include <Python.h>
34 #include <frameobject.h>
35
36 #include "py_capi_utils.h"
37
38 /* only for BLI_strncpy_wchar_from_utf8, should replace with py funcs but too late in release now */
39 #include "BLI_string_utf8.h"
40
41 #ifdef _WIN32 /* BLI_setenv */
42 #include "BLI_path_util.h"
43 #endif
44
45 /* array utility function */
46 int PyC_AsArray(void *array, PyObject *value, const Py_ssize_t length,
47                 const PyTypeObject *type, const short is_double, const char *error_prefix)
48 {
49         PyObject *value_fast;
50         Py_ssize_t value_len;
51         Py_ssize_t i;
52
53         if (!(value_fast = PySequence_Fast(value, error_prefix))) {
54                 return -1;
55         }
56
57         value_len = PySequence_Fast_GET_SIZE(value_fast);
58
59         if (value_len != length) {
60                 Py_DECREF(value);
61                 PyErr_Format(PyExc_TypeError,
62                              "%.200s: invalid sequence length. expected %d, got %d",
63                              error_prefix, length, value_len);
64                 return -1;
65         }
66
67         /* for each type */
68         if (type == &PyFloat_Type) {
69                 if (is_double) {
70                         double *array_double = array;
71                         for (i = 0; i < length; i++) {
72                                 array_double[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i));
73                         }
74                 }
75                 else {
76                         float *array_float = array;
77                         for (i = 0; i < length; i++) {
78                                 array_float[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i));
79                         }
80                 }
81         }
82         else if (type == &PyLong_Type) {
83                 /* could use is_double for 'long int' but no use now */
84                 int *array_int = array;
85                 for (i = 0; i < length; i++) {
86                         array_int[i] = PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i));
87                 }
88         }
89         else if (type == &PyBool_Type) {
90                 int *array_bool = array;
91                 for (i = 0; i < length; i++) {
92                         array_bool[i] = (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value_fast, i)) != 0);
93                 }
94         }
95         else {
96                 Py_DECREF(value_fast);
97                 PyErr_Format(PyExc_TypeError,
98                              "%s: internal error %s is invalid",
99                              error_prefix, type->tp_name);
100                 return -1;
101         }
102
103         Py_DECREF(value_fast);
104
105         if (PyErr_Occurred()) {
106                 PyErr_Format(PyExc_TypeError,
107                              "%s: one or more items could not be used as a %s",
108                              error_prefix, type->tp_name);
109                 return -1;
110         }
111
112         return 0;
113 }
114
115
116 /* for debugging */
117 void PyC_ObSpit(const char *name, PyObject *var)
118 {
119         fprintf(stderr, "<%s> : ", name);
120         if (var == NULL) {
121                 fprintf(stderr, "<NIL>");
122         }
123         else {
124                 PyObject_Print(var, stderr, 0);
125                 fprintf(stderr, " ref:%d ", (int)var->ob_refcnt);
126                 fprintf(stderr, " ptr:%p", (void *)var);
127                 
128                 fprintf(stderr, " type:");
129                 if (Py_TYPE(var))
130                         fprintf(stderr, "%s", Py_TYPE(var)->tp_name);
131                 else
132                         fprintf(stderr, "<NIL>");
133         }
134         fprintf(stderr, "\n");
135 }
136
137 void PyC_LineSpit(void)
138 {
139
140         const char *filename;
141         int lineno;
142
143         /* Note, allow calling from outside python (RNA) */
144         if (!PYC_INTERPRETER_ACTIVE) {
145                 fprintf(stderr, "python line lookup failed, interpreter inactive\n");
146                 return;
147         }
148
149         PyErr_Clear();
150         PyC_FileAndNum(&filename, &lineno);
151         
152         fprintf(stderr, "%s:%d\n", filename, lineno);
153 }
154
155 void PyC_FileAndNum(const char **filename, int *lineno)
156 {
157         PyFrameObject *frame;
158         
159         if (filename) *filename = NULL;
160         if (lineno)   *lineno = -1;
161
162         if (!(frame = PyThreadState_GET()->frame)) {
163                 return;
164         }
165
166         /* when executing a script */
167         if (filename) {
168                 *filename = _PyUnicode_AsString(frame->f_code->co_filename);
169         }
170
171         /* when executing a module */
172         if (filename && *filename == NULL) {
173                 /* try an alternative method to get the filename - module based
174                  * references below are all borrowed (double checked) */
175                 PyObject *mod_name = PyDict_GetItemString(PyEval_GetGlobals(), "__name__");
176                 if (mod_name) {
177                         PyObject *mod = PyDict_GetItem(PyImport_GetModuleDict(), mod_name);
178                         if (mod) {
179                                 *filename = PyModule_GetFilename(mod);
180                         }
181
182                         /* unlikely, fallback */
183                         if (*filename == NULL) {
184                                 *filename = _PyUnicode_AsString(mod_name);
185                         }
186                 }
187         }
188
189         if (lineno) {
190                 *lineno = PyFrame_GetLineNumber(frame);
191         }
192 }
193
194 void PyC_FileAndNum_Safe(const char **filename, int *lineno)
195 {
196         if (!PYC_INTERPRETER_ACTIVE) {
197                 return;
198         }
199
200         PyC_FileAndNum(filename, lineno);
201 }
202
203 /* Would be nice if python had this built in */
204 PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...)
205 {
206         Py_ssize_t i;
207         PyObject *item = o;
208         char *attr;
209         
210         va_list vargs;
211
212         va_start(vargs, n);
213         for (i = 0; i < n; i++) {
214                 attr = va_arg(vargs, char *);
215                 item = PyObject_GetAttrString(item, attr);
216                 
217                 if (item) 
218                         Py_DECREF(item);
219                 else /* python will set the error value here */
220                         break;
221                 
222         }
223         va_end(vargs);
224         
225         Py_XINCREF(item); /* final value has is increfed, to match PyObject_GetAttrString */
226         return item;
227 }
228
229 /* similar to PyErr_Format(),
230  *
231  * implementation - we cant actually preprend the existing exception,
232  * because it could have _any_ argiments given to it, so instead we get its
233  * __str__ output and raise our own exception including it.
234  */
235 PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *format, ...)
236 {
237         PyObject *error_value_prefix;
238         va_list args;
239
240         va_start(args, format);
241         error_value_prefix = PyUnicode_FromFormatV(format, args); /* can fail and be NULL */
242         va_end(args);
243
244         if (PyErr_Occurred()) {
245                 PyObject *error_type, *error_value, *error_traceback;
246                 PyErr_Fetch(&error_type, &error_value, &error_traceback);
247                 PyErr_Format(exception_type_prefix,
248                              "%S, %.200s(%S)",
249                              error_value_prefix,
250                              Py_TYPE(error_value)->tp_name,
251                              error_value
252                              );
253         }
254         else {
255                 PyErr_SetObject(exception_type_prefix,
256                                 error_value_prefix
257                                 );
258         }
259
260         Py_XDECREF(error_value_prefix);
261
262         /* dumb to always return NULL but matches PyErr_Format */
263         return NULL;
264 }
265
266
267 /* returns the exception string as a new PyUnicode object, depends on external traceback module */
268 #if 0
269
270 /* this version uses traceback module but somehow fails on UI errors */
271
272 PyObject *PyC_ExceptionBuffer(void)
273 {
274         PyObject *traceback_mod = NULL;
275         PyObject *format_tb_func = NULL;
276         PyObject *ret = NULL;
277
278         if (!(traceback_mod = PyImport_ImportModule("traceback"))) {
279                 goto error_cleanup;
280         }
281         else if (!(format_tb_func = PyObject_GetAttrString(traceback_mod, "format_exc"))) {
282                 goto error_cleanup;
283         }
284
285         ret = PyObject_CallObject(format_tb_func, NULL);
286
287         if (ret == Py_None) {
288                 Py_DECREF(ret);
289                 ret = NULL;
290         }
291
292 error_cleanup:
293         /* could not import the module so print the error and close */
294         Py_XDECREF(traceback_mod);
295         Py_XDECREF(format_tb_func);
296
297         return ret;
298 }
299 #else /* verbose, non-threadsafe version */
300 PyObject *PyC_ExceptionBuffer(void)
301 {
302         PyObject *stdout_backup = PySys_GetObject("stdout"); /* borrowed */
303         PyObject *stderr_backup = PySys_GetObject("stderr"); /* borrowed */
304         PyObject *string_io = NULL;
305         PyObject *string_io_buf = NULL;
306         PyObject *string_io_mod = NULL;
307         PyObject *string_io_getvalue = NULL;
308
309         PyObject *error_type, *error_value, *error_traceback;
310
311         if (!PyErr_Occurred())
312                 return NULL;
313
314         PyErr_Fetch(&error_type, &error_value, &error_traceback);
315
316         PyErr_Clear();
317
318         /* import io
319          * string_io = io.StringIO()
320          */
321
322         if (!(string_io_mod = PyImport_ImportModule("io"))) {
323                 goto error_cleanup;
324         }
325         else if (!(string_io = PyObject_CallMethod(string_io_mod, (char *)"StringIO", NULL))) {
326                 goto error_cleanup;
327         }
328         else if (!(string_io_getvalue = PyObject_GetAttrString(string_io, "getvalue"))) {
329                 goto error_cleanup;
330         }
331
332         Py_INCREF(stdout_backup); // since these were borrowed we don't want them freed when replaced.
333         Py_INCREF(stderr_backup);
334
335         PySys_SetObject("stdout", string_io); // both of these are freed when restoring
336         PySys_SetObject("stderr", string_io);
337
338         PyErr_Restore(error_type, error_value, error_traceback);
339         PyErr_Print(); /* print the error */
340         PyErr_Clear();
341
342         string_io_buf = PyObject_CallObject(string_io_getvalue, NULL);
343
344         PySys_SetObject("stdout", stdout_backup);
345         PySys_SetObject("stderr", stderr_backup);
346
347         Py_DECREF(stdout_backup); /* now sys owns the ref again */
348         Py_DECREF(stderr_backup);
349
350         Py_DECREF(string_io_mod);
351         Py_DECREF(string_io_getvalue);
352         Py_DECREF(string_io); /* free the original reference */
353
354         PyErr_Clear();
355         return string_io_buf;
356
357
358 error_cleanup:
359         /* could not import the module so print the error and close */
360         Py_XDECREF(string_io_mod);
361         Py_XDECREF(string_io);
362
363         PyErr_Restore(error_type, error_value, error_traceback);
364         PyErr_Print(); /* print the error */
365         PyErr_Clear();
366
367         return NULL;
368 }
369 #endif
370
371
372 /* string conversion, escape non-unicode chars, coerce must be set to NULL */
373 const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce)
374 {
375         const char *result;
376
377         result = _PyUnicode_AsString(py_str);
378
379         if (result) {
380                 /* 99% of the time this is enough but we better support non unicode
381                  * chars since blender doesnt limit this */
382                 return result;
383         }
384         else {
385                 PyErr_Clear();
386
387                 if (PyBytes_Check(py_str)) {
388                         return PyBytes_AS_STRING(py_str);
389                 }
390                 else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) {
391                         return PyBytes_AS_STRING(*coerce);
392                 }
393                 else {
394                         /* leave error raised from EncodeFS */
395                         return NULL;
396                 }
397         }
398 }
399
400 PyObject *PyC_UnicodeFromByteAndSize(const char *str, Py_ssize_t size)
401 {
402         PyObject *result = PyUnicode_FromStringAndSize(str, size);
403         if (result) {
404                 /* 99% of the time this is enough but we better support non unicode
405                  * chars since blender doesnt limit this */
406                 return result;
407         }
408         else {
409                 PyErr_Clear();
410                 /* this means paths will always be accessible once converted, on all OS's */
411                 result = PyUnicode_DecodeFSDefaultAndSize(str, size);
412                 return result;
413         }
414 }
415
416 PyObject *PyC_UnicodeFromByte(const char *str)
417 {
418         return PyC_UnicodeFromByteAndSize(str, strlen(str));
419 }
420
421 /*****************************************************************************
422  * Description: This function creates a new Python dictionary object.
423  * note: dict is owned by sys.modules["__main__"] module, reference is borrowed
424  * note: important we use the dict from __main__, this is what python expects
425  *  for 'pickle' to work as well as strings like this...
426  * >> foo = 10
427  * >> print(__import__("__main__").foo)
428  *
429  * note: this overwrites __main__ which gives problems with nested calles.
430  * be sure to run PyC_MainModule_Backup & PyC_MainModule_Restore if there is
431  * any chance that python is in the call stack.
432  ****************************************************************************/
433 PyObject *PyC_DefaultNameSpace(const char *filename)
434 {
435         PyInterpreterState *interp = PyThreadState_GET()->interp;
436         PyObject *mod_main = PyModule_New("__main__");
437         PyDict_SetItemString(interp->modules, "__main__", mod_main);
438         Py_DECREF(mod_main); /* sys.modules owns now */
439         PyModule_AddStringConstant(mod_main, "__name__", "__main__");
440         if (filename) {
441                 /* __file__ mainly for nice UI'ness */
442                 PyModule_AddObject(mod_main, "__file__", PyUnicode_DecodeFSDefault(filename));
443         }
444         PyModule_AddObject(mod_main, "__builtins__", interp->builtins);
445         Py_INCREF(interp->builtins); /* AddObject steals a reference */
446         return PyModule_GetDict(mod_main);
447 }
448
449 /* restore MUST be called after this */
450 void PyC_MainModule_Backup(PyObject **main_mod)
451 {
452         PyInterpreterState *interp = PyThreadState_GET()->interp;
453         *main_mod = PyDict_GetItemString(interp->modules, "__main__");
454         Py_XINCREF(*main_mod); /* don't free */
455 }
456
457 void PyC_MainModule_Restore(PyObject *main_mod)
458 {
459         PyInterpreterState *interp = PyThreadState_GET()->interp;
460         PyDict_SetItemString(interp->modules, "__main__", main_mod);
461         Py_XDECREF(main_mod);
462 }
463
464 /* must be called before Py_Initialize, expects output of BLI_get_folder(BLENDER_PYTHON, NULL) */
465 void PyC_SetHomePath(const char *py_path_bundle)
466 {
467         if (py_path_bundle == NULL) {
468                 /* Common enough to have bundled *nix python but complain on OSX/Win */
469 #if defined(__APPLE__) || defined(_WIN32)
470                 fprintf(stderr, "Warning! bundled python not found and is expected on this platform. "
471                         "(if you built with CMake: 'install' target may have not been built)\n");
472 #endif
473                 return;
474         }
475         /* set the environment path */
476         printf("found bundled python: %s\n", py_path_bundle);
477
478 #ifdef __APPLE__
479         /* OSX allow file/directory names to contain : character (represented as / in the Finder)
480          * but current Python lib (release 3.1.1) doesn't handle these correctly */
481         if (strchr(py_path_bundle, ':'))
482                 printf("Warning : Blender application is located in a path containing : or / chars\
483                            \nThis may make python import function fail\n");
484 #endif
485
486
487 #if 0 /* disable for now [#31506] - campbell */
488 #ifdef _WIN32
489         /* cmake/MSVC debug build crashes without this, why only
490          * in this case is unknown.. */
491         {
492                 /*BLI_setenv("PYTHONPATH", py_path_bundle)*/;
493         }
494 #endif
495 #endif
496
497         {
498                 static wchar_t py_path_bundle_wchar[1024];
499
500                 /* cant use this, on linux gives bug: #23018, TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 22008 */
501                 /* mbstowcs(py_path_bundle_wchar, py_path_bundle, FILE_MAXDIR); */
502
503                 BLI_strncpy_wchar_from_utf8(py_path_bundle_wchar, py_path_bundle,
504                                             sizeof(py_path_bundle_wchar) / sizeof(wchar_t));
505
506                 Py_SetPythonHome(py_path_bundle_wchar);
507                 // printf("found python (wchar_t) '%ls'\n", py_path_bundle_wchar);
508         }
509 }
510
511 /* Would be nice if python had this built in */
512 void PyC_RunQuicky(const char *filepath, int n, ...)
513 {
514         FILE *fp = fopen(filepath, "r");
515
516         if (fp) {
517                 PyGILState_STATE gilstate = PyGILState_Ensure();
518
519                 va_list vargs;  
520
521                 int *sizes = PyMem_MALLOC(sizeof(int) * (n / 2));
522                 int i;
523
524                 PyObject *py_dict = PyC_DefaultNameSpace(filepath);
525                 PyObject *values = PyList_New(n / 2); /* namespace owns this, don't free */
526
527                 PyObject *py_result, *ret;
528
529                 PyObject *struct_mod = PyImport_ImportModule("struct");
530                 PyObject *calcsize = PyObject_GetAttrString(struct_mod, "calcsize"); /* struct.calcsize */
531                 PyObject *pack = PyObject_GetAttrString(struct_mod, "pack"); /* struct.pack */
532                 PyObject *unpack = PyObject_GetAttrString(struct_mod, "unpack"); /* struct.unpack */
533
534                 Py_DECREF(struct_mod);
535
536                 va_start(vargs, n);
537                 for (i = 0; i * 2 < n; i++) {
538                         char *format = va_arg(vargs, char *);
539                         void *ptr = va_arg(vargs, void *);
540
541                         ret = PyObject_CallFunction(calcsize, (char *)"s", format);
542
543                         if (ret) {
544                                 sizes[i] = PyLong_AsSsize_t(ret);
545                                 Py_DECREF(ret);
546                                 ret = PyObject_CallFunction(unpack, (char *)"sy#", format, (char *)ptr, sizes[i]);
547                         }
548
549                         if (ret == NULL) {
550                                 printf("PyC_InlineRun error, line:%d\n", __LINE__);
551                                 PyErr_Print();
552                                 PyErr_Clear();
553
554                                 PyList_SET_ITEM(values, i, Py_None); /* hold user */
555                                 Py_INCREF(Py_None);
556
557                                 sizes[i] = 0;
558                         }
559                         else {
560                                 if (PyTuple_GET_SIZE(ret) == 1) {
561                                         /* convenience, convert single tuples into single values */
562                                         PyObject *tmp = PyTuple_GET_ITEM(ret, 0);
563                                         Py_INCREF(tmp);
564                                         Py_DECREF(ret);
565                                         ret = tmp;
566                                 }
567
568                                 PyList_SET_ITEM(values, i, ret); /* hold user */
569                         }
570                 }
571                 va_end(vargs);
572                 
573                 /* set the value so we can access it */
574                 PyDict_SetItemString(py_dict, "values", values);
575
576                 py_result = PyRun_File(fp, filepath, Py_file_input, py_dict, py_dict);
577
578                 fclose(fp);
579
580                 if (py_result) {
581
582                         /* we could skip this but then only slice assignment would work
583                          * better not be so strict */
584                         values = PyDict_GetItemString(py_dict, "values");
585
586                         if (values && PyList_Check(values)) {
587
588                                 /* don't use the result */
589                                 Py_DECREF(py_result);
590                                 py_result = NULL;
591
592                                 /* now get the values back */
593                                 va_start(vargs, n);
594                                 for (i = 0; i * 2 < n; i++) {
595                                         char *format = va_arg(vargs, char *);
596                                         void *ptr = va_arg(vargs, void *);
597                                         
598                                         PyObject *item;
599                                         PyObject *item_new;
600                                         /* prepend the string formatting and remake the tuple */
601                                         item = PyList_GET_ITEM(values, i);
602                                         if (PyTuple_CheckExact(item)) {
603                                                 int ofs = PyTuple_GET_SIZE(item);
604                                                 item_new = PyTuple_New(ofs + 1);
605                                                 while (ofs--) {
606                                                         PyObject *member = PyTuple_GET_ITEM(item, ofs);
607                                                         PyTuple_SET_ITEM(item_new, ofs + 1, member);
608                                                         Py_INCREF(member);
609                                                 }
610
611                                                 PyTuple_SET_ITEM(item_new, 0, PyUnicode_FromString(format));
612                                         }
613                                         else {
614                                                 item_new = Py_BuildValue("sO", format, item);
615                                         }
616
617                                         ret = PyObject_Call(pack, item_new, NULL);
618
619                                         if (ret) {
620                                                 /* copy the bytes back into memory */
621                                                 memcpy(ptr, PyBytes_AS_STRING(ret), sizes[i]);
622                                                 Py_DECREF(ret);
623                                         }
624                                         else {
625                                                 printf("PyC_InlineRun error on arg '%d', line:%d\n", i, __LINE__);
626                                                 PyC_ObSpit("failed converting:", item_new);
627                                                 PyErr_Print();
628                                                 PyErr_Clear();
629                                         }
630
631                                         Py_DECREF(item_new);
632                                 }
633                                 va_end(vargs);
634                         }
635                         else {
636                                 printf("PyC_InlineRun error, 'values' not a list, line:%d\n", __LINE__);
637                         }
638                 }
639                 else {
640                         printf("PyC_InlineRun error line:%d\n", __LINE__);
641                         PyErr_Print();
642                         PyErr_Clear();
643                 }
644
645                 Py_DECREF(calcsize);
646                 Py_DECREF(pack);
647                 Py_DECREF(unpack);
648
649                 PyMem_FREE(sizes);
650
651                 PyGILState_Release(gilstate);
652         }
653 }
654
655 /* generic function to avoid depending on RNA */
656 void *PyC_RNA_AsPointer(PyObject *value, const char *type_name)
657 {
658         PyObject *as_pointer;
659         PyObject *pointer;
660
661         if (!strcmp(Py_TYPE(value)->tp_name, type_name) &&
662             (as_pointer = PyObject_GetAttrString(value, "as_pointer")) != NULL &&
663             PyCallable_Check(as_pointer))
664         {
665                 void *result = NULL;
666
667                 /* must be a 'type_name' object */
668                 pointer = PyObject_CallObject(as_pointer, NULL);
669                 Py_DECREF(as_pointer);
670
671                 if (!pointer) {
672                         PyErr_SetString(PyExc_SystemError, "value.as_pointer() failed");
673                         return NULL;
674                 }
675                 result = PyLong_AsVoidPtr(pointer);
676                 Py_DECREF(pointer);
677                 if (!result) {
678                         PyErr_SetString(PyExc_SystemError, "value.as_pointer() failed");
679                 }
680
681                 return result;
682         }
683         else {
684                 PyErr_Format(PyExc_TypeError,
685                              "expected '%.200s' type found '%.200s' instead",
686                              type_name, Py_TYPE(value)->tp_name);
687                 return NULL;
688         }
689 }
690
691
692 /* PyC_FlagSet_* functions - so flags/sets can be interchanged in a generic way */
693 #include "BLI_dynstr.h"
694 #include "MEM_guardedalloc.h"
695
696 char *PyC_FlagSet_AsString(PyC_FlagSet *item)
697 {
698         DynStr *dynstr = BLI_dynstr_new();
699         PyC_FlagSet *e;
700         char *cstring;
701
702         for (e = item; item->identifier; item++) {
703                 BLI_dynstr_appendf(dynstr, (e == item) ? "'%s'" : ", '%s'", item->identifier);
704         }
705
706         cstring = BLI_dynstr_get_cstring(dynstr);
707         BLI_dynstr_free(dynstr);
708         return cstring;
709 }
710
711 int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *value)
712 {
713         for ( ; item->identifier; item++) {
714                 if (strcmp(item->identifier, identifier) == 0) {
715                         *value = item->value;
716                         return 1;
717                 }
718         }
719
720         return 0;
721 }
722
723 int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, const char *identifier, int *value, const char *error_prefix)
724 {
725         if (PyC_FlagSet_ValueFromID_int(item, identifier, value) == 0) {
726                 const char *enum_str = PyC_FlagSet_AsString(item);
727                 PyErr_Format(PyExc_ValueError,
728                              "%s: '%.200s' not found in (%s)",
729                              error_prefix, identifier, enum_str);
730                 MEM_freeN((void *)enum_str);
731                 return -1;
732         }
733
734         return 0;
735 }
736
737 /* 'value' _must_ be a set type, error check before calling */
738 int PyC_FlagSet_ToBitfield(PyC_FlagSet *items, PyObject *value, int *r_value, const char *error_prefix)
739 {
740         /* set of enum items, concatenate all values with OR */
741         int ret, flag = 0;
742
743         /* set looping */
744         Py_ssize_t pos = 0;
745         Py_ssize_t hash = 0;
746         PyObject *key;
747
748         *r_value = 0;
749
750         while (_PySet_NextEntry(value, &pos, &key, &hash)) {
751                 const char *param = _PyUnicode_AsString(key);
752
753                 if (param == NULL) {
754                         PyErr_Format(PyExc_TypeError,
755                                      "%.200s expected a string, not %.200s",
756                                      error_prefix, Py_TYPE(key)->tp_name);
757                         return -1;
758                 }
759
760                 if (PyC_FlagSet_ValueFromID(items, param, &ret, error_prefix) < 0) {
761                         return -1;
762                 }
763
764                 flag |= ret;
765         }
766
767         *r_value = flag;
768         return 0;
769 }
770
771 PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag)
772 {
773         PyObject *ret = PySet_New(NULL);
774         PyObject *pystr;
775
776         for ( ; items->identifier; items++) {
777                 if (items->value & flag) {
778                         pystr = PyUnicode_FromString(items->identifier);
779                         PySet_Add(ret, pystr);
780                         Py_DECREF(pystr);
781                 }
782         }
783
784         return ret;
785 }