This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/python/intern/bpy_operator.c
Campbell Barton 789b1617f7 Fix out of order event handling when calling operators from gizmos
Activating a gizmo used the windows eventstate which may have values
newer than the event used to activate the gizmo.

This meant transforms check for the key that activated transform
could be incorrect.

Support passing an event when calling operators to avoid this problem.
2022-03-11 22:49:47 +11:00

490 lines
14 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup pythonintern
*
* This file defines `_bpy.ops`, an internal python module which gives Python
* the ability to inspect and call operators (defined by C or Python).
*
* \note
* This C module is private, it should only be used by `release/scripts/modules/bpy/ops.py` which
* exposes operators as dynamically defined modules & callable objects to access all operators.
*/
#include <Python.h>
#include "RNA_types.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "../generic/py_capi_rna.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
#include "BPY_extern.h"
#include "bpy_capi_utils.h"
#include "bpy_operator.h"
#include "bpy_operator_wrap.h"
#include "bpy_rna.h" /* for setting argument properties & type method `get_rna_type`. */
#include "RNA_access.h"
#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_ghash.h"
#include "BKE_context.h"
#include "BKE_report.h"
/* so operators called can spawn threads which acquire the GIL */
#define BPY_RELEASE_GIL
static wmOperatorType *ot_lookup_from_py_string(PyObject *value, const char *py_fn_id)
{
const char *opname = PyUnicode_AsUTF8(value);
if (opname == NULL) {
PyErr_Format(PyExc_TypeError, "%s() expects a string argument", py_fn_id);
return NULL;
}
wmOperatorType *ot = WM_operatortype_find(opname, true);
if (ot == NULL) {
PyErr_Format(PyExc_KeyError, "%s(\"%s\") not found", py_fn_id, opname);
return NULL;
}
return ot;
}
static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args)
{
wmOperatorType *ot;
const char *opname;
PyObject *context_dict = NULL; /* optional args */
const char *context_str = NULL;
PyObject *ret;
wmOperatorCallContext context = WM_OP_EXEC_DEFAULT;
/* XXX TODO: work out a better solution for passing on context,
* could make a tuple from self and pack the name and Context into it. */
bContext *C = BPY_context_get();
if (C == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Context is None, can't poll any operators");
return NULL;
}
if (!PyArg_ParseTuple(args, "s|Os:_bpy.ops.poll", &opname, &context_dict, &context_str)) {
return NULL;
}
ot = WM_operatortype_find(opname, true);
if (ot == NULL) {
PyErr_Format(PyExc_AttributeError,
"Polling operator \"bpy.ops.%s\" error, "
"could not be found",
opname);
return NULL;
}
if (context_str) {
int context_int = context;
if (RNA_enum_value_from_id(rna_enum_operator_context_items, context_str, &context_int) == 0) {
char *enum_str = pyrna_enum_repr(rna_enum_operator_context_items);
PyErr_Format(PyExc_TypeError,
"Calling operator \"bpy.ops.%s.poll\" error, "
"expected a string enum in (%s)",
opname,
enum_str);
MEM_freeN(enum_str);
return NULL;
}
/* Copy back to the properly typed enum. */
context = context_int;
}
if (ELEM(context_dict, NULL, Py_None)) {
context_dict = NULL;
}
else if (!PyDict_Check(context_dict)) {
PyErr_Format(PyExc_TypeError,
"Calling operator \"bpy.ops.%s.poll\" error, "
"custom context expected a dict or None, got a %.200s",
opname,
Py_TYPE(context_dict)->tp_name);
return NULL;
}
struct bContext_PyState context_py_state;
if (context_dict != NULL) {
CTX_py_state_push(C, &context_py_state, (void *)context_dict);
Py_INCREF(context_dict); /* so we don't lose it */
}
/* main purpose of this function */
ret = WM_operator_poll_context((bContext *)C, ot, context) ? Py_True : Py_False;
if (context_dict != NULL) {
PyObject *context_dict_test = CTX_py_dict_get(C);
if (context_dict_test != context_dict) {
Py_DECREF(context_dict_test);
}
/* Restore with original context dict,
* probably NULL but need this for nested operator calls. */
Py_DECREF(context_dict);
CTX_py_state_pop(C, &context_py_state);
}
return Py_INCREF_RET(ret);
}
static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
{
wmOperatorType *ot;
int error_val = 0;
PointerRNA ptr;
int operator_ret = OPERATOR_CANCELLED;
const char *opname;
const char *context_str = NULL;
PyObject *kw = NULL; /* optional args */
PyObject *context_dict = NULL; /* optional args */
wmOperatorCallContext context = WM_OP_EXEC_DEFAULT;
int is_undo = false;
/* XXX TODO: work out a better solution for passing on context,
* could make a tuple from self and pack the name and Context into it. */
bContext *C = BPY_context_get();
if (C == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Context is None, can't poll any operators");
return NULL;
}
if (!PyArg_ParseTuple(args,
"sO|O!si:_bpy.ops.call",
&opname,
&context_dict,
&PyDict_Type,
&kw,
&context_str,
&is_undo)) {
return NULL;
}
ot = WM_operatortype_find(opname, true);
if (ot == NULL) {
PyErr_Format(PyExc_AttributeError,
"Calling operator \"bpy.ops.%s\" error, "
"could not be found",
opname);
return NULL;
}
if (!pyrna_write_check()) {
PyErr_Format(PyExc_RuntimeError,
"Calling operator \"bpy.ops.%s\" error, "
"can't modify blend data in this state (drawing/rendering)",
opname);
return NULL;
}
if (context_str) {
int context_int = context;
if (RNA_enum_value_from_id(rna_enum_operator_context_items, context_str, &context_int) == 0) {
char *enum_str = pyrna_enum_repr(rna_enum_operator_context_items);
PyErr_Format(PyExc_TypeError,
"Calling operator \"bpy.ops.%s\" error, "
"expected a string enum in (%s)",
opname,
enum_str);
MEM_freeN(enum_str);
return NULL;
}
/* Copy back to the properly typed enum. */
context = context_int;
}
if (ELEM(context_dict, NULL, Py_None)) {
context_dict = NULL;
}
else if (!PyDict_Check(context_dict)) {
PyErr_Format(PyExc_TypeError,
"Calling operator \"bpy.ops.%s\" error, "
"custom context expected a dict or None, got a %.200s",
opname,
Py_TYPE(context_dict)->tp_name);
return NULL;
}
/**
* It might be that there is already a Python context override. We don't want to remove that
* except when this operator call sets a new override explicitly. This is necessary so that
* called operator runs in the same context as the calling code by default.
*/
struct bContext_PyState context_py_state;
if (context_dict != NULL) {
CTX_py_state_push(C, &context_py_state, (void *)context_dict);
Py_INCREF(context_dict); /* so we don't lose it */
}
if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
bool msg_free = false;
const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
PyErr_Format(PyExc_RuntimeError,
"Operator bpy.ops.%.200s.poll() %.200s",
opname,
msg ? msg : "failed, context is incorrect");
CTX_wm_operator_poll_msg_clear(C);
if (msg_free) {
MEM_freeN((void *)msg);
}
error_val = -1;
}
else {
WM_operator_properties_create_ptr(&ptr, ot);
WM_operator_properties_sanitize(&ptr, 0);
if (kw && PyDict_Size(kw)) {
error_val = pyrna_pydict_to_props(
&ptr, kw, false, "Converting py args to operator properties: ");
}
if (error_val == 0) {
ReportList *reports;
reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
/* Own so these don't move into global reports. */
BKE_reports_init(reports, RPT_STORE | RPT_OP_HOLD);
#ifdef BPY_RELEASE_GIL
/* release GIL, since a thread could be started from an operator
* that updates a driver */
/* NOTE: I have not seen any examples of code that does this
* so it may not be officially supported but seems to work ok. */
{
PyThreadState *ts = PyEval_SaveThread();
#endif
operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports, is_undo);
#ifdef BPY_RELEASE_GIL
/* regain GIL */
PyEval_RestoreThread(ts);
}
#endif
error_val = BPy_reports_to_error(reports, PyExc_RuntimeError, false);
/* operator output is nice to have in the terminal/console too */
if (!BLI_listbase_is_empty(&reports->list)) {
BPy_reports_write_stdout(reports, NULL);
}
BKE_reports_clear(reports);
if ((reports->flag & RPT_FREE) == 0) {
MEM_freeN(reports);
}
else {
/* The WM is now responsible for running the modal operator,
* show reports in the info window. */
reports->flag &= ~RPT_OP_HOLD;
}
}
WM_operator_properties_free(&ptr);
#if 0
/* if there is some way to know an operator takes args we should use this */
{
/* no props */
if (kw != NULL) {
PyErr_Format(PyExc_AttributeError, "Operator \"%s\" does not take any args", opname);
return NULL;
}
WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL, NULL);
}
#endif
}
if (context_dict != NULL) {
PyObject *context_dict_test = CTX_py_dict_get(C);
if (context_dict_test != context_dict) {
Py_DECREF(context_dict_test);
}
/* Restore with original context dict,
* probably NULL but need this for nested operator calls. */
Py_DECREF(context_dict);
CTX_py_state_pop(C, &context_py_state);
}
if (error_val == -1) {
return NULL;
}
/* When calling `bpy.ops.wm.read_factory_settings()` `bpy.data's` main pointer
* is freed by clear_globals(), further access will crash blender.
* Setting context is not needed in this case, only calling because this
* function corrects bpy.data (internal Main pointer) */
BPY_modules_update();
/* return operator_ret as a bpy enum */
return pyrna_enum_bitfield_as_set(rna_enum_operator_return_items, operator_ret);
}
static PyObject *pyop_as_string(PyObject *UNUSED(self), PyObject *args)
{
wmOperatorType *ot;
PointerRNA ptr;
const char *opname;
PyObject *kw = NULL; /* optional args */
bool all_args = true;
bool macro_args = true;
int error_val = 0;
char *buf = NULL;
PyObject *pybuf;
bContext *C = BPY_context_get();
if (C == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"Context is None, can't get the string representation of this object.");
return NULL;
}
if (!PyArg_ParseTuple(args,
"s|O!O&O&:_bpy.ops.as_string",
&opname,
&PyDict_Type,
&kw,
PyC_ParseBool,
&all_args,
PyC_ParseBool,
&macro_args)) {
return NULL;
}
ot = WM_operatortype_find(opname, true);
if (ot == NULL) {
PyErr_Format(PyExc_AttributeError,
"_bpy.ops.as_string: operator \"%.200s\" "
"could not be found",
opname);
return NULL;
}
// WM_operator_properties_create(&ptr, opname);
/* Save another lookup */
RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
if (kw && PyDict_Size(kw)) {
error_val = pyrna_pydict_to_props(
&ptr, kw, false, "Converting py args to operator properties: ");
}
if (error_val == 0) {
buf = WM_operator_pystring_ex(C, NULL, all_args, macro_args, ot, &ptr);
}
WM_operator_properties_free(&ptr);
if (error_val == -1) {
return NULL;
}
if (buf) {
pybuf = PyUnicode_FromString(buf);
MEM_freeN(buf);
}
else {
pybuf = PyUnicode_FromString("");
}
return pybuf;
}
static PyObject *pyop_dir(PyObject *UNUSED(self))
{
GHashIterator iter;
PyObject *list;
int i;
WM_operatortype_iter(&iter);
list = PyList_New(BLI_ghash_len(iter.gh));
for (i = 0; !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter), i++) {
wmOperatorType *ot = BLI_ghashIterator_getValue(&iter);
PyList_SET_ITEM(list, i, PyUnicode_FromString(ot->idname));
}
return list;
}
static PyObject *pyop_getrna_type(PyObject *UNUSED(self), PyObject *value)
{
wmOperatorType *ot;
if ((ot = ot_lookup_from_py_string(value, "get_rna_type")) == NULL) {
return NULL;
}
PointerRNA ptr;
RNA_pointer_create(NULL, &RNA_Struct, ot->srna, &ptr);
BPy_StructRNA *pyrna = (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ptr);
return (PyObject *)pyrna;
}
static PyObject *pyop_get_bl_options(PyObject *UNUSED(self), PyObject *value)
{
wmOperatorType *ot;
if ((ot = ot_lookup_from_py_string(value, "get_bl_options")) == NULL) {
return NULL;
}
return pyrna_enum_bitfield_as_set(rna_enum_operator_type_flag_items, ot->flag);
}
static struct PyMethodDef bpy_ops_methods[] = {
{"poll", (PyCFunction)pyop_poll, METH_VARARGS, NULL},
{"call", (PyCFunction)pyop_call, METH_VARARGS, NULL},
{"as_string", (PyCFunction)pyop_as_string, METH_VARARGS, NULL},
{"dir", (PyCFunction)pyop_dir, METH_NOARGS, NULL},
{"get_rna_type", (PyCFunction)pyop_getrna_type, METH_O, NULL},
{"get_bl_options", (PyCFunction)pyop_get_bl_options, METH_O, NULL},
{"macro_define", (PyCFunction)PYOP_wrap_macro_define, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef bpy_ops_module = {
PyModuleDef_HEAD_INIT,
"_bpy.ops",
NULL,
-1, /* multiple "initialization" just copies the module dict. */
bpy_ops_methods,
NULL,
NULL,
NULL,
NULL,
};
PyObject *BPY_operator_module(void)
{
PyObject *submodule;
submodule = PyModule_Create(&bpy_ops_module);
return submodule;
}