Blender V4.3
bpy_operator.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
16#include <Python.h>
17
18#include "RNA_types.hh"
19
20#include "BLI_listbase.h"
21#include "BLI_utildefines.h"
22
27
28#include "BPY_extern.hh"
29#include "bpy_capi_utils.hh"
30#include "bpy_operator.hh"
31#include "bpy_operator_wrap.hh"
32#include "bpy_rna.hh" /* for setting argument properties & type method `get_rna_type`. */
33
34#include "RNA_access.hh"
35#include "RNA_enum_types.hh"
36#include "RNA_prototypes.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "MEM_guardedalloc.h"
42
43#include "BLI_ghash.h"
44
45#include "BKE_context.hh"
46#include "BKE_global.hh"
47#include "BKE_report.hh"
48
49/* so operators called can spawn threads which acquire the GIL */
50#define BPY_RELEASE_GIL
51
52static wmOperatorType *ot_lookup_from_py_string(PyObject *value, const char *py_fn_id)
53{
54 const char *opname = PyUnicode_AsUTF8(value);
55 if (opname == nullptr) {
56 PyErr_Format(PyExc_TypeError, "%s() expects a string argument", py_fn_id);
57 return nullptr;
58 }
59
60 wmOperatorType *ot = WM_operatortype_find(opname, true);
61 if (ot == nullptr) {
62 PyErr_Format(PyExc_KeyError, "%s(\"%s\") not found", py_fn_id, opname);
63 return nullptr;
64 }
65 return ot;
66}
67
68static PyObject *pyop_poll(PyObject * /*self*/, PyObject *args)
69{
71 const char *opname;
72 const char *context_str = nullptr;
73 PyObject *ret;
74
76
77 /* XXX TODO: work out a better solution for passing on context,
78 * could make a tuple from self and pack the name and Context into it. */
80
81 if (C == nullptr) {
82 PyErr_SetString(PyExc_RuntimeError, "Context is None, can't poll any operators");
83 return nullptr;
84 }
85
86 /* All arguments are positional. */
87 static const char *_keywords[] = {"", "", nullptr};
88 static _PyArg_Parser _parser = {
90 "s" /* `opname` */
91 "|" /* Optional arguments. */
92 "s" /* `context_str` */
93 ":_bpy.ops.poll",
94 _keywords,
95 nullptr,
96 };
97 if (!_PyArg_ParseTupleAndKeywordsFast(args, nullptr, &_parser, &opname, &context_str)) {
98 return nullptr;
99 }
100
101 ot = WM_operatortype_find(opname, true);
102
103 if (ot == nullptr) {
104 PyErr_Format(PyExc_AttributeError,
105 "Polling operator \"bpy.ops.%s\" error, "
106 "could not be found",
107 opname);
108 return nullptr;
109 }
110
111 if (context_str) {
112 int context_int = context;
113
114 if (RNA_enum_value_from_id(rna_enum_operator_context_items, context_str, &context_int) == 0) {
116 PyErr_Format(PyExc_TypeError,
117 "Calling operator \"bpy.ops.%s.poll\" error, "
118 "expected a string enum in (%s)",
119 opname,
120 enum_str);
121 MEM_freeN(enum_str);
122 return nullptr;
123 }
124 /* Copy back to the properly typed enum. */
125 context = wmOperatorCallContext(context_int);
126 }
127
128 /* main purpose of this function */
129 ret = WM_operator_poll_context((bContext *)C, ot, context) ? Py_True : Py_False;
130
131 return Py_NewRef(ret);
132}
133
134static PyObject *pyop_call(PyObject * /*self*/, PyObject *args)
135{
137 int error_val = 0;
139 int operator_ret = OPERATOR_CANCELLED;
140
141 const char *opname;
142 const char *context_str = nullptr;
143 PyObject *kw = nullptr; /* optional args */
144
146 int is_undo = false;
147
148 /* XXX TODO: work out a better solution for passing on context,
149 * could make a tuple from self and pack the name and Context into it. */
151
152 if (C == nullptr) {
153 PyErr_SetString(PyExc_RuntimeError, "Context is None, can't poll any operators");
154 return nullptr;
155 }
156
157 /* All arguments are positional. */
158 static const char *_keywords[] = {"", "", "", "", nullptr};
159 static _PyArg_Parser _parser = {
161 "s" /* `opname` */
162 "|" /* Optional arguments. */
163 "O!" /* `kw` */
164 "s" /* `context_str` */
165 "i" /* `is_undo` */
166 ":_bpy.ops.call",
167 _keywords,
168 nullptr,
169 };
170 if (!_PyArg_ParseTupleAndKeywordsFast(
171 args, nullptr, &_parser, &opname, &PyDict_Type, &kw, &context_str, &is_undo))
172 {
173 return nullptr;
174 }
175
176 ot = WM_operatortype_find(opname, true);
177
178 if (ot == nullptr) {
179 PyErr_Format(PyExc_AttributeError,
180 "Calling operator \"bpy.ops.%s\" error, "
181 "could not be found",
182 opname);
183 return nullptr;
184 }
185
186 if (!pyrna_write_check()) {
187 PyErr_Format(PyExc_RuntimeError,
188 "Calling operator \"bpy.ops.%s\" error, "
189 "can't modify blend data in this state (drawing/rendering)",
190 opname);
191 return nullptr;
192 }
193
194 if (context_str) {
195 int context_int = context;
196
197 if (RNA_enum_value_from_id(rna_enum_operator_context_items, context_str, &context_int) == 0) {
199 PyErr_Format(PyExc_TypeError,
200 "Calling operator \"bpy.ops.%s\" error, "
201 "expected a string enum in (%s)",
202 opname,
203 enum_str);
204 MEM_freeN(enum_str);
205 return nullptr;
206 }
207 /* Copy back to the properly typed enum. */
208 context = wmOperatorCallContext(context_int);
209 }
210
211 if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
212 bool msg_free = false;
213 const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
214 PyErr_Format(PyExc_RuntimeError,
215 "Operator bpy.ops.%.200s.poll() %.200s",
216 opname,
217 msg ? msg : "failed, context is incorrect");
219 if (msg_free) {
220 MEM_freeN((void *)msg);
221 }
222 error_val = -1;
223 }
224 else {
227
228 if (kw && PyDict_Size(kw)) {
229 error_val = pyrna_pydict_to_props(
230 &ptr, kw, false, "Converting py args to operator properties:");
231 }
232
233 if (error_val == 0) {
234 ReportList *reports;
235
236 reports = static_cast<ReportList *>(MEM_mallocN(sizeof(ReportList), "wmOperatorReportList"));
237
238 /* Own so these don't move into global reports. */
240
241#ifdef BPY_RELEASE_GIL
242 /* release GIL, since a thread could be started from an operator
243 * that updates a driver */
244 /* NOTE: I have not seen any examples of code that does this
245 * so it may not be officially supported but seems to work ok. */
246 {
247 PyThreadState *ts = PyEval_SaveThread();
248#endif
249
250 operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports, is_undo);
251
252#ifdef BPY_RELEASE_GIL
253 /* regain GIL */
254 PyEval_RestoreThread(ts);
255 }
256#endif
257
258 error_val = BPy_reports_to_error(reports, PyExc_RuntimeError, false);
259
260 /* operator output is nice to have in the terminal/console too */
261 if (!BLI_listbase_is_empty(&reports->list)) {
262 /* Restore the print level as this is owned by the operator now. */
263 eReportType level = eReportType(reports->printlevel);
265 BPy_reports_write_stdout(reports, nullptr);
266 BKE_report_print_level_set(reports, level);
267 }
268
269 BKE_reports_clear(reports);
270 if ((reports->flag & RPT_FREE) == 0) {
271 BKE_reports_free(reports);
272 MEM_freeN(reports);
273 }
274 else {
275 /* The WM is now responsible for running the modal operator,
276 * show reports in the info window. */
277 reports->flag &= ~RPT_OP_HOLD;
278 }
279 }
280
282
283#if 0
284 /* if there is some way to know an operator takes args we should use this */
285 {
286 /* no props */
287 if (kw != nullptr) {
288 PyErr_Format(PyExc_AttributeError, "Operator \"%s\" does not take any args", opname);
289 return nullptr;
290 }
291
292 WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, nullptr, nullptr);
293 }
294#endif
295 }
296
297 if (error_val == -1) {
298 return nullptr;
299 }
300
301 /* When calling `bpy.ops.wm.read_factory_settings()` `bpy.data's` main pointer
302 * is freed by clear_globals(), further access will crash blender.
303 * Setting context is not needed in this case, only calling because this
304 * function corrects bpy.data (internal Main pointer) */
306
307 /* return operator_ret as a bpy enum */
309}
310
311static PyObject *pyop_as_string(PyObject * /*self*/, PyObject *args)
312{
314
315 const char *opname;
316 PyObject *kw = nullptr; /* optional args */
317 bool all_args = true;
318 bool macro_args = true;
319 int error_val = 0;
320
321 PyObject *pybuf;
322
324
325 if (C == nullptr) {
326 PyErr_SetString(PyExc_RuntimeError,
327 "Context is None, can't get the string representation of this object.");
328 return nullptr;
329 }
330
331 /* All arguments are positional. */
332 static const char *_keywords[] = {"", "", "", "", nullptr};
333 static _PyArg_Parser _parser = {
335 "s" /* `opname` */
336 "|" /* Optional arguments. */
337 "O!" /* `kw` */
338 "O&" /* `all_args` */
339 "O&" /* `macro_args` */
340 ":_bpy.ops.as_string",
341 _keywords,
342 nullptr,
343 };
344 if (!_PyArg_ParseTupleAndKeywordsFast(args,
345 nullptr,
346 &_parser,
347 &opname,
348 &PyDict_Type,
349 &kw,
351 &all_args,
353 &macro_args))
354 {
355 return nullptr;
356 }
357
358 ot = WM_operatortype_find(opname, true);
359
360 if (ot == nullptr) {
361 PyErr_Format(PyExc_AttributeError,
362 "_bpy.ops.as_string: operator \"%.200s\" "
363 "could not be found",
364 opname);
365 return nullptr;
366 }
367
368 // WM_operator_properties_create(&ptr, opname);
369 /* Save another lookup */
370 PointerRNA ptr = RNA_pointer_create(nullptr, ot->srna, nullptr);
371
372 if (kw && PyDict_Size(kw)) {
373 error_val = pyrna_pydict_to_props(
374 &ptr, kw, false, "Converting py args to operator properties:");
375 }
376
377 std::string op_string;
378 if (error_val == 0) {
379 op_string = WM_operator_pystring_ex(C, nullptr, all_args, macro_args, ot, &ptr);
380 }
381
383
384 if (error_val == -1) {
385 return nullptr;
386 }
387
388 if (!op_string.empty()) {
389 pybuf = PyUnicode_FromString(op_string.c_str());
390 }
391 else {
392 pybuf = PyUnicode_FromString("");
393 }
394
395 return pybuf;
396}
397
398static PyObject *pyop_dir(PyObject * /*self*/)
399{
401 PyObject *list = PyList_New(map.size());
402
403 int i = 0;
404 for (wmOperatorType *ot : map.values()) {
405 PyList_SET_ITEM(list, i, PyUnicode_FromString(ot->idname));
406 i++;
407 }
408
409 return list;
410}
411
412static PyObject *pyop_getrna_type(PyObject * /*self*/, PyObject *value)
413{
415 if ((ot = ot_lookup_from_py_string(value, "get_rna_type")) == nullptr) {
416 return nullptr;
417 }
418
419 PointerRNA ptr = RNA_pointer_create(nullptr, &RNA_Struct, ot->srna);
421 return (PyObject *)pyrna;
422}
423
424static PyObject *pyop_get_bl_options(PyObject * /*self*/, PyObject *value)
425{
427 if ((ot = ot_lookup_from_py_string(value, "get_bl_options")) == nullptr) {
428 return nullptr;
429 }
431}
432
433#if (defined(__GNUC__) && !defined(__clang__))
434# pragma GCC diagnostic push
435# pragma GCC diagnostic ignored "-Wcast-function-type"
436#endif
437
438static PyMethodDef bpy_ops_methods[] = {
439 {"poll", (PyCFunction)pyop_poll, METH_VARARGS, nullptr},
440 {"call", (PyCFunction)pyop_call, METH_VARARGS, nullptr},
441 {"as_string", (PyCFunction)pyop_as_string, METH_VARARGS, nullptr},
442 {"dir", (PyCFunction)pyop_dir, METH_NOARGS, nullptr},
443 {"get_rna_type", (PyCFunction)pyop_getrna_type, METH_O, nullptr},
444 {"get_bl_options", (PyCFunction)pyop_get_bl_options, METH_O, nullptr},
445 {"macro_define", (PyCFunction)PYOP_wrap_macro_define, METH_VARARGS, nullptr},
446 {nullptr, nullptr, 0, nullptr},
447};
448
449#if (defined(__GNUC__) && !defined(__clang__))
450# pragma GCC diagnostic pop
451#endif
452
453static PyModuleDef bpy_ops_module = {
454 /*m_base*/ PyModuleDef_HEAD_INIT,
455 /*m_name*/ "_bpy.ops",
456 /*m_doc*/ nullptr,
457 /*m_size*/ -1, /* multiple "initialization" just copies the module dict. */
458 /*m_methods*/ bpy_ops_methods,
459 /*m_slots*/ nullptr,
460 /*m_traverse*/ nullptr,
461 /*m_clear*/ nullptr,
462 /*m_free*/ nullptr,
463};
464
466{
467 PyObject *submodule;
468
469 submodule = PyModule_Create(&bpy_ops_module);
470
471 return submodule;
472}
void CTX_wm_operator_poll_msg_clear(bContext *C)
const char * CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
void BKE_reports_free(ReportList *reports)
Definition report.cc:69
void BKE_report_print_level_set(ReportList *reports, eReportType level)
Definition report.cc:237
void BKE_reports_clear(ReportList *reports)
Definition report.cc:81
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:54
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BPY_modules_update()
@ RPT_PRINT_HANDLED_BY_OWNER
Read Guarded memory(de)allocation.
wmOperatorCallContext
Definition WM_types.hh:216
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
void BPy_reports_write_stdout(const ReportList *reports, const char *header)
short BPy_reports_to_error(ReportList *reports, PyObject *exception, const bool clear)
struct bContext * BPY_context_get(void)
static PyObject * pyop_getrna_type(PyObject *, PyObject *value)
static PyObject * pyop_get_bl_options(PyObject *, PyObject *value)
static PyObject * pyop_dir(PyObject *)
static PyMethodDef bpy_ops_methods[]
static PyObject * pyop_call(PyObject *, PyObject *args)
static PyObject * pyop_poll(PyObject *, PyObject *args)
static PyObject * pyop_as_string(PyObject *, PyObject *args)
static PyModuleDef bpy_ops_module
static wmOperatorType * ot_lookup_from_py_string(PyObject *value, const char *py_fn_id)
PyObject * BPY_operator_module()
PyObject * PYOP_wrap_macro_define(PyObject *, PyObject *args)
bool pyrna_write_check()
Definition bpy_rna.cc:369
int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, const bool all_args, const char *error_prefix)
Definition bpy_rna.cc:1486
PyObject * pyrna_struct_CreatePyObject(PointerRNA *ptr)
Definition bpy_rna.cc:7694
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#define G(x, y, z)
PyObject * pyrna_enum_bitfield_as_set(const EnumPropertyItem *items, int value)
char * pyrna_enum_repr(const EnumPropertyItem *item)
int PyC_ParseBool(PyObject *o, void *p)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
header-only utilities
return ret
bool RNA_enum_value_from_id(const EnumPropertyItem *item, const char *identifier, int *r_value)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
const EnumPropertyItem rna_enum_operator_context_items[]
Definition rna_ui.cc:33
const EnumPropertyItem rna_enum_operator_type_flag_items[]
Definition rna_wm.cc:517
const EnumPropertyItem rna_enum_operator_return_items[]
Definition rna_wm.cc:562
const char * idname
Definition WM_types.hh:992
StructRNA * srna
Definition WM_types.hh:1080
bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
int WM_operator_call_py(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, ReportList *reports, const bool is_undo)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
const wmOperatorTypeMap & WM_operatortype_map()
std::string WM_operator_pystring_ex(bContext *C, wmOperator *op, const bool all_args, const bool macro_args, wmOperatorType *ot, PointerRNA *opptr)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)