/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup pythonintern * * This file extends `bpy.types.Operator` with C/Python API methods and attributes. */ #include #include "BLI_string.h" #include "BKE_context.hh" #include "BPY_extern.hh" #include "bpy_capi_utils.hh" #include "bpy_rna_operator.hh" /* Own include, #BPY_rna_operator_poll_message_set_method_def. */ /* -------------------------------------------------------------------- */ /** \name Operator `poll_message_set` Method * \{ */ static char *pyop_poll_message_get_fn(bContext * /*C*/, void *user_data) { PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *py_args = static_cast(user_data); PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0); char *msg = nullptr; if (PyUnicode_Check(py_func_or_msg)) { Py_ssize_t msg_len; const char *msg_src = PyUnicode_AsUTF8AndSize(py_func_or_msg, &msg_len); msg = BLI_strdupn(msg_src, msg_len); } else { PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX); PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first); Py_DECREF(py_args_after_first); bool error = false; /* Null for no string. */ if (py_msg == nullptr) { error = true; } else { if (py_msg == Py_None) { /* Pass. */ } else if (PyUnicode_Check(py_msg)) { Py_ssize_t msg_src_len; const char *msg_src = PyUnicode_AsUTF8AndSize(py_msg, &msg_src_len); msg = BLI_strdupn(msg_src, msg_src_len); } else { PyErr_Format(PyExc_TypeError, "poll_message_set(function, ...): expected string or None, got %.200s", Py_TYPE(py_msg)->tp_name); error = true; } Py_DECREF(py_msg); } if (error) { PyErr_Print(); PyErr_Clear(); } } PyGILState_Release(gilstate); return msg; } static void pyop_poll_message_free_fn(bContext * /*C*/, void *user_data) { /* Handles the GIL. */ BPY_DECREF(user_data); } PyDoc_STRVAR( /* Wrap. */ BPY_rna_operator_poll_message_set_doc, ".. classmethod:: poll_message_set(message, *args)\n" "\n" " Set the message to show in the tool-tip when poll fails.\n" "\n" " When message is callable, " "additional user defined positional arguments are passed to the message function.\n" "\n" " :arg message: The message or a function that returns the message.\n" " :type message: str | Callable[[Any, ...], str | None]\n"); static PyObject *BPY_rna_operator_poll_message_set(PyObject * /*self*/, PyObject *args) { const Py_ssize_t args_len = PyTuple_GET_SIZE(args); if (args_len == 0) { PyErr_SetString(PyExc_ValueError, "poll_message_set(message, ...): requires a message argument"); return nullptr; } PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0); if (PyUnicode_Check(py_func_or_msg)) { if (args_len > 1) { PyErr_SetString(PyExc_ValueError, "poll_message_set(message): does not support additional arguments"); return nullptr; } } else if (PyCallable_Check(py_func_or_msg)) { /* pass */ } else { PyErr_Format(PyExc_TypeError, "poll_message_set(message, ...): " "expected at least 1 string or callable argument, got %.200s", Py_TYPE(py_func_or_msg)->tp_name); return nullptr; } bContext *C = BPY_context_get(); bContextPollMsgDyn_Params params{}; params.get_fn = pyop_poll_message_get_fn; params.free_fn = pyop_poll_message_free_fn; params.user_data = Py_NewRef(args); CTX_wm_operator_poll_msg_set_dynamic(C, ¶ms); Py_RETURN_NONE; } PyMethodDef BPY_rna_operator_poll_message_set_method_def = { "poll_message_set", (PyCFunction)BPY_rna_operator_poll_message_set, METH_VARARGS | METH_STATIC, BPY_rna_operator_poll_message_set_doc, }; /** \} */