Files
test/source/blender/python/intern/bpy_app.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

736 lines
22 KiB
C++
Raw Permalink Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
2010-01-31 23:41:46 +00:00
/** \file
* \ingroup pythonintern
*
* This file defines a 'PyStructSequence' accessed via 'bpy.app', mostly
* exposing static applications variables such as version and buildinfo
* however some writable variables have been added such as 'debug' and 'tempdir'
2011-02-27 20:10:08 +00:00
*/
#include <Python.h>
#include "bpy_app.hh"
#include "bpy_app_alembic.hh"
#include "bpy_app_build_options.hh"
#include "bpy_app_ffmpeg.hh"
#include "bpy_app_ocio.hh"
#include "bpy_app_oiio.hh"
#include "bpy_app_opensubdiv.hh"
#include "bpy_app_openvdb.hh"
#include "bpy_app_sdl.hh"
#include "bpy_app_usd.hh"
#include "bpy_app_translations.hh"
#include "bpy_app_handlers.hh"
#include "bpy_capi_utils.hh"
#include "bpy_driver.hh"
2010-01-31 23:41:46 +00:00
#include "BPY_extern_python.hh" /* For #BPY_python_app_help_text_fn. */
/* modules */
#include "bpy_app_icons.hh"
#include "bpy_app_timers.hh"
#include "BLI_utildefines.h"
#include "BKE_appdir.hh"
#include "BKE_blender_version.h"
#include "BKE_global.hh"
#include "BKE_main.hh"
2010-01-31 23:41:46 +00:00
#include "GPU_shader.hh"
#include "UI_interface_icons.hh"
#include "ED_undo.hh"
#include "MEM_guardedalloc.h"
#include "RNA_enum_types.hh" /* For `rna_enum_wm_job_type_items`. */
/* for notifiers */
#include "WM_api.hh"
#include "WM_types.hh"
#include "../generic/py_capi_rna.hh"
#include "../generic/py_capi_utils.hh"
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
2010-01-31 23:41:46 +00:00
#ifdef BUILD_DATE
extern "C" char build_date[];
extern "C" char build_time[];
extern "C" ulong build_commit_timestamp;
extern "C" char build_commit_date[];
extern "C" char build_commit_time[];
extern "C" char build_hash[];
extern "C" char build_branch[];
extern "C" char build_platform[];
extern "C" char build_type[];
extern "C" char build_cflags[];
extern "C" char build_cxxflags[];
extern "C" char build_linkflags[];
extern "C" char build_system[];
2010-01-31 23:41:46 +00:00
#endif
static PyTypeObject BlenderAppType;
static PyStructSequence_Field app_info_fields[] = {
{"version",
"The Blender version as a tuple of 3 numbers (major, minor, micro). eg. (4, 3, 1)"},
{"version_file",
"The Blender File version, as a tuple of 3 numbers (major, minor, file sub-version), that "
"will be used to save a .blend file. The last item in this tuple indicates the file "
"sub-version, which is different from the release micro version (the last item of the "
"``bpy.app.version`` tuple). The file sub-version can be incremented multiple times while a "
"Blender version is under development. This value is, and should be, used for handling "
"compatibility changes between Blender versions"},
{"version_string", "The Blender version formatted as a string"},
{"version_cycle", "The release status of this build alpha/beta/rc/release"},
{"background",
"Boolean, True when blender is running without a user interface (started with -b)"},
{"module", "Boolean, True when running Blender as a python module"},
{"factory_startup", "Boolean, True when blender is running with --factory-startup)"},
{"portable", "Boolean, True unless blender was built to reference absolute paths (on UNIX)."},
2010-01-31 23:41:46 +00:00
/* buildinfo */
{"build_date", "The date this blender instance was built"},
{"build_time", "The time this blender instance was built"},
{"build_commit_timestamp", "The unix timestamp of commit this blender instance was built"},
{"build_commit_date", "The date of commit this blender instance was built"},
{"build_commit_time", "The time of commit this blender instance was built"},
{"build_hash", "The commit hash this blender instance was built with"},
{"build_branch", "The branch this blender instance was built from"},
{"build_platform", "The platform this blender instance was built for"},
{"build_type", "The type of build (Release, Debug)"},
{"build_cflags", "C compiler flags"},
{"build_cxxflags", "C++ compiler flags"},
{"build_linkflags", "Binary linking flags"},
{"build_system", "Build system used"},
/* submodules */
{"alembic", "Alembic library information backend"},
{"usd", "USD library information backend"},
{"ffmpeg", "FFmpeg library information backend"},
{"ocio", "OpenColorIO library information backend"},
{"oiio", "OpenImageIO library information backend"},
{"opensubdiv", "OpenSubdiv library information backend"},
{"openvdb", "OpenVDB library information backend"},
{"sdl", "SDL library information backend"},
{"build_options", "A set containing most important enabled optional build features"},
{"handlers", "Application handler callbacks"},
{"translations", "Application and addons internationalization API"},
/* Modules (not struct sequence). */
{"icons", "Manage custom icons"},
{"timers", "Manage timers"},
{nullptr},
2010-01-31 23:41:46 +00:00
};
PyDoc_STRVAR(
/* Wrap. */
bpy_app_doc,
"This module contains application values that remain unchanged during runtime.\n");
static PyStructSequence_Desc app_info_desc = {
/*name*/ "bpy.app",
/*doc*/ bpy_app_doc,
/*fields*/ app_info_fields,
/*n_in_sequence*/ ARRAY_SIZE(app_info_fields) - 1,
2010-01-31 23:41:46 +00:00
};
static PyObject *make_app_info()
2010-01-31 23:41:46 +00:00
{
PyObject *app_info;
int pos = 0;
app_info = PyStructSequence_New(&BlenderAppType);
if (app_info == nullptr) {
return nullptr;
2010-01-31 23:41:46 +00:00
}
#define SetIntItem(flag) PyStructSequence_SET_ITEM(app_info, pos++, PyLong_FromLong(flag))
#define SetStrItem(str) PyStructSequence_SET_ITEM(app_info, pos++, PyUnicode_FromString(str))
#define SetBytesItem(str) PyStructSequence_SET_ITEM(app_info, pos++, PyBytes_FromString(str))
2010-01-31 23:41:46 +00:00
#define SetObjItem(obj) PyStructSequence_SET_ITEM(app_info, pos++, obj)
SetObjItem(
PyC_Tuple_Pack_I32({BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_VERSION_PATCH}));
SetObjItem(PyC_Tuple_Pack_I32(
{BLENDER_FILE_VERSION / 100, BLENDER_FILE_VERSION % 100, BLENDER_FILE_SUBVERSION}));
SetStrItem(BKE_blender_version_string());
SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE));
SetObjItem(PyBool_FromLong(G.background));
#ifdef WITH_PYTHON_MODULE
SetObjItem(Py_NewRef(Py_True));
#else
SetObjItem(Py_NewRef(Py_False));
#endif
SetObjItem(PyBool_FromLong(G.factory_startup));
#ifdef WITH_INSTALL_PORTABLE
SetObjItem(Py_NewRef(Py_True));
#else
SetObjItem(Py_NewRef(Py_False));
#endif
/* build info, use bytes since we can't assume _any_ encoding:
* see patch #30154 for issue */
#ifdef BUILD_DATE
SetBytesItem(build_date);
SetBytesItem(build_time);
SetIntItem(build_commit_timestamp);
SetBytesItem(build_commit_date);
SetBytesItem(build_commit_time);
SetBytesItem(build_hash);
SetBytesItem(build_branch);
SetBytesItem(build_platform);
SetBytesItem(build_type);
SetBytesItem(build_cflags);
SetBytesItem(build_cxxflags);
SetBytesItem(build_linkflags);
SetBytesItem(build_system);
#else
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetIntItem(0);
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
SetBytesItem("Unknown");
#endif
2010-01-31 23:41:46 +00:00
SetObjItem(BPY_app_alembic_struct());
SetObjItem(BPY_app_usd_struct());
SetObjItem(BPY_app_ffmpeg_struct());
SetObjItem(BPY_app_ocio_struct());
SetObjItem(BPY_app_oiio_struct());
SetObjItem(BPY_app_opensubdiv_struct());
SetObjItem(BPY_app_openvdb_struct());
SetObjItem(BPY_app_sdl_struct());
SetObjItem(BPY_app_build_options_struct());
SetObjItem(BPY_app_handlers_struct());
SetObjItem(BPY_app_translations_struct());
/* modules */
SetObjItem(BPY_app_icons_module());
SetObjItem(BPY_app_timers_module());
2010-01-31 23:41:46 +00:00
#undef SetIntItem
#undef SetStrItem
#undef SetBytesItem
2010-01-31 23:41:46 +00:00
#undef SetObjItem
if (PyErr_Occurred()) {
Py_DECREF(app_info);
return nullptr;
2010-01-31 23:41:46 +00:00
}
return app_info;
}
/* a few getsets because it makes sense for them to be in bpy.app even though
* they are not static */
PyDoc_STRVAR(
/* Wrap. */
bpy_app_debug_doc,
"Boolean, for debug info "
"(started with ``--debug`` / ``--debug-*`` matching this attribute name).");
static PyObject *bpy_app_debug_get(PyObject * /*self*/, void *closure)
{
const int flag = POINTER_AS_INT(closure);
return PyBool_FromLong(G.debug & flag);
}
static int bpy_app_debug_set(PyObject * /*self*/, PyObject *value, void *closure)
{
const int flag = POINTER_AS_INT(closure);
const int param = PyObject_IsTrue(value);
if (param == -1) {
PyErr_SetString(PyExc_TypeError, "bpy.app.debug can only be True/False");
return -1;
}
if (param) {
G.debug |= flag;
}
else {
G.debug &= ~flag;
}
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_internet_offline_doc,
"Boolean, true when internet access is allowed by Blender & 3rd party scripts "
"(read-only).");
PyDoc_STRVAR(
/* Wrap. */
bpy_app_internet_offline_override_doc,
"Boolean, true when internet access preference is overridden by the command line "
"(read-only).");
PyDoc_STRVAR(
/* Wrap. */
bpy_app_global_flag_doc,
"Boolean, for application behavior "
"(started with ``--enable-*`` matching this attribute name)");
static PyObject *bpy_app_global_flag_get(PyObject * /*self*/, void *closure)
{
const int flag = POINTER_AS_INT(closure);
return PyBool_FromLong(G.f & flag);
}
static int bpy_app_global_flag_set(PyObject * /*self*/, PyObject *value, void *closure)
{
const int flag = POINTER_AS_INT(closure);
const int param = PyObject_IsTrue(value);
if (param == -1) {
PyErr_SetString(PyExc_TypeError, "bpy.app.use_* can only be True/False");
return -1;
}
if (param) {
G.f |= flag;
}
else {
G.f &= ~flag;
}
return 0;
}
static int bpy_app_global_flag_set__only_disable(PyObject * /*self*/,
PyObject *value,
void *closure)
{
const int param = PyObject_IsTrue(value);
if (param == 1) {
PyErr_SetString(PyExc_ValueError, "This bpy.app.use_* option can only be disabled");
return -1;
}
return bpy_app_global_flag_set(nullptr, value, closure);
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_debug_value_doc,
"Short, number which can be set to non-zero values for testing purposes.");
static PyObject *bpy_app_debug_value_get(PyObject * /*self*/, void * /*closure*/)
{
return PyLong_FromLong(G.debug_value);
}
static int bpy_app_debug_value_set(PyObject * /*self*/, PyObject *value, void * /*closure*/)
{
const short param = PyC_Long_AsI16(value);
if (param == -1 && PyErr_Occurred()) {
PyC_Err_SetString_Prefix(PyExc_TypeError,
"bpy.app.debug_value can only be set to a whole number");
return -1;
}
G.debug_value = param;
WM_main_add_notifier(NC_WINDOW, nullptr);
return 0;
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_tempdir_doc,
"String, the temp directory used by blender (read-only).");
static PyObject *bpy_app_tempdir_get(PyObject * /*self*/, void * /*closure*/)
{
return PyC_UnicodeFromBytes(BKE_tempdir_session());
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_driver_dict_doc,
"Dictionary for drivers namespace, editable in-place, reset on file load (read-only).");
static PyObject *bpy_app_driver_dict_get(PyObject * /*self*/, void * /*closure*/)
{
if (bpy_pydriver_Dict == nullptr) {
if (bpy_pydriver_create_dict() != 0) {
PyErr_SetString(PyExc_RuntimeError, "bpy.app.driver_namespace failed to create dictionary");
return nullptr;
2012-03-26 20:41:54 +00:00
}
}
return Py_NewRef(bpy_pydriver_Dict);
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_preview_render_size_doc,
"Reference size for icon/preview renders (read-only).");
static PyObject *bpy_app_preview_render_size_get(PyObject * /*self*/, void *closure)
{
return PyLong_FromLong(
long(UI_icon_preview_to_render_size(eIconSizes(POINTER_AS_INT(closure)))));
}
static PyObject *bpy_app_autoexec_fail_message_get(PyObject * /*self*/, void * /*closure*/)
{
return PyC_UnicodeFromBytes(G.autoexec_fail);
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_python_args_doc,
"Leading arguments to use when calling Python directly (via ``sys.executable``). "
"These arguments match settings Blender uses to "
"ensure Python runs with a compatible environment (read-only).");
static PyObject *bpy_app_python_args_get(PyObject * /*self*/, void * /*closure*/)
{
const char *args[1];
int args_num = 0;
if (!BPY_python_use_system_env_get()) {
/* Isolated Python environment. */
args[args_num++] = "-I";
}
return PyC_Tuple_PackArray_String(args, args_num);
}
PyDoc_STRVAR(
/* Wrap. */
bpy_app_binary_path_doc,
"The location of Blender's executable, useful for utilities that open new instances. "
"Read-only unless Blender is built as a Python module - in this case the value is "
"an empty string which script authors may point to a Blender binary.");
static PyObject *bpy_app_binary_path_get(PyObject * /*self*/, void * /*closure*/)
{
return PyC_UnicodeFromBytes(BKE_appdir_program_path());
}
static int bpy_app_binary_path_set(PyObject * /*self*/, PyObject *value, void * /*closure*/)
{
#ifndef WITH_PYTHON_MODULE
PyErr_SetString(PyExc_AttributeError,
"bpy.app.binary_path is only writable when built as a Python module");
return -1;
#endif
PyObject *value_coerce = nullptr;
const char *filepath = PyC_UnicodeAsBytes(value, &value_coerce);
if (filepath == nullptr) {
PyErr_Format(PyExc_ValueError, "expected a string or bytes, got %s", Py_TYPE(value)->tp_name);
return -1;
}
BKE_appdir_program_path_init(filepath);
Py_XDECREF(value_coerce);
return 0;
}
static PyGetSetDef bpy_app_getsets[] = {
{"debug", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG},
{"debug_freestyle",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_FREESTYLE},
{"debug_python",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_PYTHON},
{"debug_events",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_EVENTS},
{"debug_handlers",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_HANDLERS},
{"debug_wm", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG_WM},
{"debug_depsgraph",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_DEPSGRAPH},
{"debug_depsgraph_build",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_DEPSGRAPH_BUILD},
{"debug_depsgraph_eval",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_DEPSGRAPH_EVAL},
{"debug_depsgraph_tag",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_DEPSGRAPH_TAG},
{"debug_depsgraph_time",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_DEPSGRAPH_TIME},
{"debug_depsgraph_pretty",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_DEPSGRAPH_PRETTY},
{"debug_simdata",
bpy_app_debug_get,
bpy_app_debug_set,
bpy_app_debug_doc,
(void *)G_DEBUG_SIMDATA},
{"debug_io", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG_IO},
{"use_event_simulate",
bpy_app_global_flag_get,
bpy_app_global_flag_set__only_disable,
bpy_app_global_flag_doc,
(void *)G_FLAG_EVENT_SIMULATE},
{"use_userpref_skip_save_on_exit",
bpy_app_global_flag_get,
bpy_app_global_flag_set,
bpy_app_global_flag_doc,
(void *)G_FLAG_USERPREF_NO_SAVE_ON_EXIT},
{"debug_value",
bpy_app_debug_value_get,
bpy_app_debug_value_set,
bpy_app_debug_value_doc,
nullptr},
{"tempdir", bpy_app_tempdir_get, nullptr, bpy_app_tempdir_doc, nullptr},
{"driver_namespace", bpy_app_driver_dict_get, nullptr, bpy_app_driver_dict_doc, nullptr},
{"render_icon_size",
2016-01-09 22:56:28 +11:00
bpy_app_preview_render_size_get,
nullptr,
bpy_app_preview_render_size_doc,
2016-01-09 22:56:28 +11:00
(void *)ICON_SIZE_ICON},
{"render_preview_size",
2016-01-09 22:56:28 +11:00
bpy_app_preview_render_size_get,
nullptr,
bpy_app_preview_render_size_doc,
2016-01-09 22:56:28 +11:00
(void *)ICON_SIZE_PREVIEW},
{"online_access",
bpy_app_global_flag_get,
nullptr,
bpy_app_internet_offline_doc,
(void *)G_FLAG_INTERNET_ALLOW},
{"online_access_override",
bpy_app_global_flag_get,
nullptr,
bpy_app_internet_offline_override_doc,
(void *)G_FLAG_INTERNET_OVERRIDE_PREF_ANY},
/* security */
{"autoexec_fail",
bpy_app_global_flag_get,
nullptr,
nullptr,
(void *)G_FLAG_SCRIPT_AUTOEXEC_FAIL},
{"autoexec_fail_quiet",
bpy_app_global_flag_get,
nullptr,
nullptr,
(void *)G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET},
{"autoexec_fail_message", bpy_app_autoexec_fail_message_get, nullptr, nullptr, nullptr},
{"python_args", bpy_app_python_args_get, nullptr, bpy_app_python_args_doc, nullptr},
/* Support script authors setting the Blender binary path to use, otherwise this value
* is not known when built as a Python module. */
{"binary_path",
bpy_app_binary_path_get,
bpy_app_binary_path_set,
bpy_app_binary_path_doc,
nullptr},
{nullptr, nullptr, nullptr, nullptr, nullptr},
};
PyDoc_STRVAR(
/* Wrap. */
bpy_app_is_job_running_doc,
".. staticmethod:: is_job_running(job_type)\n"
"\n"
" Check whether a job of the given type is running.\n"
"\n"
" :arg job_type: job type in :ref:`rna_enum_wm_job_type_items`.\n"
" :type job_type: str\n"
" :return: Whether a job of the given type is currently running.\n"
" :rtype: bool\n");
static PyObject *bpy_app_is_job_running(PyObject * /*self*/, PyObject *args, PyObject *kwds)
{
BPy_EnumProperty_Parse job_type_enum{};
job_type_enum.items = rna_enum_wm_job_type_items;
job_type_enum.value = 0;
static const char *_keywords[] = {"job_type", nullptr};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `job_type` */
":is_job_running",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kwds, &_parser, pyrna_enum_value_parse_string, &job_type_enum))
{
return nullptr;
}
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
if (job_type_enum.value == WM_JOB_TYPE_SHADER_COMPILATION) {
/* Shader compilation no longer uses the WM_job API, so we handle this as a special case
* to avoid breaking the Python API. */
return PyBool_FromLong(GPU_shader_batch_is_compiling());
}
return PyBool_FromLong(WM_jobs_has_running_type(wm, job_type_enum.value));
}
char *(*BPY_python_app_help_text_fn)(bool all) = nullptr;
PyDoc_STRVAR(
/* Wrap. */
bpy_app_help_text_doc,
".. staticmethod:: help_text(*, all=False)\n"
"\n"
" Return the help text as a string.\n"
"\n"
" :arg all: Return all arguments, "
"even those which aren't available for the current platform.\n"
" :type all: bool\n");
static PyObject *bpy_app_help_text(PyObject * /*self*/, PyObject *args, PyObject *kwds)
{
bool all = false;
static const char *_keywords[] = {"all", nullptr};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"|$" /* Optional keyword only arguments. */
"O&" /* `all` */
":help_text",
_keywords,
2023-08-03 19:14:53 +10:00
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &all)) {
return nullptr;
}
char *buf = BPY_python_app_help_text_fn(all);
PyObject *result = PyUnicode_FromString(buf);
MEM_freeN(buf);
return result;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
PyDoc_STRVAR(
/* Wrap. */
bpy_app_memory_usage_undo_doc,
".. staticmethod:: memory_usage_undo()\n"
"\n"
" Get undo memory usage information.\n"
"\n"
" :return: Memory usage of the undo stack in bytes.\n"
" :rtype: int\n");
static PyObject *bpy_app_memory_usage_undo(PyObject * /*self*/, PyObject * /*args*/)
{
size_t total_memory = 0;
UndoStack *ustack = ED_undo_stack_get();
if (ustack) {
total_memory = ED_undosys_total_memory_calc(ustack);
}
return PyLong_FromSize_t(total_memory);
}
static PyMethodDef bpy_app_methods[] = {
{"is_job_running",
(PyCFunction)bpy_app_is_job_running,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
bpy_app_is_job_running_doc},
{"help_text",
(PyCFunction)bpy_app_help_text,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
bpy_app_help_text_doc},
{"memory_usage_undo",
(PyCFunction)bpy_app_memory_usage_undo,
METH_NOARGS | METH_STATIC,
bpy_app_memory_usage_undo_doc},
{nullptr, nullptr, 0, nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
static void py_struct_seq_getset_init()
{
/* tricky dynamic members, not to py-spec! */
for (PyGetSetDef *getset = bpy_app_getsets; getset->name; getset++) {
PyObject *item = PyDescr_NewGetSet(&BlenderAppType, getset);
PyDict_SetItem(BlenderAppType.tp_dict, PyDescr_NAME(item), item);
Py_DECREF(item);
}
}
static void py_struct_seq_method_init()
{
for (PyMethodDef *method = bpy_app_methods; method->ml_name; method++) {
BLI_assert_msg(method->ml_flags & METH_STATIC, "Only static methods make sense for 'bpy.app'");
PyObject *item = PyCFunction_New(method, nullptr);
PyDict_SetItemString(BlenderAppType.tp_dict, method->ml_name, item);
Py_DECREF(item);
}
}
/* end dynamic bpy.app */
PyObject *BPY_app_struct()
2010-01-31 23:41:46 +00:00
{
PyObject *ret;
2010-01-31 23:41:46 +00:00
PyStructSequence_InitType(&BlenderAppType, &app_info_desc);
ret = make_app_info();
2010-01-31 23:41:46 +00:00
/* prevent user from creating new instances */
BlenderAppType.tp_init = nullptr;
BlenderAppType.tp_new = nullptr;
2023-05-24 11:21:18 +10:00
/* Without this we can't do `set(sys.modules)` #29635. */
BlenderAppType.tp_hash = (hashfunc)Py_HashPointer;
2022-09-16 18:13:19 +10:00
/* Kind of a hack on top of #PyStructSequence. */
py_struct_seq_getset_init();
py_struct_seq_method_init();
return ret;
2010-01-31 23:41:46 +00:00
}