37 PyConfig_InitPythonConfig(&config);
38 PyStatus
status = PyConfig_SetBytesArgv(&config, argc, (
char *
const *)argv);
39 PyObject *py_argv =
nullptr;
41 PyErr_Format(PyExc_ValueError,
"%s",
status.err_msg);
45 py_argv = PyList_New(config.argv.length);
46 for (Py_ssize_t
i = 0;
i < config.argv.length;
i++) {
47 PyList_SET_ITEM(py_argv,
i, PyUnicode_FromWideChar(config.argv.items[
i], -1));
50 PyConfig_Clear(&config);
65 PyGILState_STATE gilstate;
68 int exit_code = EXIT_FAILURE;
76 bool has_error =
false;
79 if (py_argv ==
nullptr) {
83 PyObject *exec_args = PyTuple_New(1);
84 PyTuple_SET_ITEM(exec_args, 0, py_argv);
86 PyObject *
result = PyObject_Call(py_exec_fn, exec_args,
nullptr);
93 if ((
result ==
nullptr) && PyErr_ExceptionMatches(PyExc_SystemExit)) {
94 PyObject *error_type, *error_value, *error_traceback;
95 PyErr_Fetch(&error_type, &error_value, &error_traceback);
96 if (PyObject_TypeCheck(error_value, (PyTypeObject *)PyExc_SystemExit) &&
97 (((PySystemExitObject *)error_value)->code !=
nullptr))
100 result = ((PySystemExitObject *)error_value)->code;
107 PyErr_Restore(error_type, error_value, error_traceback);
115 if (!PyLong_Check(
result)) {
116 PyErr_Format(PyExc_TypeError,
117 "Expected an int return value, not a %.200s",
118 Py_TYPE(
result)->tp_name);
122 const int exit_code_test = PyC_Long_AsI32(
result);
123 if ((exit_code_test == -1) && PyErr_Occurred()) {
124 exit_code = EXIT_SUCCESS;
128 exit_code = exit_code_test;
148 PyGILState_STATE gilstate = PyGILState_Ensure();
150 PyGILState_Release(gilstate);
187 bpy_cli_command_register_doc,
188 ".. method:: register_cli_command(id, execute)\n"
190 " Register a command, accessible via the (``-c`` / ``--command``) command-line argument.\n"
192 " :arg id: The command identifier (must pass an ``str.isidentifier`` check).\n"
194 " If the ``id`` is already registered, a warning is printed and "
195 "the command is inaccessible to prevent accidents invoking the wrong command.\n"
197 " :arg execute: Callback, taking a single list of strings and returns an int.\n"
198 " The arguments are built from all command-line arguments following the command id.\n"
199 " The return value should be 0 for success, 1 on failure "
200 "(specific error codes from the ``os`` module can also be used).\n"
201 " :type execute: callable\n"
202 " :return: The command handle which can be passed to :func:`unregister_cli_command`.\n"
204 " This uses Python's capsule type "
205 "however the result should be considered an opaque handle only used for unregistering.\n"
206 " :rtype: capsule\n");
210 PyObject *py_exec_fn;
212 static const char *_keywords[] = {
217 static _PyArg_Parser _parser = {
221 ":register_cli_command",
225 if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &PyUnicode_Type, &py_id, &py_exec_fn))
229 if (!PyUnicode_IsIdentifier(py_id)) {
230 PyErr_SetString(PyExc_ValueError,
"The command id is not a valid identifier");
233 if (!PyCallable_Check(py_exec_fn)) {
234 PyErr_SetString(PyExc_ValueError,
"The execute argument must be callable");
238 const char *
id = PyUnicode_AsUTF8(py_id);
240 std::unique_ptr<CommandHandler> cmd_ptr = std::make_unique<BPyCommandHandler>(
241 std::string(
id), Py_NewRef(py_exec_fn));
242 void *cmd_p = cmd_ptr.get();
251 bpy_cli_command_unregister_doc,
252 ".. method:: unregister_cli_command(handle)\n"
254 " Unregister a CLI command.\n"
256 " :arg handle: The return value of :func:`register_cli_command`.\n"
257 " :type handle: capsule\n");
260 if (!PyCapsule_CheckExact(value)) {
261 PyErr_Format(PyExc_TypeError,
262 "Expected a capsule returned from register_cli_command(...), found a: %.200s",
263 Py_TYPE(value)->tp_name);
269 if (cmd ==
nullptr) {
270 const char *capsule_name = PyCapsule_GetName(value);
272 PyErr_SetString(PyExc_ValueError,
"The command has already been removed");
275 PyErr_Format(PyExc_ValueError,
276 "Unrecognized capsule ID \"%.200s\"",
277 capsule_name ? capsule_name :
"<null>");
295# pragma clang diagnostic push
296# pragma clang diagnostic ignored "-Wcast-function-type"
298# pragma GCC diagnostic push
299# pragma GCC diagnostic ignored "-Wcast-function-type"
304 "register_cli_command",
306 METH_STATIC | METH_VARARGS | METH_KEYWORDS,
307 bpy_cli_command_register_doc,
310 "unregister_cli_command",
312 METH_STATIC | METH_O,
313 bpy_cli_command_unregister_doc,
318# pragma clang diagnostic pop
320# pragma GCC diagnostic pop
Blender CLI Generic --command Support.
bool BKE_blender_cli_command_unregister(CommandHandler *cmd)
void BKE_blender_cli_command_register(std::unique_ptr< CommandHandler > cmd)
void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate)
void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate)
PyMethodDef BPY_cli_command_register_def
static PyObject * py_argv_from_bytes(const int argc, const char **argv)
static void bpy_cli_command_free(PyObject *py_exec_fn)
static const char * bpy_cli_command_capsule_name
static int bpy_cli_command_exec(bContext *C, PyObject *py_exec_fn, const int argc, const char **argv)
static PyObject * bpy_cli_command_register(PyObject *, PyObject *args, PyObject *kw)
static PyObject * bpy_cli_command_unregister(PyObject *, PyObject *value)
PyDoc_STRVAR(bpy_cli_command_register_doc, ".. method:: register_cli_command(id, execute)\n" "\n" " Register a command, accessible via the (``-c`` / ``--command``) command-line argument.\n" "\n" " :arg id: The command identifier (must pass an ``str.isidentifier`` check).\n" "\n" " If the ``id`` is already registered, a warning is printed and " "the command is inaccessible to prevent accidents invoking the wrong command.\n" " :type id: str\n" " :arg execute: Callback, taking a single list of strings and returns an int.\n" " The arguments are built from all command-line arguments following the command id.\n" " The return value should be 0 for success, 1 on failure " "(specific error codes from the ``os`` module can also be used).\n" " :type execute: callable\n" " :return: The command handle which can be passed to :func:`unregister_cli_command`.\n" "\n" " This uses Python's capsule type " "however the result should be considered an opaque handle only used for unregistering.\n" " :rtype: capsule\n")
static const char * bpy_cli_command_capsule_name_invalid
PyMethodDef BPY_cli_command_unregister_def
int exec(bContext *C, int argc, const char **argv) override
BPyCommandHandler(const std::string &id, PyObject *py_exec_fn)
~BPyCommandHandler() override
CommandHandler(const std::string &id)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()