Merge branch 'master' into blender2.8
[blender.git] / source / blender / python / bmesh / bmesh_py_ops_call.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  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Campbell Barton
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/python/bmesh/bmesh_py_ops_call.c
27  *  \ingroup pybmesh
28  *
29  * This file provides __call__ aka BPy_BMO_call for
30  * the bmesh operator and has been given its own file
31  * because argument conversion is involved.
32  */
33
34 #include <Python.h>
35
36 #include "BLI_utildefines.h"
37
38 #include "../mathutils/mathutils.h"
39
40 #include "bmesh.h"
41
42 #include "bmesh_py_ops_call.h"  /* own include */
43
44 #include "bmesh_py_types.h"
45
46 #include "../generic/python_utildefines.h"
47 #include "../generic/py_capi_utils.h"
48
49 static int bpy_bm_op_as_py_error(BMesh *bm)
50 {
51         if (BMO_error_occurred(bm)) {
52                 /* note: we could have multiple errors */
53                 const char *errmsg;
54                 if (BMO_error_get(bm, &errmsg, NULL)) {
55                         PyErr_Format(PyExc_RuntimeError,
56                                      "bmesh operator: %.200s",
57                                      errmsg);
58                         BMO_error_clear(bm);
59                         return -1;
60                 }
61         }
62         return 0;
63 }
64
65 /**
66  * \brief Utility function to check BMVert/BMEdge/BMFace's
67  *
68  * \param value
69  * \param bm Check the \a value against this.
70  * \param htype Test \a value matches this type.
71  * \param descr Description text.
72  */
73 static int bpy_slot_from_py_elem_check(
74         BPy_BMElem *value, BMesh *bm, const char htype,
75         /* for error messages */
76         const char *opname, const char *slot_name, const char *descr)
77 {
78         if (!BPy_BMElem_Check(value) ||
79             !(value->ele->head.htype & htype))
80         {
81                 PyErr_Format(PyExc_TypeError,
82                              "%.200s: keyword \"%.200s\" %.200s, expected a %.200s not *.200s",
83                              opname, slot_name, descr,
84                              BPy_BMElem_StringFromHType(htype),
85                              Py_TYPE(value)->tp_name);
86                 return -1;
87         }
88         else if (value->bm == NULL) {
89                 PyErr_Format(PyExc_TypeError,
90                              "%.200s: keyword \"%.200s\" %.200s invalidated element",
91                              opname, slot_name, descr);
92                 return -1;
93         }
94         else if (value->bm != bm) {  /* we may want to make this check optional by setting 'bm' to NULL */
95                 PyErr_Format(PyExc_TypeError,
96                              "%.200s: keyword \"%.200s\" %.200s invalidated element",
97                              opname, slot_name, descr);
98                 return -1;
99         }
100         return 0;
101 }
102
103 /**
104  * \brief Utility function to check BMVertSeq/BMEdgeSeq/BMFaceSeq's
105  *
106  * \param value Caller must check its a BMeshSeq
107  * \param bm Check the \a value against this.
108  * \param htype_py The type(s) of \a value.
109  * \param htype_bmo The type(s) supported by the target slot.
110  * \param descr Description text.
111  */
112 static int bpy_slot_from_py_elemseq_check(
113         BPy_BMGeneric *value, BMesh *bm,
114         const char htype_py, const char htype_bmo,
115         /* for error messages */
116         const char *opname, const char *slot_name, const char *descr)
117 {
118         if (value->bm == NULL) {
119                 PyErr_Format(PyExc_TypeError,
120                              "%.200s: keyword \"%.200s\" %.200s, invalidated sequence",
121                              opname, slot_name, descr);
122                 return -1;
123         }
124         else if (value->bm != bm) {  /* we may want to make this check optional by setting 'bm' to NULL */
125                 PyErr_Format(PyExc_TypeError,
126                              "%.200s: keyword \"%.200s\" %.200s, invalidated sequence",
127                              opname, slot_name, descr);
128                 return -1;
129         }
130         else if ((htype_py & htype_bmo) == 0) {
131                 char str_bmo[32];
132                 char str_py[32];
133                 PyErr_Format(PyExc_TypeError,
134                              "%.200s: keyword \"%.200s\" %.200s, expected "
135                              "a sequence of %.200s not %.200s",
136                              opname, slot_name, descr,
137                              BPy_BMElem_StringFromHType_ex(htype_bmo, str_bmo),
138                              BPy_BMElem_StringFromHType_ex(htype_py,  str_py));
139                 return -1;
140         }
141
142         return 0;
143 }
144
145 /**
146  * Use for giving py args to an operator.
147  */
148 static int bpy_slot_from_py(
149         BMesh *bm, BMOperator *bmop, BMOpSlot *slot, PyObject *value,
150         /* the are just for exception messages */
151         const char *opname, const char *slot_name)
152 {
153         switch (slot->slot_type) {
154                 case BMO_OP_SLOT_BOOL:
155                 {
156                         const int param = PyC_Long_AsBool(value);
157
158                         if (param == -1) {
159                                 PyErr_Format(PyExc_TypeError,
160                                              "%.200s: keyword \"%.200s\" expected True/False or 0/1, not %.200s",
161                                              opname, slot_name, Py_TYPE(value)->tp_name);
162                                 return -1;
163                         }
164                         else {
165                                 BMO_SLOT_AS_BOOL(slot) = param;
166                         }
167
168                         break;
169                 }
170                 case BMO_OP_SLOT_INT:
171                 {
172                         const int param = PyC_Long_AsI32(value);
173
174                         if (param == -1 && PyErr_Occurred()) {
175                                 PyErr_Format(PyExc_TypeError,
176                                              "%.200s: keyword \"%.200s\" expected an int, not %.200s",
177                                              opname, slot_name, Py_TYPE(value)->tp_name);
178                                 return -1;
179                         }
180                         else {
181                                 BMO_SLOT_AS_INT(slot) = param;
182                         }
183                         break;
184                 }
185                 case BMO_OP_SLOT_FLT:
186                 {
187                         float param = PyFloat_AsDouble(value);
188                         if (param == -1 && PyErr_Occurred()) {
189                                 PyErr_Format(PyExc_TypeError,
190                                              "%.200s: keyword \"%.200s\" expected a float, not %.200s",
191                                              opname, slot_name, Py_TYPE(value)->tp_name);
192                                 return -1;
193                         }
194                         else {
195                                 BMO_SLOT_AS_FLOAT(slot) = param;
196                         }
197                         break;
198                 }
199                 case BMO_OP_SLOT_MAT:
200                 {
201                         /* XXX - BMesh operator design is crappy here, operator slot should define matrix size,
202                          * not the caller! */
203                         MatrixObject *pymat;
204                         if (!Matrix_ParseAny(value, &pymat)) {
205                                 return -1;
206                         }
207                         const ushort size = pymat->num_col;
208                         if ((size != pymat->num_row) || (!ELEM(size, 3, 4))) {
209                                 PyErr_Format(PyExc_TypeError,
210                                              "%.200s: keyword \"%.200s\" expected a 3x3 or 4x4 matrix Matrix",
211                                              opname, slot_name);
212                                 return -1;
213                         }
214
215                         BMO_slot_mat_set(bmop, bmop->slots_in, slot_name, pymat->matrix, size);
216                         break;
217                 }
218                 case BMO_OP_SLOT_VEC:
219                 {
220                         /* passing slot name here is a bit non-descriptive */
221                         if (mathutils_array_parse(BMO_SLOT_AS_VECTOR(slot), 3, 3, value, slot_name) == -1) {
222                                 return -1;
223                         }
224                         break;
225                 }
226                 case BMO_OP_SLOT_ELEMENT_BUF:
227                 {
228                         if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
229                                 if (bpy_slot_from_py_elem_check((BPy_BMElem *)value, bm, (slot->slot_subtype.elem & BM_ALL_NOLOOP),
230                                                                 opname, slot_name, "single element") == -1)
231                                 {
232                                         return -1;  /* error is set in bpy_slot_from_py_elem_check() */
233                                 }
234
235                                 BMO_slot_buffer_from_single(bmop, slot, &((BPy_BMElem *)value)->ele->head);
236                         }
237                         else {
238                                 /* there are many ways we could interpret arguments, for now...
239                                  * - verts/edges/faces from the mesh direct,
240                                  *   this way the operator takes every item.
241                                  * - `TODO` a plain python sequence (list) of elements.
242                                  * - `TODO`  an iterator. eg.
243                                  *   face.verts
244                                  * - `TODO`  (type, flag) pair, eg.
245                                  *   ('VERT', {'TAG'})
246                                  */
247
248                                 if (BPy_BMVertSeq_Check(value)) {
249                                         if (bpy_slot_from_py_elemseq_check((BPy_BMGeneric *)value, bm,
250                                                                            BM_VERT, (slot->slot_subtype.elem & BM_ALL_NOLOOP),
251                                                                            opname, slot_name, "element buffer") == -1)
252                                         {
253                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
254                                         }
255
256                                         BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_VERT);
257                                 }
258                                 else if (BPy_BMEdgeSeq_Check(value)) {
259                                         if (bpy_slot_from_py_elemseq_check((BPy_BMGeneric *)value, bm,
260                                                                            BM_EDGE, (slot->slot_subtype.elem & BM_ALL_NOLOOP),
261                                                                            opname, slot_name, "element buffer") == -1)
262                                         {
263                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
264                                         }
265
266                                         BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_EDGE);
267                                 }
268                                 else if (BPy_BMFaceSeq_Check(value)) {
269                                         if (bpy_slot_from_py_elemseq_check((BPy_BMGeneric *)value, bm,
270                                                                            BM_FACE, (slot->slot_subtype.elem & BM_ALL_NOLOOP),
271                                                                            opname, slot_name, "element buffer") == -1)
272                                         {
273                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
274                                         }
275                                         BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_FACE);
276                                 }
277
278                                 else if (BPy_BMElemSeq_Check(value)) {
279                                         BMIter iter;
280                                         BMHeader *ele;
281                                         int tot;
282                                         unsigned int i;
283
284                                         if (bpy_slot_from_py_elemseq_check((BPy_BMGeneric *)value, bm,
285                                                                            bm_iter_itype_htype_map[((BPy_BMElemSeq *)value)->itype],
286                                                                            (slot->slot_subtype.elem & BM_ALL_NOLOOP),
287                                                                            opname, slot_name, "element buffer") == -1)
288                                         {
289                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
290                                         }
291
292                                         /* this will loop over all elements which is a shame but
293                                          * we need to know this before alloc */
294                                         /* calls bpy_bmelemseq_length() */
295                                         tot = Py_TYPE(value)->tp_as_sequence->sq_length(value);
296
297                                         BMO_slot_buffer_alloc(bmop, bmop->slots_in, slot_name, tot);
298
299                                         i = 0;
300                                         BM_ITER_BPY_BM_SEQ (ele, &iter, ((BPy_BMElemSeq *)value)) {
301                                                 slot->data.buf[i] = ele;
302                                                 i++;
303                                         }
304                                 }
305                                 /* keep this last */
306                                 else if (PySequence_Check(value)) {
307                                         BMElem **elem_array = NULL;
308                                         Py_ssize_t elem_array_len;
309
310                                         elem_array = BPy_BMElem_PySeq_As_Array(&bm, value, 0, PY_SSIZE_T_MAX,
311                                                                                &elem_array_len, (slot->slot_subtype.elem & BM_ALL_NOLOOP),
312                                                                                true, true, slot_name);
313
314                                         /* error is set above */
315                                         if (elem_array == NULL) {
316                                                 return -1;
317                                         }
318
319                                         BMO_slot_buffer_alloc(bmop, bmop->slots_in, slot_name, elem_array_len);
320                                         memcpy(slot->data.buf, elem_array, sizeof(void *) * elem_array_len);
321                                         PyMem_FREE(elem_array);
322                                 }
323                                 else {
324                                         PyErr_Format(PyExc_TypeError,
325                                                      "%.200s: keyword \"%.200s\" expected "
326                                                      "a bmesh sequence, list, (htype, flag) pair, not %.200s",
327                                                      opname, slot_name, Py_TYPE(value)->tp_name);
328                                         return -1;
329                                 }
330                         }
331                         break;
332                 }
333                 case BMO_OP_SLOT_MAPPING:
334                 {
335                         /* first check types */
336                         if (slot->slot_subtype.map != BMO_OP_SLOT_SUBTYPE_MAP_EMPTY) {
337                                 if (!PyDict_Check(value)) {
338                                         PyErr_Format(PyExc_TypeError,
339                                                      "%.200s: keyword \"%.200s\" expected "
340                                                      "a dict, not %.200s",
341                                                      opname, slot_name, Py_TYPE(value)->tp_name);
342                                         return -1;
343                                 }
344                         }
345                         else {
346                                 if (!PySet_Check(value)) {
347                                         PyErr_Format(PyExc_TypeError,
348                                                      "%.200s: keyword \"%.200s\" expected "
349                                                      "a set, not %.200s",
350                                                      opname, slot_name, Py_TYPE(value)->tp_name);
351                                         return -1;
352                                 }
353                         }
354
355                         switch (slot->slot_subtype.map) {
356                                 case BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
357                                 {
358                                         if (PyDict_Size(value) > 0) {
359                                                 PyObject *arg_key, *arg_value;
360                                                 Py_ssize_t arg_pos = 0;
361                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
362                                                         if (bpy_slot_from_py_elem_check((BPy_BMElem *)arg_key, bm, BM_ALL_NOLOOP,
363                                                                                         opname, slot_name, "invalid key in dict") == -1)
364                                                         {
365                                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
366                                                         }
367
368                                                         if (bpy_slot_from_py_elem_check((BPy_BMElem *)arg_value, bm, BM_ALL_NOLOOP,
369                                                                                         opname, slot_name, "invalid value in dict") == -1)
370                                                         {
371                                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
372                                                         }
373
374                                                         BMO_slot_map_elem_insert(bmop, slot,
375                                                                                  ((BPy_BMElem *)arg_key)->ele, ((BPy_BMElem *)arg_value)->ele);
376                                                 }
377                                         }
378                                         break;
379                                 }
380                                 case BMO_OP_SLOT_SUBTYPE_MAP_FLT:
381                                 {
382                                         if (PyDict_Size(value) > 0) {
383                                                 PyObject *arg_key, *arg_value;
384                                                 Py_ssize_t arg_pos = 0;
385                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
386                                                         float value_f;
387
388                                                         if (bpy_slot_from_py_elem_check((BPy_BMElem *)arg_key, bm, BM_ALL_NOLOOP,
389                                                                                         opname, slot_name, "invalid key in dict") == -1)
390                                                         {
391                                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
392                                                         }
393
394                                                         value_f = PyFloat_AsDouble(arg_value);
395
396                                                         if (value_f == -1.0f && PyErr_Occurred()) {
397                                                                 PyErr_Format(PyExc_TypeError,
398                                                                              "%.200s: keyword \"%.200s\" expected "
399                                                                              "a dict with float values, not %.200s",
400                                                                              opname, slot_name, Py_TYPE(arg_value)->tp_name);
401                                                                 return -1;
402                                                         }
403
404                                                         BMO_slot_map_float_insert(bmop, slot,
405                                                                                   ((BPy_BMElem *)arg_key)->ele, value_f);
406                                                 }
407                                         }
408                                         break;
409                                 }
410                                 case BMO_OP_SLOT_SUBTYPE_MAP_INT:
411                                 {
412                                         if (PyDict_Size(value) > 0) {
413                                                 PyObject *arg_key, *arg_value;
414                                                 Py_ssize_t arg_pos = 0;
415                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
416                                                         int value_i;
417
418                                                         if (bpy_slot_from_py_elem_check((BPy_BMElem *)arg_key, bm, BM_ALL_NOLOOP,
419                                                                                         opname, slot_name, "invalid key in dict") == -1)
420                                                         {
421                                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
422                                                         }
423
424                                                         value_i = PyC_Long_AsI32(arg_value);
425
426                                                         if (value_i == -1 && PyErr_Occurred()) {
427                                                                 PyErr_Format(PyExc_TypeError,
428                                                                              "%.200s: keyword \"%.200s\" expected "
429                                                                              "a dict with int values, not %.200s",
430                                                                              opname, slot_name, Py_TYPE(arg_value)->tp_name);
431                                                                 return -1;
432                                                         }
433
434                                                         BMO_slot_map_int_insert(bmop, slot,
435                                                                                 ((BPy_BMElem *)arg_key)->ele, value_i);
436                                                 }
437                                         }
438                                         break;
439                                 }
440                                 case BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
441                                 {
442                                         if (PyDict_Size(value) > 0) {
443                                                 PyObject *arg_key, *arg_value;
444                                                 Py_ssize_t arg_pos = 0;
445                                                 while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
446                                                         int value_i;
447
448                                                         if (bpy_slot_from_py_elem_check((BPy_BMElem *)arg_key, bm, BM_ALL_NOLOOP,
449                                                                                         opname, slot_name, "invalid key in dict") == -1)
450                                                         {
451                                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
452                                                         }
453
454                                                         value_i = PyC_Long_AsI32(arg_value);
455
456                                                         if (value_i == -1 && PyErr_Occurred()) {
457                                                                 PyErr_Format(PyExc_TypeError,
458                                                                              "%.200s: keyword \"%.200s\" expected "
459                                                                              "a dict with bool values, not %.200s",
460                                                                              opname, slot_name, Py_TYPE(arg_value)->tp_name);
461                                                                 return -1;
462                                                         }
463
464                                                         BMO_slot_map_bool_insert(bmop, slot,
465                                                                                  ((BPy_BMElem *)arg_key)->ele, value_i != 0);
466                                                 }
467                                         }
468                                         break;
469                                 }
470                                 case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY:
471                                 {
472                                         if (PySet_Size(value) > 0) {
473                                                 PyObject *arg_key;
474                                                 Py_ssize_t arg_pos = 0;
475                                                 Py_ssize_t arg_hash = 0;
476                                                 while (_PySet_NextEntry(value, &arg_pos, &arg_key, &arg_hash)) {
477
478                                                         if (bpy_slot_from_py_elem_check((BPy_BMElem *)arg_key, bm, BM_ALL_NOLOOP,
479                                                                                         opname, slot_name, "invalid key in set") == -1)
480                                                         {
481                                                                 return -1;  /* error is set in bpy_slot_from_py_elem_check() */
482                                                         }
483
484                                                         BMO_slot_map_empty_insert(bmop, slot,
485                                                                                  ((BPy_BMElem *)arg_key)->ele);
486                                                 }
487                                         }
488                                         break;
489                                 }
490                                 case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
491                                 {
492                                         /* can't convert from these */
493                                         PyErr_Format(PyExc_NotImplementedError,
494                                                      "This arguments mapping subtype %d is not supported", slot->slot_subtype.map);
495                                         return -1;
496                                 }
497                         }
498                         break;
499                 }
500                 default:
501                         /* TODO --- many others */
502                         PyErr_Format(PyExc_NotImplementedError,
503                                      "%.200s: keyword \"%.200s\" type %d not working yet!",
504                                      opname, slot_name, slot->slot_type);
505                         return -1;
506         }
507
508         /* all is well */
509         return 0;
510 }
511
512 /**
513  * Use for getting return values from an operator thats already executed.
514  *
515  * \note Don't throw any exceptions and should always return a valid (PyObject *).
516  */
517 static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
518 {
519         PyObject *item = NULL;
520
521         /* keep switch in same order as above */
522         switch (slot->slot_type) {
523                 case BMO_OP_SLOT_BOOL:
524                         item = PyBool_FromLong((BMO_SLOT_AS_BOOL(slot)));
525                         break;
526                 case BMO_OP_SLOT_INT:
527                         item = PyLong_FromLong(BMO_SLOT_AS_INT(slot));
528                         break;
529                 case BMO_OP_SLOT_FLT:
530                         item = PyFloat_FromDouble((double)BMO_SLOT_AS_FLOAT(slot));
531                         break;
532                 case BMO_OP_SLOT_MAT:
533                         item = Matrix_CreatePyObject((float *)BMO_SLOT_AS_MATRIX(slot), 4, 4, NULL);
534                         break;
535                 case BMO_OP_SLOT_VEC:
536                         item = Vector_CreatePyObject(BMO_SLOT_AS_VECTOR(slot), slot->len, NULL);
537                         break;
538                 case BMO_OP_SLOT_PTR:
539                         BLI_assert(0);  /* currently we don't have any pointer return values in use */
540                         item = Py_INCREF_RET(Py_None);
541                         break;
542                 case BMO_OP_SLOT_ELEMENT_BUF:
543                 {
544                         if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
545                                 BMHeader *ele = BMO_slot_buffer_get_single(slot);
546                                 item = ele ? BPy_BMElem_CreatePyObject(bm, ele) : Py_INCREF_RET(Py_None);
547                         }
548                         else {
549                                 const int size = slot->len;
550                                 void **buffer = BMO_SLOT_AS_BUFFER(slot);
551                                 int j;
552
553                                 item = PyList_New(size);
554                                 for (j = 0; j < size; j++) {
555                                         BMHeader *ele = buffer[j];
556                                         PyList_SET_ITEM(item, j, BPy_BMElem_CreatePyObject(bm, ele));
557                                 }
558                         }
559                         break;
560                 }
561                 case BMO_OP_SLOT_MAPPING:
562                 {
563                         GHash *slot_hash = BMO_SLOT_AS_GHASH(slot);
564                         GHashIterator hash_iter;
565
566                         switch (slot->slot_subtype.map) {
567                                 case BMO_OP_SLOT_SUBTYPE_MAP_ELEM:
568                                 {
569                                         item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
570                                         if (slot_hash) {
571                                                 GHASH_ITER (hash_iter, slot_hash) {
572                                                         BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
573                                                         void     *ele_val = BLI_ghashIterator_getValue(&hash_iter);
574
575                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm, ele_key);
576                                                         PyObject *py_val =  BPy_BMElem_CreatePyObject(bm, ele_val);
577
578                                                         PyDict_SetItem(item, py_key, py_val);
579                                                         Py_DECREF(py_key);
580                                                         Py_DECREF(py_val);
581                                                 }
582                                         }
583                                         break;
584                                 }
585                                 case BMO_OP_SLOT_SUBTYPE_MAP_FLT:
586                                 {
587                                         item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
588                                         if (slot_hash) {
589                                                 GHASH_ITER (hash_iter, slot_hash) {
590                                                         BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
591                                                         void     *ele_val = BLI_ghashIterator_getValue(&hash_iter);
592
593                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
594                                                         PyObject *py_val =  PyFloat_FromDouble(*(float *)&ele_val);
595
596                                                         PyDict_SetItem(item, py_key, py_val);
597                                                         Py_DECREF(py_key);
598                                                         Py_DECREF(py_val);
599                                                 }
600                                         }
601                                         break;
602                                 }
603                                 case BMO_OP_SLOT_SUBTYPE_MAP_INT:
604                                 {
605                                         item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
606                                         if (slot_hash) {
607                                                 GHASH_ITER (hash_iter, slot_hash) {
608                                                         BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
609                                                         void     *ele_val = BLI_ghashIterator_getValue(&hash_iter);
610
611                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
612                                                         PyObject *py_val =  PyLong_FromLong(*(int *)&ele_val);
613
614                                                         PyDict_SetItem(item, py_key, py_val);
615                                                         Py_DECREF(py_key);
616                                                         Py_DECREF(py_val);
617                                                 }
618                                         }
619                                         break;
620                                 }
621                                 case BMO_OP_SLOT_SUBTYPE_MAP_BOOL:
622                                 {
623                                         item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
624                                         if (slot_hash) {
625                                                 GHASH_ITER (hash_iter, slot_hash) {
626                                                         BMHeader *ele_key = BLI_ghashIterator_getKey(&hash_iter);
627                                                         void     *ele_val = BLI_ghashIterator_getValue(&hash_iter);
628
629                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
630                                                         PyObject *py_val =  PyBool_FromLong(*(bool *)&ele_val);
631
632                                                         PyDict_SetItem(item, py_key, py_val);
633                                                         Py_DECREF(py_key);
634                                                         Py_DECREF(py_val);
635                                                 }
636                                         }
637                                         break;
638                                 }
639                                 case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY:
640                                 {
641                                         item = PySet_New(NULL);
642                                         if (slot_hash) {
643                                                 GHASH_ITER (hash_iter, slot_hash) {
644                                                         BMHeader       *ele_key = BLI_ghashIterator_getKey(&hash_iter);
645
646                                                         PyObject *py_key =  BPy_BMElem_CreatePyObject(bm,  ele_key);
647
648                                                         PySet_Add(item, py_key);
649
650                                                         Py_DECREF(py_key);
651                                                 }
652                                         }
653                                         break;
654                                 }
655                                 case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
656                                         /* can't convert from these */
657                                         item = Py_INCREF_RET(Py_None);
658                                         break;
659                         }
660                         break;
661                 }
662         }
663         BLI_assert(item != NULL);
664
665         return item;
666 }
667
668 /**
669  * This is the __call__ for bmesh.ops.xxx()
670  */
671 PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw)
672 {
673         PyObject *ret;
674         BPy_BMesh *py_bm;
675         BMesh *bm;
676
677         BMOperator bmop;
678
679         if ((PyTuple_GET_SIZE(args) == 1) &&
680             (py_bm = (BPy_BMesh *)PyTuple_GET_ITEM(args, 0)) &&
681             (BPy_BMesh_Check(py_bm)))
682         {
683                 BPY_BM_CHECK_OBJ(py_bm);
684                 bm = py_bm->bm;
685
686                 if (bm->use_toolflags == false) {
687                         PyErr_SetString(PyExc_ValueError,
688                                         "bmesh created with 'use_operators=False'");
689                         return NULL;
690                 }
691
692                 /* could complain about entering with exceptions... */
693                 BMO_error_clear(bm);
694         }
695         else {
696                 PyErr_SetString(PyExc_TypeError,
697                                 "bmesh operators expect a single BMesh positional argument, all other args must be keywords");
698                 return NULL;
699         }
700
701         /* TODO - error check this!, though we do the error check on attribute access */
702         /* TODO - make flags optional */
703         BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, self->opname);
704
705         if (kw && PyDict_Size(kw) > 0) {
706                 /* setup properties, see bpy_rna.c: pyrna_py_to_prop()
707                  * which shares this logic for parsing properties */
708
709                 PyObject *key, *value;
710                 Py_ssize_t pos = 0;
711                 while (PyDict_Next(kw, &pos, &key, &value)) {
712                         const char *slot_name = _PyUnicode_AsString(key);
713                         BMOpSlot *slot;
714
715                         if (!BMO_slot_exists(bmop.slots_in, slot_name)) {
716                                 PyErr_Format(PyExc_TypeError,
717                                              "%.200s: keyword \"%.200s\" is invalid for this operator",
718                                              self->opname, slot_name);
719                                 BMO_op_finish(bm, &bmop);
720                                 return NULL;
721                         }
722
723                         slot = BMO_slot_get(bmop.slots_in, slot_name);
724
725                         /* now assign the value */
726                         if (bpy_slot_from_py(bm, &bmop, slot, value,
727                                              self->opname, slot_name) == -1)
728                         {
729                                 BMO_op_finish(bm, &bmop);
730                                 return NULL;
731                         }
732                 }
733         }
734
735         BMO_op_exec(bm, &bmop);
736
737         /* from here until the end of the function, no returns, just set 'ret' */
738         if (UNLIKELY(bpy_bm_op_as_py_error(bm) == -1)) {
739                 ret = NULL;  /* exception raised above */
740         }
741         else if (bmop.slots_out[0].slot_name == NULL) {
742                 ret = Py_INCREF_RET(Py_None);
743         }
744         else {
745                 /* build return value */
746                 int i;
747                 ret = PyDict_New();
748
749                 for (i = 0; bmop.slots_out[i].slot_name; i++) {
750                         // BMOpDefine *op_def = opdefines[bmop.type];
751                         // BMOSlotType *slot_type = op_def->slot_types_out[i];
752                         BMOpSlot *slot = &bmop.slots_out[i];
753                         PyObject *item;
754
755                         /* this function doesn't throw exceptions */
756                         item = bpy_slot_to_py(bm, slot);
757                         if (item == NULL) {
758                                 item = Py_INCREF_RET(Py_None);
759                         }
760
761 #if 1
762                         /* temp code, strip off '.out' while we keep this convention */
763                         {
764                                 char slot_name_strip[MAX_SLOTNAME];
765                                 const char *ch = strchr(slot->slot_name, '.');  /* can't fail! */
766                                 int tot = ch - slot->slot_name;
767                                 BLI_assert(ch != NULL);
768                                 memcpy(slot_name_strip, slot->slot_name, tot);
769                                 slot_name_strip[tot] = '\0';
770                                 PyDict_SetItemString(ret, slot_name_strip, item);
771                         }
772 #else
773                         PyDict_SetItemString(ret, slot->slot_name, item);
774 #endif
775                         Py_DECREF(item);
776                 }
777         }
778
779         BMO_op_finish(bm, &bmop);
780         return ret;
781 }