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