diff options
Diffstat (limited to 'source/blender/python/intern')
31 files changed, 482 insertions, 328 deletions
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 6c3f422d3f0..772b31fd9d8 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -341,6 +341,8 @@ if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() -add_definitions(${GL_DEFINITIONS}) +if(WITH_POTRACE) + add_definitions(-DWITH_POTRACE) +endif() blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index de8fd87db58..804a28d0ebc 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -35,6 +35,8 @@ #include "RNA_access.h" #include "RNA_types.h" +#include "GPU_state.h" + #include "bpy.h" #include "bpy_app.h" #include "bpy_capi_utils.h" @@ -145,7 +147,7 @@ static PyObject *bpy_blend_paths(PyObject *UNUSED(self), PyObject *args, PyObjec return list; } -// PyDoc_STRVAR(bpy_user_resource_doc[] = // now in bpy/utils.py +// PyDoc_STRVAR(bpy_user_resource_doc[] = /* now in bpy/utils.py */ static PyObject *bpy_user_resource(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { const struct PyC_StringEnumItems type_items[] = { @@ -330,6 +332,9 @@ static PyMethodDef meth_bpy_escape_identifier = { static PyObject *bpy_import_test(const char *modname) { PyObject *mod = PyImport_ImportModuleLevel(modname, NULL, NULL, NULL, 0); + + GPU_bgl_end(); + if (mod) { Py_DECREF(mod); } @@ -344,7 +349,7 @@ static PyObject *bpy_import_test(const char *modname) /****************************************************************************** * Description: Creates the bpy module and adds it to sys.modules for importing ******************************************************************************/ -void BPy_init_modules(void) +void BPy_init_modules(struct bContext *C) { PointerRNA ctx_ptr; PyObject *mod; @@ -395,8 +400,7 @@ void BPy_init_modules(void) PyModule_AddObject(mod, "_utils_previews", BPY_utils_previews_module()); PyModule_AddObject(mod, "msgbus", BPY_msgbus_module()); - /* bpy context */ - RNA_pointer_create(NULL, &RNA_Context, (void *)BPy_GetContext(), &ctx_ptr); + RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr); bpy_context_module = (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ctx_ptr); /* odd that this is needed, 1 ref on creation and another for the module * but without we get a crash on exit */ diff --git a/source/blender/python/intern/bpy.h b/source/blender/python/intern/bpy.h index 744bf903443..25a047edfb5 100644 --- a/source/blender/python/intern/bpy.h +++ b/source/blender/python/intern/bpy.h @@ -24,7 +24,9 @@ extern "C" { #endif -void BPy_init_modules(void); +struct bContext; + +void BPy_init_modules(struct bContext *C); extern PyObject *bpy_package_py; /* bpy_interface_atexit.c */ @@ -33,6 +35,7 @@ void BPY_atexit_unregister(void); extern struct CLG_LogRef *BPY_LOG_CONTEXT; extern struct CLG_LogRef *BPY_LOG_RNA; +extern struct CLG_LogRef *BPY_LOG_INTERFACE; #ifdef __cplusplus } diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index f0de05f95b3..02ab001dbf6 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -82,6 +82,7 @@ static PyTypeObject BlenderAppType; static PyStructSequence_Field app_info_fields[] = { {"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"}, + {"version_file", "The blend file version, compatible with ``bpy.data.version``"}, {"version_string", "The Blender version formatted as a string"}, {"version_cycle", "The release status of this build alpha/beta/rc/release"}, {"version_char", "Deprecated, always an empty string"}, @@ -126,17 +127,7 @@ static PyStructSequence_Field app_info_fields[] = { }; PyDoc_STRVAR(bpy_app_doc, - "This module contains application values that remain unchanged during runtime.\n" - "\n" - "Submodules:\n" - "\n" - ".. toctree::\n" - " :maxdepth: 1\n" - "\n" - " bpy.app.handlers.rst\n" - " bpy.app.icons.rst\n" - " bpy.app.timers.rst\n" - " bpy.app.translations.rst\n"); + "This module contains application values that remain unchanged during runtime."); static PyStructSequence_Desc app_info_desc = { "bpy.app", /* name */ @@ -161,6 +152,8 @@ static PyObject *make_app_info(void) 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)); @@ -170,7 +163,7 @@ static PyObject *make_app_info(void) SetObjItem(PyBool_FromLong(G.factory_startup)); /* build info, use bytes since we can't assume _any_ encoding: - * see patch [#30154] for issue */ + * see patch T30154 for issue */ #ifdef BUILD_DATE SetBytesItem(build_date); SetBytesItem(build_time); @@ -302,36 +295,13 @@ static int bpy_app_global_flag_set__only_disable(PyObject *UNUSED(self), return bpy_app_global_flag_set(NULL, value, closure); } -#define BROKEN_BINARY_PATH_PYTHON_HACK - PyDoc_STRVAR(bpy_app_binary_path_python_doc, - "String, the path to the python executable (read-only)"); -static PyObject *bpy_app_binary_path_python_get(PyObject *self, void *UNUSED(closure)) + "String, the path to the python executable (read-only). " + "Deprecated! Use ``sys.executable`` instead."); +static PyObject *bpy_app_binary_path_python_get(PyObject *UNUSED(self), void *UNUSED(closure)) { - /* refcount is held in BlenderAppType.tp_dict */ - static PyObject *ret = NULL; - - if (ret == NULL) { - /* only run once */ - char fullpath[1024]; - BKE_appdir_program_python_search( - fullpath, sizeof(fullpath), PY_MAJOR_VERSION, PY_MINOR_VERSION); - ret = PyC_UnicodeFromByte(fullpath); -#ifdef BROKEN_BINARY_PATH_PYTHON_HACK - Py_INCREF(ret); - UNUSED_VARS(self); -#else - PyDict_SetItem( - BlenderAppType.tp_dict, - /* XXX BAAAADDDDDD! self is not a PyDescr at all! it's bpy.app!!! */ PyDescr_NAME(self), - ret); -#endif - } - else { - Py_INCREF(ret); - } - - return ret; + PyErr_Warn(PyExc_RuntimeWarning, "Use 'sys.executable' instead of 'binary_path_python'!"); + return Py_INCREF_RET(PySys_GetObject("executable")); } PyDoc_STRVAR(bpy_app_debug_value_doc, @@ -383,7 +353,7 @@ PyDoc_STRVAR(bpy_app_preview_render_size_doc, "Reference size for icon/preview renders (read-only)"); static PyObject *bpy_app_preview_render_size_get(PyObject *UNUSED(self), void *closure) { - return PyLong_FromLong((long)UI_preview_render_size(POINTER_AS_INT(closure))); + return PyLong_FromLong((long)UI_icon_preview_to_render_size(POINTER_AS_INT(closure))); } static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void *UNUSED(closure)) @@ -532,7 +502,7 @@ PyObject *BPY_app_struct(void) BlenderAppType.tp_init = NULL; BlenderAppType.tp_new = NULL; BlenderAppType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ /* kindof a hack ontop of PyStructSequence */ py_struct_seq_getset_init(); diff --git a/source/blender/python/intern/bpy_app_alembic.c b/source/blender/python/intern/bpy_app_alembic.c index dc7632baeef..d5640045977 100644 --- a/source/blender/python/intern/bpy_app_alembic.c +++ b/source/blender/python/intern/bpy_app_alembic.c @@ -100,7 +100,7 @@ PyObject *BPY_app_alembic_struct(void) BlenderAppABCType.tp_init = NULL; BlenderAppABCType.tp_new = NULL; BlenderAppABCType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index cc97f6dec94..aaceb7b393f 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -61,6 +61,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {"usd", NULL}, {"fluid", NULL}, {"xr_openxr", NULL}, + {"potrace", NULL}, {NULL}, }; @@ -282,6 +283,12 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif +#ifdef WITH_POTRACE + SetObjIncref(Py_True); +#else + SetObjIncref(Py_False); +#endif + #undef SetObjIncref return builtopts_info; @@ -299,7 +306,7 @@ PyObject *BPY_app_build_options_struct(void) BlenderAppBuildOptionsType.tp_init = NULL; BlenderAppBuildOptionsType.tp_new = NULL; BlenderAppBuildOptionsType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_ffmpeg.c b/source/blender/python/intern/bpy_app_ffmpeg.c index 193b0385044..4e396668450 100644 --- a/source/blender/python/intern/bpy_app_ffmpeg.c +++ b/source/blender/python/intern/bpy_app_ffmpeg.c @@ -75,7 +75,7 @@ static PyObject *make_ffmpeg_info(void) return NULL; } -#if 0 // UNUSED +#if 0 /* UNUSED */ # define SetIntItem(flag) PyStructSequence_SET_ITEM(ffmpeg_info, pos++, PyLong_FromLong(flag)) #endif #ifndef WITH_FFMPEG @@ -140,7 +140,7 @@ PyObject *BPY_app_ffmpeg_struct(void) BlenderAppFFmpegType.tp_init = NULL; BlenderAppFFmpegType.tp_new = NULL; BlenderAppFFmpegType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index a874e23ff32..1e81621246e 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -232,7 +232,7 @@ PyObject *BPY_app_handlers_struct(void) BlenderAppCbType.tp_init = NULL; BlenderAppCbType.tp_new = NULL; BlenderAppCbType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ /* assign the C callbacks */ if (ret) { diff --git a/source/blender/python/intern/bpy_app_ocio.c b/source/blender/python/intern/bpy_app_ocio.c index bbfc734a77f..3a36e90018f 100644 --- a/source/blender/python/intern/bpy_app_ocio.c +++ b/source/blender/python/intern/bpy_app_ocio.c @@ -104,7 +104,7 @@ PyObject *BPY_app_ocio_struct(void) BlenderAppOCIOType.tp_init = NULL; BlenderAppOCIOType.tp_new = NULL; BlenderAppOCIOType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_oiio.c b/source/blender/python/intern/bpy_app_oiio.c index 1f65641606d..0038f982170 100644 --- a/source/blender/python/intern/bpy_app_oiio.c +++ b/source/blender/python/intern/bpy_app_oiio.c @@ -100,7 +100,7 @@ PyObject *BPY_app_oiio_struct(void) BlenderAppOIIOType.tp_init = NULL; BlenderAppOIIOType.tp_new = NULL; BlenderAppOIIOType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_opensubdiv.c b/source/blender/python/intern/bpy_app_opensubdiv.c index 09cd6201831..90aab2a4500 100644 --- a/source/blender/python/intern/bpy_app_opensubdiv.c +++ b/source/blender/python/intern/bpy_app_opensubdiv.c @@ -96,7 +96,7 @@ PyObject *BPY_app_opensubdiv_struct(void) /* prevent user from creating new instances */ BlenderAppOpenSubdivType.tp_init = NULL; BlenderAppOpenSubdivType.tp_new = NULL; - /* without this we can't do set(sys.modules) [#29635] */ + /* without this we can't do set(sys.modules) T29635. */ BlenderAppOpenSubdivType.tp_hash = (hashfunc)_Py_HashPointer; return ret; diff --git a/source/blender/python/intern/bpy_app_openvdb.c b/source/blender/python/intern/bpy_app_openvdb.c index 9958029dd0a..c98c6ec0137 100644 --- a/source/blender/python/intern/bpy_app_openvdb.c +++ b/source/blender/python/intern/bpy_app_openvdb.c @@ -104,7 +104,7 @@ PyObject *BPY_app_openvdb_struct(void) BlenderAppOVDBType.tp_init = NULL; BlenderAppOVDBType.tp_new = NULL; BlenderAppOVDBType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_sdl.c b/source/blender/python/intern/bpy_app_sdl.c index a3f9fdc67c1..645119c1c5d 100644 --- a/source/blender/python/intern/bpy_app_sdl.c +++ b/source/blender/python/intern/bpy_app_sdl.c @@ -89,7 +89,7 @@ static PyObject *make_sdl_info(void) SDL_GetVersion(&version); sdl_available = true; } -# else // WITH_SDL_DYNLOAD=OFF +# else /* WITH_SDL_DYNLOAD=OFF */ sdl_available = true; # if SDL_MAJOR_VERSION >= 2 SDL_GetVersion(&version); @@ -107,7 +107,7 @@ static PyObject *make_sdl_info(void) } SetObjItem(PyBool_FromLong(sdl_available)); -#else // WITH_SDL=OFF +#else /* WITH_SDL=OFF */ SetObjItem(PyBool_FromLong(0)); SetObjItem(PyC_Tuple_Pack_I32(0, 0, 0)); SetStrItem("Unknown"); @@ -137,7 +137,7 @@ PyObject *BPY_app_sdl_struct(void) BlenderAppSDLType.tp_init = NULL; BlenderAppSDLType.tp_new = NULL; BlenderAppSDLType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index f95261df6b2..ee64d0a409c 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -420,9 +420,9 @@ static BLT_i18n_contexts_descriptor _contexts[] = BLT_I18NCONTEXTS_DESC; static PyStructSequence_Field app_translations_contexts_fields[ARRAY_SIZE(_contexts)] = {{NULL}}; static PyStructSequence_Desc app_translations_contexts_desc = { - "bpy.app.translations.contexts", /* name */ - "This named tuple contains all pre-defined translation contexts", /* doc */ - app_translations_contexts_fields, /* fields */ + "bpy.app.translations.contexts", /* name */ + "This named tuple contains all predefined translation contexts", /* doc */ + app_translations_contexts_fields, /* fields */ ARRAY_SIZE(app_translations_contexts_fields) - 1, }; @@ -464,7 +464,7 @@ static PyObject *app_translations_contexts_make(void) * \{ */ PyDoc_STRVAR(app_translations_contexts_doc, - "A named tuple containing all pre-defined translation contexts.\n" + "A named tuple containing all predefined translation contexts.\n" "\n" ".. warning::\n" " Never use a (new) context starting with \"" BLT_I18NCONTEXT_DEFAULT_BPYRNA @@ -794,10 +794,14 @@ static PyTypeObject BlenderAppTranslationsType = { 0, /* tp_itemsize */ /* methods */ /* No destructor, this is a singleton! */ - NULL, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_dealloc */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ NULL, /* tp_repr */ @@ -896,7 +900,7 @@ PyObject *BPY_app_translations_struct(void) /* prevent user from creating new instances */ BlenderAppTranslationsType.tp_new = NULL; - /* without this we can't do set(sys.modules) [#29635] */ + /* without this we can't do set(sys.modules) T29635. */ BlenderAppTranslationsType.tp_hash = (hashfunc)_Py_HashPointer; return ret; diff --git a/source/blender/python/intern/bpy_app_usd.c b/source/blender/python/intern/bpy_app_usd.c index d60264ef259..4a0ee96061a 100644 --- a/source/blender/python/intern/bpy_app_usd.c +++ b/source/blender/python/intern/bpy_app_usd.c @@ -101,7 +101,7 @@ PyObject *BPY_app_usd_struct(void) BlenderAppUSDType.tp_init = NULL; BlenderAppUSDType.tp_new = NULL; BlenderAppUSDType.tp_hash = (hashfunc) - _Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ return ret; } diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c index 8eb44e918d7..57dc4c0a679 100644 --- a/source/blender/python/intern/bpy_capi_utils.c +++ b/source/blender/python/intern/bpy_capi_utils.c @@ -38,16 +38,6 @@ #include "../generic/py_capi_utils.h" -static bContext *__py_context = NULL; -bContext *BPy_GetContext(void) -{ - return __py_context; -} -void BPy_SetContext(bContext *C) -{ - __py_context = C; -} - char *BPy_enum_as_string(const EnumPropertyItem *item) { DynStr *dynstr = BLI_dynstr_new(); diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index 861b23190a2..55f8a291410 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -48,9 +48,7 @@ bool BPy_errors_to_report_ex(struct ReportList *reports, bool BPy_errors_to_report_brief_with_prefix(struct ReportList *reports, const char *error_prefix); bool BPy_errors_to_report(struct ReportList *reports); -/* TODO - find a better solution! */ -struct bContext *BPy_GetContext(void); -void BPy_SetContext(struct bContext *C); +struct bContext *BPY_context_get(void); extern void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate); extern void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate); diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 4ef685b7987..c0536693f38 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -266,7 +266,7 @@ static void pydriver_error(ChannelDriver *driver) "\nError in Driver: The following Python expression failed:\n\t'%s'\n\n", driver->expression); - // BPy_errors_to_report(NULL); // TODO - reports + // BPy_errors_to_report(NULL); /* TODO - reports */ PyErr_Print(); PyErr_Clear(); } @@ -494,7 +494,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, } /* needed since drivers are updated directly after undo where 'main' is - * re-allocated [#28807] */ + * re-allocated T28807. */ BPY_update_rna_module(); /* init global dictionary for py-driver evaluation settings */ @@ -613,7 +613,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, fprintf( stderr, "\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n", dvar->name); - // BPy_errors_to_report(NULL); // TODO - reports + // BPy_errors_to_report(NULL); /* TODO - reports */ PyErr_Print(); PyErr_Clear(); } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index bc7318e1a15..0b11ac639c7 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -80,6 +80,7 @@ /* Logging types to use anywhere in the Python modules. */ CLG_LOGREF_DECLARE_GLOBAL(BPY_LOG_CONTEXT, "bpy.context"); +CLG_LOGREF_DECLARE_GLOBAL(BPY_LOG_INTERFACE, "bpy.interface"); CLG_LOGREF_DECLARE_GLOBAL(BPY_LOG_RNA, "bpy.rna"); /* for internal use, when starting and ending python scripts */ @@ -91,7 +92,7 @@ static int py_call_level = 0; /* Set by command line arguments before Python starts. */ static bool py_use_system_env = false; -// #define TIME_PY_RUN // simple python tests. prints on exit. +// #define TIME_PY_RUN /* simple python tests. prints on exit. */ #ifdef TIME_PY_RUN # include "PIL_time.h" @@ -111,8 +112,8 @@ void BPY_context_update(bContext *C) return; } - BPy_SetContext(C); - BPY_modules_update(C); /* can give really bad results if this isn't here */ + BPY_context_set(C); + BPY_modules_update(); /* can give really bad results if this isn't here */ } void bpy_context_set(bContext *C, PyGILState_STATE *gilstate) @@ -155,7 +156,7 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) /* XXX - Calling classes currently wont store the context :\, * cant set NULL because of this. but this is very flakey still. */ #if 0 - BPy_SetContext(NULL); + BPY_context_set(NULL); #endif #ifdef TIME_PY_RUN @@ -165,6 +166,46 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) } } +/** + * Use for `CTX_*_set(..)` functions need to set values which are later read back as expected. + * In this case we don't want the Python context to override the values as it causes problems + * see T66256. + * + * \param dict_p: A pointer to #bContext.data.py_context so we can assign a new value. + * \param dict_orig: The value of #bContext.data.py_context_orig to check if we need to copy. + * + * \note Typically accessed via #BPY_context_dict_clear_members macro. + */ +void BPY_context_dict_clear_members_array(void **dict_p, + void *dict_orig, + const char *context_members[], + uint context_members_len) +{ + PyGILState_STATE gilstate; + const bool use_gil = !PyC_IsInterpreterActive(); + + if (use_gil) { + gilstate = PyGILState_Ensure(); + } + + /* Copy on write. */ + if (*dict_p == dict_orig) { + *dict_p = PyDict_Copy(dict_orig); + } + + PyObject *dict = *dict_p; + BLI_assert(PyDict_Check(dict)); + for (uint i = 0; i < context_members_len; i++) { + if (PyDict_DelItemString(dict, context_members[i])) { + PyErr_Clear(); + } + } + + if (use_gil) { + PyGILState_Release(gilstate); + } +} + void BPY_text_free_code(Text *text) { if (text->compiled) { @@ -184,7 +225,10 @@ void BPY_text_free_code(Text *text) } } -void BPY_modules_update(bContext *C) +/** + * Needed so the #Main pointer in `bpy.data` doesn't become out of date. + */ +void BPY_modules_update(void) { #if 0 /* slow, this runs all the time poll, draw etc 100's of time a sec. */ PyObject *mod = PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); @@ -194,14 +238,16 @@ void BPY_modules_update(bContext *C) /* refreshes the main struct */ BPY_update_rna_module(); - if (bpy_context_module) { - bpy_context_module->ptr.data = (void *)C; - } +} + +bContext *BPY_context_get(void) +{ + return bpy_context_module->ptr.data; } void BPY_context_set(bContext *C) { - BPy_SetContext(C); + bpy_context_module->ptr.data = (void *)C; } #ifdef WITH_FLUID @@ -255,13 +301,16 @@ static struct _inittab bpy_internal_modules[] = { }; /* call BPY_context_set first */ -void BPY_python_start(int argc, const char **argv) +void BPY_python_start(bContext *C, int argc, const char **argv) { #ifndef WITH_PYTHON_MODULE PyThreadState *py_tstate = NULL; - const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL); - /* Not essential but nice to set our name. */ + /* Needed for Python's initialization for portable Python installations. + * We could use #Py_SetPath, but this overrides Python's internal logic + * for calculating it's own module search paths. + * + * `sys.executable` is overwritten after initialization to the Python binary. */ { const char *program_path = BKE_appdir_program_path(); wchar_t program_path_wchar[FILE_MAX]; @@ -272,8 +321,21 @@ void BPY_python_start(int argc, const char **argv) /* must run before python initializes */ PyImport_ExtendInittab(bpy_internal_modules); - /* allow to use our own included python */ - PyC_SetHomePath(py_path_bundle); + /* Allow to use our own included Python. `py_path_bundle` may be NULL. */ + { + const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL); + if (py_path_bundle != NULL) { + PyC_SetHomePath(py_path_bundle); + } + else { + /* Common enough to use the system Python on Linux/Unix, warn on other systems. */ +# if defined(__APPLE__) || defined(_WIN32) + fprintf(stderr, + "Bundled Python not found and is expected on this platform " + "(the 'install' target may have not been built)\n"); +# endif + } + } /* Without this the `sys.stdout` may be set to 'ascii' * (it is on my system at least), where printing unicode values will raise @@ -291,25 +353,41 @@ void BPY_python_start(int argc, const char **argv) Py_IgnoreEnvironmentFlag = !py_use_system_env; Py_NoUserSiteDirectory = !py_use_system_env; + /* Initialize Python (also acquires lock). */ Py_Initialize(); - // PySys_SetArgv(argc, argv); /* broken in py3, not a huge deal */ - /* sigh, why do python guys not have a (char **) version anymore? */ + /* We could convert to #wchar_t then pass to #PySys_SetArgv (or use #PyConfig in Python 3.8+). + * However this risks introducing subtle changes in encoding that are hard to track down. + * + * So rely on #PyC_UnicodeFromByte since it's a tried & true way of getting paths + * that include non `utf-8` compatible characters, see: T20021. */ { - int i; PyObject *py_argv = PyList_New(argc); - for (i = 0; i < argc; i++) { - /* should fix bug #20021 - utf path name problems, by replacing - * PyUnicode_FromString, with this one */ + for (int i = 0; i < argc; i++) { PyList_SET_ITEM(py_argv, i, PyC_UnicodeFromByte(argv[i])); } - PySys_SetObject("argv", py_argv); Py_DECREF(py_argv); } - /* Initialize thread support (also acquires lock) */ - PyEval_InitThreads(); + /* Setting the program name is important so the 'multiprocessing' module + * can launch new Python instances. */ + { + const char *sys_variable = "executable"; + char program_path[FILE_MAX]; + if (BKE_appdir_program_python_search( + program_path, sizeof(program_path), PY_MAJOR_VERSION, PY_MINOR_VERSION)) { + PyObject *py_program_path = PyC_UnicodeFromByte(program_path); + PySys_SetObject(sys_variable, py_program_path); + Py_DECREF(py_program_path); + } + else { + fprintf(stderr, + "Unable to find the python binary, " + "the multiprocessing module may not be functional!\n"); + PySys_SetObject(sys_variable, Py_None); + } + } # ifdef WITH_FLUID /* Required to prevent assertion error, see: @@ -349,7 +427,7 @@ void BPY_python_start(int argc, const char **argv) #endif /* bpy.* and lets us import it */ - BPy_init_modules(); + BPy_init_modules(C); pyrna_alloc_types(); @@ -627,7 +705,7 @@ static void bpy_module_delay_init(PyObject *bpy_proxy) const int argc = 1; const char *argv[2]; - /* updating the module dict below will loose the reference to __file__ */ + /* updating the module dict below will lose the reference to __file__ */ PyObject *filename_obj = PyModule_GetFilenameObject(bpy_proxy); const char *filename_rel = _PyUnicode_AsString(filename_obj); /* can be relative */ diff --git a/source/blender/python/intern/bpy_interface_atexit.c b/source/blender/python/intern/bpy_interface_atexit.c index e69914671dd..8e5a6dc530b 100644 --- a/source/blender/python/intern/bpy_interface_atexit.c +++ b/source/blender/python/intern/bpy_interface_atexit.c @@ -34,7 +34,7 @@ static PyObject *bpy_atexit(PyObject *UNUSED(self), PyObject *UNUSED(args), PyObject *UNUSED(kw)) { /* close down enough of blender at least not to crash */ - struct bContext *C = BPy_GetContext(); + struct bContext *C = BPY_context_get(); WM_exit_ex(C, false); diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index a3750d348f5..bc3d8b2c360 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -92,9 +92,13 @@ static PyTypeObject bpy_lib_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)bpy_lib_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ NULL, /* tp_repr */ @@ -180,7 +184,7 @@ PyDoc_STRVAR( " :type relative: bool\n"); static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { - Main *bmain = CTX_data_main(BPy_GetContext()); + Main *bmain = CTX_data_main(BPY_context_get()); BPy_Library *ret; const char *filename = NULL; bool is_rel = false, is_link = false; @@ -319,7 +323,7 @@ static void bpy_lib_exit_warn_type(BPy_Library *self, PyObject *item) static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) { - Main *bmain = CTX_data_main(BPy_GetContext()); + Main *bmain = CTX_data_main(BPY_context_get()); Main *mainl = NULL; const int err = 0; const bool do_append = ((self->flag & FILE_LINK) == 0); diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index 3739f56dc79..df45007b68f 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -265,7 +265,7 @@ static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args } /* Note: we may want to have a way to pass this in. */ - bContext *C = (bContext *)BPy_GetContext(); + bContext *C = BPY_context_get(); struct wmMsgBus *mbus = CTX_wm_message_bus(C); wmMsgParams_RNA msg_key_params = {{0}}; @@ -340,7 +340,7 @@ static PyObject *bpy_msgbus_publish_rna(PyObject *UNUSED(self), PyObject *args, } /* Note: we may want to have a way to pass this in. */ - bContext *C = (bContext *)BPy_GetContext(); + bContext *C = BPY_context_get(); struct wmMsgBus *mbus = CTX_wm_message_bus(C); wmMsgParams_RNA msg_key_params = {{0}}; @@ -359,7 +359,7 @@ PyDoc_STRVAR(bpy_msgbus_clear_by_owner_doc, " Clear all subscribers using this owner.\n"); static PyObject *bpy_msgbus_clear_by_owner(PyObject *UNUSED(self), PyObject *py_owner) { - bContext *C = (bContext *)BPy_GetContext(); + bContext *C = BPY_context_get(); struct wmMsgBus *mbus = CTX_wm_message_bus(C); WM_msgbus_clear_by_owner(mbus, py_owner); Py_RETURN_NONE; diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 6d86d788644..5ac76ab9ac1 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -77,7 +77,6 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) wmOperatorType *ot; const char *opname; PyObject *context_dict = NULL; /* optional args */ - PyObject *context_dict_back; const char *context_str = NULL; PyObject *ret; @@ -85,7 +84,7 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) /* 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 = (bContext *)BPy_GetContext(); + bContext *C = BPY_context_get(); if (C == NULL) { PyErr_SetString(PyExc_RuntimeError, "Context is None, cant poll any operators"); @@ -119,7 +118,7 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) } } - if (context_dict == NULL || context_dict == Py_None) { + if (ELEM(context_dict, NULL, Py_None)) { context_dict = NULL; } else if (!PyDict_Check(context_dict)) { @@ -131,16 +130,25 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) return NULL; } - context_dict_back = CTX_py_dict_get(C); - CTX_py_dict_set(C, (void *)context_dict); - Py_XINCREF(context_dict); /* so we done loose it */ + 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; - /* restore with original context dict, probably NULL but need this for nested operator calls */ - Py_XDECREF(context_dict); - CTX_py_dict_set(C, (void *)context_dict_back); + 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); } @@ -156,7 +164,6 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) const char *context_str = NULL; PyObject *kw = NULL; /* optional args */ PyObject *context_dict = NULL; /* optional args */ - PyObject *context_dict_back; /* note that context is an int, python does the conversion in this case */ int context = WM_OP_EXEC_DEFAULT; @@ -164,7 +171,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) /* 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 = (bContext *)BPy_GetContext(); + bContext *C = BPY_context_get(); if (C == NULL) { PyErr_SetString(PyExc_RuntimeError, "Context is None, cant poll any operators"); @@ -213,7 +220,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) } } - if (context_dict == NULL || context_dict == Py_None) { + if (ELEM(context_dict, NULL, Py_None)) { context_dict = NULL; } else if (!PyDict_Check(context_dict)) { @@ -225,17 +232,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) return NULL; } - context_dict_back = CTX_py_dict_get(C); - /** * 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_dict_set(C, (void *)context_dict); + CTX_py_state_push(C, &context_py_state, (void *)context_dict); + Py_INCREF(context_dict); /* so we don't lose it */ } - Py_XINCREF(context_dict); /* so we done loose it */ if (WM_operator_poll_context((bContext *)C, ot, context) == false) { const char *msg = CTX_wm_operator_poll_msg_get(C); @@ -314,9 +320,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) #endif } - /* restore with original context dict, probably NULL but need this for nested operator calls */ - Py_XDECREF(context_dict); - CTX_py_dict_set(C, (void *)context_dict_back); + 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; @@ -326,7 +339,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) * 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(C); + BPY_modules_update(); /* return operator_ret as a bpy enum */ return pyrna_enum_bitfield_to_py(rna_enum_operator_return_items, operator_ret); @@ -346,7 +359,7 @@ static PyObject *pyop_as_string(PyObject *UNUSED(self), PyObject *args) char *buf = NULL; PyObject *pybuf; - bContext *C = (bContext *)BPy_GetContext(); + bContext *C = BPY_context_get(); if (C == NULL) { PyErr_SetString(PyExc_RuntimeError, diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index bc708bd4a89..d45c8e8b131 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -112,17 +112,21 @@ static const EnumPropertyItem property_flag_override_collection_items[] = { 0, "No Name", "Do not use the names of the items, only their indices in the collection"}, + {PROPOVERRIDE_LIBRARY_INSERTION, + "USE_INSERTION", + 0, + "Use Insertion", + "Allow users to add new items in that collection in library overrides"}, {0, NULL, 0, NULL, NULL}, }; #define BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC \ - " :arg override: Enumerator in ['LIBRARY_OVERRIDABLE', 'NO_PROPERTY_NAME'].\n" \ + " :arg override: Enumerator in ['LIBRARY_OVERRIDABLE', 'NO_PROPERTY_NAME', " \ + "'USE_INSERTION'].\n" \ " :type override: set\n" /* subtypes */ -/* XXX Keep in sync with rna_rna.c's rna_enum_property_subtype_items ??? - * Currently it is not... - */ +/* Keep in sync with RNA_types.h PropertySubType and rna_rna.c's rna_enum_property_subtype_items */ static const EnumPropertyItem property_subtype_string_items[] = { {PROP_FILEPATH, "FILE_PATH", 0, "File Path", ""}, {PROP_DIRPATH, "DIR_PATH", 0, "Directory Path", ""}, @@ -147,6 +151,9 @@ static const EnumPropertyItem property_subtype_number_items[] = { {PROP_ANGLE, "ANGLE", 0, "Angle", ""}, {PROP_TIME, "TIME", 0, "Time", ""}, {PROP_DISTANCE, "DISTANCE", 0, "Distance", ""}, + {PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""}, + {PROP_POWER, "POWER", 0, "Power", ""}, + {PROP_TEMPERATURE, "TEMPERATURE", 0, "Temperature", ""}, {PROP_NONE, "NONE", 0, "None", ""}, {0, NULL, 0, NULL, NULL}, @@ -154,7 +161,7 @@ static const EnumPropertyItem property_subtype_number_items[] = { #define BPY_PROPDEF_SUBTYPE_NUMBER_DOC \ " :arg subtype: Enumerator in ['PIXEL', 'UNSIGNED', 'PERCENTAGE', 'FACTOR', 'ANGLE', " \ - "'TIME', 'DISTANCE', 'NONE'].\n" \ + "'TIME', 'DISTANCE', 'DISTANCE_CAMERA', 'POWER', 'TEMPERATURE', 'NONE'].\n" \ " :type subtype: string\n" static const EnumPropertyItem property_subtype_array_items[] = { @@ -168,11 +175,11 @@ static const EnumPropertyItem property_subtype_array_items[] = { {PROP_QUATERNION, "QUATERNION", 0, "Quaternion", ""}, {PROP_AXISANGLE, "AXISANGLE", 0, "Axis Angle", ""}, {PROP_XYZ, "XYZ", 0, "XYZ", ""}, + {PROP_XYZ_LENGTH, "XYZ_LENGTH", 0, "XYZ Length", ""}, {PROP_COLOR_GAMMA, "COLOR_GAMMA", 0, "Color Gamma", ""}, + {PROP_COORDS, "COORDINATES", 0, "Vector Coordinates", ""}, {PROP_LAYER, "LAYER", 0, "Layer", ""}, {PROP_LAYER_MEMBER, "LAYER_MEMBER", 0, "Layer Member", ""}, - {PROP_POWER, "POWER", 0, "Power", ""}, - {PROP_TEMPERATURE, "TEMPERATURE", 0, "Temperature", ""}, {PROP_NONE, "NONE", 0, "None", ""}, {0, NULL, 0, NULL, NULL}, @@ -181,7 +188,7 @@ static const EnumPropertyItem property_subtype_array_items[] = { #define BPY_PROPDEF_SUBTYPE_ARRAY_DOC \ " :arg subtype: Enumerator in ['COLOR', 'TRANSLATION', 'DIRECTION', " \ "'VELOCITY', 'ACCELERATION', 'MATRIX', 'EULER', 'QUATERNION', 'AXISANGLE', " \ - "'XYZ', 'COLOR_GAMMA', 'LAYER', 'LAYER_MEMBER', 'POWER', 'NONE'].\n" \ + "'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \ " :type subtype: string\n" /* PyObject's */ @@ -1986,8 +1993,9 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, } \ srna = srna_from_self(self, #_func "(...):"); \ if (srna == NULL) { \ - if (PyErr_Occurred()) \ + if (PyErr_Occurred()) { \ return NULL; \ + } \ return bpy_prop_deferred_return(pymeth_##_func, kw); \ } \ (void)0 diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 0980d9df762..6e41bc96eed 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -80,6 +80,11 @@ #define USE_MATHUTILS #define USE_STRING_COERCE +/* Unfortunately Python needs to hold a global reference to the context. + * If we remove this is means `bpy.context` won't be usable from some parts of the code: + * `bpy.app.handler` callbacks for example. + * Even though this is arguably "correct", it's going to cause problems for existing scripts, + * so accept having this for the time being. */ BPy_StructRNA *bpy_context_module = NULL; /* for fast access */ static PyObject *pyrna_struct_Subtype(PointerRNA *ptr); @@ -457,7 +462,7 @@ static int mathutils_rna_vector_set(BaseMathObject *bmo, int subtype) RNA_property_float_set_array(&self->ptr, self->prop, bmo->data); if (RNA_property_update_check(self->prop)) { - RNA_property_update(BPy_GetContext(), &self->ptr, self->prop); + RNA_property_update(BPY_context_get(), &self->ptr, self->prop); } /* Euler order exception. */ @@ -468,7 +473,7 @@ static int mathutils_rna_vector_set(BaseMathObject *bmo, int subtype) if (order != eul->order) { RNA_property_enum_set(&self->ptr, prop_eul_order, eul->order); if (RNA_property_update_check(prop_eul_order)) { - RNA_property_update(BPy_GetContext(), &self->ptr, prop_eul_order); + RNA_property_update(BPY_context_get(), &self->ptr, prop_eul_order); } } } @@ -517,7 +522,7 @@ static int mathutils_rna_vector_set_index(BaseMathObject *bmo, int UNUSED(subtyp RNA_property_float_set_index(&self->ptr, self->prop, index, bmo->data[index]); if (RNA_property_update_check(self->prop)) { - RNA_property_update(BPy_GetContext(), &self->ptr, self->prop); + RNA_property_update(BPY_context_get(), &self->ptr, self->prop); } return 0; @@ -576,7 +581,7 @@ static int mathutils_rna_matrix_set(BaseMathObject *bmo, int UNUSED(subtype)) RNA_property_float_set_array(&self->ptr, self->prop, bmo->data); if (RNA_property_update_check(self->prop)) { - RNA_property_update(BPy_GetContext(), &self->ptr, self->prop); + RNA_property_update(BPY_context_get(), &self->ptr, self->prop); } return 0; } @@ -722,7 +727,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) } else { /* Order will be updated from callback on use. */ - // TODO, get order from RNA + /* TODO, get order from RNA. */ PyObject *eul_cb = Euler_CreatePyObject_cb( ret, EULER_ORDER_XYZ, mathutils_rna_array_cb_index, MATHUTILS_CB_SUBTYPE_EUL); Py_DECREF(ret); /* The euler owns 'ret' now. */ @@ -890,22 +895,36 @@ static PyObject *pyrna_struct_str(BPy_StructRNA *self) { PyObject *ret; const char *name; + const char *extra_info = ""; if (!PYRNA_STRUCT_IS_VALID(self)) { return PyUnicode_FromFormat("<bpy_struct, %.200s invalid>", Py_TYPE(self)->tp_name); } - /* Print name if available. */ + ID *id = self->ptr.owner_id; + if (id && id != DEG_get_original_id(id)) { + extra_info = ", evaluated"; + } + + /* Print name if available. + * + * Always include the pointer address since it can help identify unique data, + * or when data is re-allocated internally. */ name = RNA_struct_name_get_alloc(&self->ptr, NULL, 0, NULL); if (name) { - ret = PyUnicode_FromFormat( - "<bpy_struct, %.200s(\"%.200s\")>", RNA_struct_identifier(self->ptr.type), name); + ret = PyUnicode_FromFormat("<bpy_struct, %.200s(\"%.200s\") at %p%s>", + RNA_struct_identifier(self->ptr.type), + name, + self->ptr.data, + extra_info); MEM_freeN((void *)name); return ret; } - return PyUnicode_FromFormat( - "<bpy_struct, %.200s at %p>", RNA_struct_identifier(self->ptr.type), self->ptr.data); + return PyUnicode_FromFormat("<bpy_struct, %.200s at %p%s>", + RNA_struct_identifier(self->ptr.type), + self->ptr.data, + extra_info); } static PyObject *pyrna_struct_repr(BPy_StructRNA *self) @@ -914,18 +933,14 @@ static PyObject *pyrna_struct_repr(BPy_StructRNA *self) PyObject *tmp_str; PyObject *ret; - if (id == NULL || !PYRNA_STRUCT_IS_VALID(self)) { + if (id == NULL || !PYRNA_STRUCT_IS_VALID(self) || (DEG_get_original_id(id) != id)) { /* fallback */ return pyrna_struct_str(self); } tmp_str = PyUnicode_FromString(id->name + 2); - if (DEG_get_original_id(id) != id) { - ret = PyUnicode_FromFormat( - "Evaluated %s %R", BKE_idtype_idcode_to_name(GS(id->name)), tmp_str); - } - else if (RNA_struct_is_ID(self->ptr.type) && (id->flag & LIB_EMBEDDED_DATA) == 0) { + if (RNA_struct_is_ID(self->ptr.type) && (id->flag & LIB_EMBEDDED_DATA) == 0) { ret = PyUnicode_FromFormat( "bpy.data.%s[%R]", BKE_idtype_idcode_to_name_plural(GS(id->name)), tmp_str); } @@ -1219,7 +1234,7 @@ static const char *pyrna_enum_as_string(PointerRNA *ptr, PropertyRNA *prop) const char *result; bool free = false; - RNA_property_enum_items(BPy_GetContext(), ptr, prop, &item, NULL, &free); + RNA_property_enum_items(BPY_context_get(), ptr, prop, &item, NULL, &free); if (item) { result = BPy_enum_as_string(item); } @@ -1247,7 +1262,7 @@ static int pyrna_string_to_enum( return -1; } - if (!RNA_property_enum_value(BPy_GetContext(), ptr, prop, param, r_value)) { + if (!RNA_property_enum_value(BPY_context_get(), ptr, prop, param, r_value)) { const char *enum_str = pyrna_enum_as_string(ptr, prop); PyErr_Format(PyExc_TypeError, "%.200s enum \"%.200s\" not found in (%s)", @@ -1389,7 +1404,7 @@ static int pyrna_prop_to_enum_bitfield( return -1; } - RNA_property_enum_items(BPy_GetContext(), ptr, prop, &item, NULL, &free); + RNA_property_enum_items(BPY_context_get(), ptr, prop, &item, NULL, &free); if (item) { ret = pyrna_set_to_enum_bitfield(item, value, r_value, error_prefix); @@ -1441,7 +1456,7 @@ static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) ret = PySet_New(NULL); - if (RNA_property_enum_bitflag_identifiers(BPy_GetContext(), ptr, prop, val, identifier)) { + if (RNA_property_enum_bitflag_identifiers(BPY_context_get(), ptr, prop, val, identifier)) { int index; for (index = 0; identifier[index]; index++) { @@ -1453,7 +1468,7 @@ static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) } else { const char *identifier; - if (RNA_property_enum_identifier(BPy_GetContext(), ptr, prop, val, &identifier)) { + if (RNA_property_enum_identifier(BPY_context_get(), ptr, prop, val, &identifier)) { ret = PyUnicode_FromString(identifier); } else { @@ -2202,7 +2217,7 @@ static int pyrna_py_to_prop( /* Run RNA property functions. */ if (RNA_property_update_check(prop)) { - RNA_property_update(BPy_GetContext(), ptr, prop); + RNA_property_update(BPY_context_get(), ptr, prop); } return 0; @@ -2278,7 +2293,7 @@ static int pyrna_py_to_prop_array_index(BPy_PropertyArrayRNA *self, int index, P /* Run RNA property functions. */ if (RNA_property_update_check(prop)) { - RNA_property_update(BPy_GetContext(), ptr, prop); + RNA_property_update(BPY_context_get(), ptr, prop); } return ret; @@ -3282,7 +3297,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self, if (ret != -1) { if (RNA_property_update_check(self->prop)) { - RNA_property_update(BPy_GetContext(), &self->ptr, self->prop); + RNA_property_update(BPY_context_get(), &self->ptr, self->prop); } } @@ -3589,20 +3604,30 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self) } PyDoc_STRVAR(pyrna_struct_is_property_set_doc, - ".. method:: is_property_set(property)\n" + ".. method:: is_property_set(property, ghost=True)\n" "\n" " Check if a property is set, use for testing operator properties.\n" "\n" + " :arg ghost: Used for operators that re-run with previous settings.\n" + " In this case the property is not marked as set,\n" + " yet the value from the previous execution is used.\n" + "\n" + " In rare cases you may want to set this option to false.\n" + "\n" + " :type ghost: boolean\n" " :return: True when the property has been set.\n" " :rtype: boolean\n"); -static PyObject *pyrna_struct_is_property_set(BPy_StructRNA *self, PyObject *args) +static PyObject *pyrna_struct_is_property_set(BPy_StructRNA *self, PyObject *args, PyObject *kw) { PropertyRNA *prop; const char *name; + bool use_ghost = true; PYRNA_STRUCT_CHECK_OBJ(self); - if (!PyArg_ParseTuple(args, "s:is_property_set", &name)) { + static const char *_keywords[] = {"", "ghost", NULL}; + static _PyArg_Parser _parser = {"s|$O&:is_property_set", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &name, PyC_ParseBool, &use_ghost)) { return NULL; } @@ -3614,7 +3639,7 @@ static PyObject *pyrna_struct_is_property_set(BPy_StructRNA *self, PyObject *arg return NULL; } - return PyBool_FromLong(RNA_property_is_set(&self->ptr, prop)); + return PyBool_FromLong(RNA_property_is_set_ex(&self->ptr, prop, use_ghost)); } PyDoc_STRVAR(pyrna_struct_property_unset_doc, @@ -3952,7 +3977,7 @@ PyDoc_STRVAR(pyrna_prop_update_doc, " however in rare cases it's useful to call explicitly.\n"); static PyObject *pyrna_prop_update(BPy_PropertyRNA *self) { - RNA_property_update(BPy_GetContext(), &self->ptr, self->prop); + RNA_property_update(BPY_context_get(), &self->ptr, self->prop); Py_RETURN_NONE; } @@ -4250,9 +4275,9 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname) ListBase newlb; short newtype; - const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); + const eContextResult done = CTX_data_get(C, name, &newptr, &newlb, &newtype); - if (done == 1) { /* Found. */ + if (done == CTX_RESULT_OK) { switch (newtype) { case CTX_DATA_TYPE_POINTER: if (newptr.data == NULL) { @@ -4285,7 +4310,7 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname) break; } } - else if (done == -1) { /* Found, but not set. */ + else if (done == CTX_RESULT_NO_DATA) { ret = Py_None; Py_INCREF(ret); } @@ -4472,9 +4497,9 @@ static int pyrna_struct_setattro(BPy_StructRNA *self, PyObject *pyname, PyObject ListBase newlb; short newtype; - const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); + const eContextResult done = CTX_data_get(C, name, &newptr, &newlb, &newtype); - if (done == 1) { + if (done == CTX_RESULT_OK) { PyErr_Format( PyExc_AttributeError, "bpy_struct: Context property \"%.200s\" is read-only", name); BLI_freelistN(&newlb); @@ -4829,7 +4854,7 @@ static PyObject *pyrna_prop_collection_items(BPy_PropertyRNA *self) } } else { - /* A bit strange, but better then returning an empty list. */ + /* A bit strange, but better than returning an empty list. */ PyTuple_SET_ITEM(item, 0, PyLong_FromLong(i)); } PyTuple_SET_ITEM(item, 1, pyrna_struct_CreatePyObject(&itemptr)); @@ -5406,7 +5431,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, /* Get/set both take the same args currently. */ PyObject *seq; - if (prop_type != PROP_INT && prop_type != PROP_FLOAT) { + if (!ELEM(prop_type, PROP_INT, PROP_FLOAT)) { PyErr_Format(PyExc_TypeError, "foreach_get/set available only for int and float"); return NULL; } @@ -5639,7 +5664,7 @@ static struct PyMethodDef pyrna_struct_methods[] = { {"is_property_set", (PyCFunction)pyrna_struct_is_property_set, - METH_VARARGS, + METH_VARARGS | METH_KEYWORDS, pyrna_struct_is_property_set_doc}, {"property_unset", (PyCFunction)pyrna_struct_property_unset, @@ -5845,7 +5870,7 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat /* Resolve the array from a new pytype. */ - /* TODO(Kazanbas) make multi-dimensional sequences here. */ + /* TODO(Kazanbas): make multi-dimensional sequences here. */ switch (type) { case PROP_BOOLEAN: @@ -6038,7 +6063,7 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject /* enable this so all strings are copied and freed after calling. * this exposes bugs where the pointer to the string is held and re-used */ - // #define DEBUG_STRING_FREE + /* #define DEBUG_STRING_FREE */ #ifdef DEBUG_STRING_FREE PyObject *string_free_ls = PyList_New(0); @@ -6204,7 +6229,7 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject /* Check if we gave args that don't exist in the function * Printing the error is slow, but it should only happen when developing. - * The "if" below is quick check to make sure less keyword args were passed then we gave. + * The "if" below is quick check to make sure less keyword args were passed than we gave. * (Don't overwrite the error if we have one, * otherwise can skip important messages and confuse with args). */ @@ -6284,7 +6309,7 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject if (err == 0) { /* Call function. */ ReportList reports; - bContext *C = BPy_GetContext(); + bContext *C = BPY_context_get(); BKE_reports_init(&reports, RPT_STORE); RNA_function_call(C, &reports, self_ptr, self_func, &parms); @@ -6378,10 +6403,14 @@ PyTypeObject pyrna_struct_meta_idprop_Type = { 0, /* tp_itemsize */ /* methods */ - NULL, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_dealloc */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* deprecated in Python 3.0! */ NULL, /* tp_repr */ @@ -6431,7 +6460,7 @@ PyTypeObject pyrna_struct_meta_idprop_Type = { #if defined(_MSC_VER) NULL, /* defer assignment */ #else - &PyType_Type, /* struct _typeobject *tp_base; */ + &PyType_Type, /* struct _typeobject *tp_base; */ #endif NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ @@ -6460,9 +6489,13 @@ PyTypeObject pyrna_struct_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)pyrna_struct_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ (reprfunc)pyrna_struct_repr, /* tp_repr */ @@ -6496,7 +6529,7 @@ PyTypeObject pyrna_struct_Type = { /* delete references to contained objects */ (inquiry)pyrna_struct_clear, /* inquiry tp_clear; */ #else - NULL, /* traverseproc tp_traverse; */ + NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ @@ -6549,9 +6582,13 @@ PyTypeObject pyrna_prop_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)pyrna_prop_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ (reprfunc)pyrna_prop_repr, /* tp_repr */ @@ -6633,9 +6670,13 @@ PyTypeObject pyrna_prop_array_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)pyrna_prop_array_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ (reprfunc)pyrna_prop_array_repr, /* tp_repr */ @@ -6716,9 +6757,13 @@ PyTypeObject pyrna_prop_collection_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)pyrna_prop_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ NULL, @@ -6802,9 +6847,13 @@ static PyTypeObject pyrna_prop_collection_idprop_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)pyrna_prop_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ NULL, @@ -6887,10 +6936,14 @@ PyTypeObject pyrna_func_Type = { sizeof(BPy_FunctionRNA), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - NULL, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_dealloc */ +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +#else + (printfunc)NULL, /* printfunc tp_print */ +#endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ (reprfunc)pyrna_func_repr, /* tp_repr */ @@ -6984,9 +7037,13 @@ static PyTypeObject pyrna_prop_collection_iter_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)pyrna_prop_collection_iter_dealloc, /* tp_dealloc */ - (printfunc)NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ +# if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall_offset */ +# else + (printfunc)NULL, /* printfunc tp_print */ +# endif + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in Python 3.0! */ NULL, @@ -8093,18 +8150,11 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, for (link = lb->first; link; link = link->next) { FunctionRNA *func = (FunctionRNA *)link; const int flag = RNA_function_flag(func); - /* TODO(campbell): this is used for classmethod's too, - * even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg. - * Keep this as-is since it's working, but we should be using - * 'FUNC_USE_SELF_TYPE' for many functions. */ - const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); - if (!(flag & FUNC_REGISTER)) { continue; } item = PyObject_GetAttrString(py_class, RNA_function_identifier(func)); - have_function[i] = (item != NULL); i++; @@ -8117,80 +8167,89 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, RNA_function_identifier(func)); return -1; } - PyErr_Clear(); + + continue; } - else { - /* No need to keep a ref, the class owns it - * (technically we should keep a reference, but...). */ - Py_DECREF(item); - if (is_staticmethod) { - if (PyMethod_Check(item) == 0) { - PyErr_Format(PyExc_TypeError, - "expected %.200s, %.200s class \"%.200s\" " - "attribute to be a static/class method, not a %.200s", - class_type, - py_class_name, - RNA_function_identifier(func), - Py_TYPE(item)->tp_name); - return -1; - } - item = ((PyMethodObject *)item)->im_func; + /* TODO(campbell): this is used for classmethod's too, + * even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg. + * Keep this as-is since it's working, but we should be using + * 'FUNC_USE_SELF_TYPE' for many functions. */ + const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); + + /* Store original so we can decrement its reference before returning. */ + PyObject *item_orig = item; + + if (is_staticmethod) { + if (PyMethod_Check(item) == 0) { + PyErr_Format(PyExc_TypeError, + "expected %.200s, %.200s class \"%.200s\" " + "attribute to be a static/class method, not a %.200s", + class_type, + py_class_name, + RNA_function_identifier(func), + Py_TYPE(item)->tp_name); + Py_DECREF(item_orig); + return -1; } - else { - if (PyFunction_Check(item) == 0) { - PyErr_Format(PyExc_TypeError, - "expected %.200s, %.200s class \"%.200s\" " - "attribute to be a function, not a %.200s", - class_type, - py_class_name, - RNA_function_identifier(func), - Py_TYPE(item)->tp_name); - return -1; - } + item = ((PyMethodObject *)item)->im_func; + } + else { + if (PyFunction_Check(item) == 0) { + PyErr_Format(PyExc_TypeError, + "expected %.200s, %.200s class \"%.200s\" " + "attribute to be a function, not a %.200s", + class_type, + py_class_name, + RNA_function_identifier(func), + Py_TYPE(item)->tp_name); + Py_DECREF(item_orig); + return -1; } + } - func_arg_count = rna_function_arg_count(func, &func_arg_min_count); + func_arg_count = rna_function_arg_count(func, &func_arg_min_count); - if (func_arg_count >= 0) { /* -1 if we don't care. */ - arg_count = ((PyCodeObject *)PyFunction_GET_CODE(item))->co_argcount; + if (func_arg_count >= 0) { /* -1 if we don't care. */ + arg_count = ((PyCodeObject *)PyFunction_GET_CODE(item))->co_argcount; - /* note, the number of args we check for and the number of args we give to - * '@staticmethods' are different (quirk of Python), - * this is why rna_function_arg_count() doesn't return the value -1*/ - if (is_staticmethod) { - func_arg_count++; - func_arg_min_count++; - } + /* note, the number of args we check for and the number of args we give to + * '@staticmethods' are different (quirk of Python), + * this is why rna_function_arg_count() doesn't return the value -1*/ + if (is_staticmethod) { + func_arg_count++; + func_arg_min_count++; + } - if (arg_count < func_arg_min_count || arg_count > func_arg_count) { - if (func_arg_min_count != func_arg_count) { - PyErr_Format( - PyExc_ValueError, - "expected %.200s, %.200s class \"%.200s\" function to have between %d and %d " - "args, found %d", - class_type, - py_class_name, - RNA_function_identifier(func), - func_arg_count, - func_arg_min_count, - arg_count); - } - else { - PyErr_Format( - PyExc_ValueError, - "expected %.200s, %.200s class \"%.200s\" function to have %d args, found %d", - class_type, - py_class_name, - RNA_function_identifier(func), - func_arg_count, - arg_count); - } - return -1; + if (arg_count < func_arg_min_count || arg_count > func_arg_count) { + if (func_arg_min_count != func_arg_count) { + PyErr_Format( + PyExc_ValueError, + "expected %.200s, %.200s class \"%.200s\" function to have between %d and %d " + "args, found %d", + class_type, + py_class_name, + RNA_function_identifier(func), + func_arg_count, + func_arg_min_count, + arg_count); + } + else { + PyErr_Format( + PyExc_ValueError, + "expected %.200s, %.200s class \"%.200s\" function to have %d args, found %d", + class_type, + py_class_name, + RNA_function_identifier(func), + func_arg_count, + arg_count); } + Py_DECREF(item_orig); + return -1; } } + Py_DECREF(item_orig); } /* Verify properties. */ @@ -8303,7 +8362,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param /* XXX, this is needed because render engine calls without a context * this should be supported at some point, but at the moment it's not! */ if (C == NULL) { - C = BPy_GetContext(); + C = BPY_context_get(); } /* Annoying! We need to check if the screen gets set to NULL which is a @@ -8690,19 +8749,21 @@ void pyrna_free_types(void) PyDoc_STRVAR(pyrna_register_class_doc, ".. method:: register_class(cls)\n" "\n" - " Register a subclass of a blender type in (:class:`bpy.types.Panel`,\n" - " :class:`bpy.types.UIList`, :class:`bpy.types.Menu`, :class:`bpy.types.Header`,\n" - " :class:`bpy.types.Operator`, :class:`bpy.types.KeyingSetInfo`,\n" - " :class:`bpy.types.RenderEngine`).\n" + " Register a subclass of a Blender type class.\n" "\n" - " If the class has a *register* class method it will be called\n" - " before registration.\n" + " :arg cls: Blender type class in:\n" + " :class:`bpy.types.Panel`, :class:`bpy.types.UIList`,\n" + " :class:`bpy.types.Menu`, :class:`bpy.types.Header`,\n" + " :class:`bpy.types.Operator`, :class:`bpy.types.KeyingSetInfo`,\n" + " :class:`bpy.types.RenderEngine`\n" + " :type cls: class\n" + " :raises ValueError:\n" + " if the class is not a subclass of a registerable blender class.\n" "\n" " .. note::\n" "\n" - " :exc:`ValueError` exception is raised if the class is not a\n" - " subclass of a registerable blender class.\n" - "\n"); + " If the class has a *register* class method it will be called\n" + " before registration.\n"); PyMethodDef meth_bpy_register_class = { "register_class", pyrna_register_class, METH_O, pyrna_register_class_doc}; static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class) @@ -8770,7 +8831,7 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class } /* Get the context, so register callback can do necessary refreshes. */ - C = BPy_GetContext(); + C = BPY_context_get(); /* Call the register callback with reports & identifier. */ BKE_reports_init(&reports, RPT_STORE); @@ -8808,7 +8869,10 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class /* Old srna still references us, keep the check in case registering somehow can free it. */ if (RNA_struct_py_type_get(srna)) { RNA_struct_py_type_set(srna, NULL); - // Py_DECREF(py_class); /* Should be able to do this XXX since the old RNA adds a new ref. */ +#if 0 + /* Should be able to do this XXX since the old RNA adds a new ref. */ + Py_DECREF(py_class); +#endif } /* Can't use this because it returns a dict proxy @@ -8978,7 +9042,7 @@ static PyObject *pyrna_unregister_class(PyObject *UNUSED(self), PyObject *py_cla } /* Get the context, so register callback can do necessary refreshes. */ - C = BPy_GetContext(); + C = BPY_context_get(); /* Call unregister. */ unreg(CTX_data_main(C), srna); /* Calls bpy_class_free, this decref's py_class. */ diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index 1d52706c5f9..b7648ee830f 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -259,7 +259,7 @@ static int pyrna_struct_keyframe_parse(PointerRNA *ptr, } if (*r_cfra == FLT_MAX) { - *r_cfra = CTX_data_scene(BPy_GetContext())->r.cfra; + *r_cfra = CTX_data_scene(BPY_context_get())->r.cfra; } /* flag may be null (no option currently for remove keyframes e.g.). */ @@ -341,7 +341,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb * It is unlikely that driver code (which is the reason this depsgraph pointer is obtained) will * be executed from this function call, as this only happens when `options` has * `INSERTKEY_DRIVER`, which is not exposed to Python. */ - bContext *C = BPy_GetContext(); + bContext *C = BPY_context_get(); struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, cfra); @@ -498,7 +498,7 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb int i; /* try to find index of beztriple to get rid of */ - i = binarysearch_bezt_index(fcu->bezt, cfra, fcu->totvert, &found); + i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ delete_fcurve_key(fcu, i, 1); @@ -600,8 +600,8 @@ PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args) ret = pyrna_struct_CreatePyObject(&tptr); } - bContext *context = BPy_GetContext(); - WM_event_add_notifier(BPy_GetContext(), NC_ANIMATION | ND_FCURVES_ORDER, NULL); + bContext *context = BPY_context_get(); + WM_event_add_notifier(BPY_context_get(), NC_ANIMATION | ND_FCURVES_ORDER, NULL); DEG_relations_tag_update(CTX_data_main(context)); } else { @@ -659,7 +659,7 @@ PyObject *pyrna_struct_driver_remove(BPy_StructRNA *self, PyObject *args) return NULL; } - bContext *context = BPy_GetContext(); + bContext *context = BPY_context_get(); WM_event_add_notifier(context, NC_ANIMATION | ND_FCURVES_ORDER, NULL); DEG_relations_tag_update(CTX_data_main(context)); diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 542d014f8b2..1f27e4bcca8 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -433,14 +433,14 @@ static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *ar "Gizmo target property array") == -1) { goto fail; } - WM_gizmo_target_property_float_set_array(BPy_GetContext(), gz, gz_prop, value); + WM_gizmo_target_property_float_set_array(BPY_context_get(), gz, gz_prop, value); } else { float value; if ((value = PyFloat_AsDouble(params.value)) == -1.0f && PyErr_Occurred()) { goto fail; } - WM_gizmo_target_property_float_set(BPy_GetContext(), gz, gz_prop, value); + WM_gizmo_target_property_float_set(BPY_context_get(), gz, gz_prop, value); } Py_RETURN_NONE; } diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index 30623724593..042f7b6fd67 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -87,7 +87,7 @@ static struct PyMethodDef pyrna_uilayout_methods[] = { /* -------------------------------------------------------------------- */ /** \name Window Manager Clipboard Property * - * Avoid using the RNA API because this value may change between checking it's length + * Avoid using the RNA API because this value may change between checking its length * and creating the buffer, causing writes past the allocated length. * \{ */ diff --git a/source/blender/python/intern/bpy_rna_ui.c b/source/blender/python/intern/bpy_rna_ui.c index 9f37b8300db..ec92eadfd72 100644 --- a/source/blender/python/intern/bpy_rna_ui.c +++ b/source/blender/python/intern/bpy_rna_ui.c @@ -17,7 +17,7 @@ /** \file * \ingroup pythonintern * - * This adds helpers to #uiLayout which can't be added easily to RNA it's self. + * This adds helpers to #uiLayout which can't be added easily to RNA itself. */ #include <Python.h> @@ -29,6 +29,7 @@ #include "UI_interface.h" #include "bpy_rna.h" +#include "bpy_rna_ui.h" PyDoc_STRVAR(bpy_rna_uilayout_introspect_doc, ".. method:: introspect()\n" diff --git a/source/blender/python/intern/bpy_utils_previews.c b/source/blender/python/intern/bpy_utils_previews.c index 81b44dd5c43..7a826d99a3d 100644 --- a/source/blender/python/intern/bpy_utils_previews.c +++ b/source/blender/python/intern/bpy_utils_previews.c @@ -49,15 +49,20 @@ #define STR_SOURCE_TYPES "'IMAGE', 'MOVIE', 'BLEND', 'FONT'" -PyDoc_STRVAR(bpy_utils_previews_new_doc, - ".. method:: new(name)\n" - "\n" - " Generate a new empty preview, or return existing one matching ``name``.\n" - "\n" - " :arg name: The name (unique id) identifying the preview.\n" - " :type name: string\n" - " :return: The Preview matching given name, or a new empty one.\n" - " :rtype: :class:`bpy.types.ImagePreview`\n"); +PyDoc_STRVAR( + bpy_utils_previews_new_doc, + ".. method:: new(name)\n" + "\n" + " Generate a new empty preview.\n" + "\n" + " :arg name: The name (unique id) identifying the preview.\n" + " :type name: string\n" + " :return: The Preview matching given name, or a new empty one.\n" + " :rtype: :class:`bpy.types.ImagePreview`\n" + " :rtype: :class:`bpy.types.ImagePreview`\n" + /* This is only true when accessed via 'bpy.utils.previews.ImagePreviewCollection.load', + * however this is the public API, allow this minor difference to the internal version here. */ + " :raises KeyError: if ``name`` already exists."); static PyObject *bpy_utils_previews_new(PyObject *UNUSED(self), PyObject *args) { char *name; @@ -78,7 +83,7 @@ PyDoc_STRVAR( bpy_utils_previews_load_doc, ".. method:: load(name, filepath, filetype, force_reload=False)\n" "\n" - " Generate a new preview from given file path, or return existing one matching ``name``.\n" + " Generate a new preview from given file path.\n" "\n" " :arg name: The name (unique id) identifying the preview.\n" " :type name: string\n" @@ -91,7 +96,10 @@ PyDoc_STRVAR( "exists in cache.\n" " :type force_reload: bool\n" " :return: The Preview matching given name, or a new empty one.\n" - " :rtype: :class:`bpy.types.ImagePreview`\n"); + " :rtype: :class:`bpy.types.ImagePreview`\n" + /* This is only true when accessed via 'bpy.utils.previews.ImagePreviewCollection.load', + * however this is the public API, allow this minor difference to the internal version here. */ + " :raises KeyError: if ``name`` already exists."); static PyObject *bpy_utils_previews_load(PyObject *UNUSED(self), PyObject *args) { char *name, *path, *path_type_s; diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index a58e2bcb975..b460ef5e0c0 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -73,15 +73,15 @@ static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usyste static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)]; static PyStructSequence_Desc bpyunits_systems_desc = { - "bpy.utils.units.systems", /* name */ - "This named tuple contains all pre-defined unit systems", /* doc */ - bpyunits_systems_fields, /* fields */ + "bpy.utils.units.systems", /* name */ + "This named tuple contains all predefined unit systems", /* doc */ + bpyunits_systems_fields, /* fields */ ARRAY_SIZE(bpyunits_systems_fields) - 1, }; static PyStructSequence_Desc bpyunits_categories_desc = { - "bpy.utils.units.categories", /* name */ - "This named tuple contains all pre-defined unit names", /* doc */ - bpyunits_categories_fields, /* fields */ + "bpy.utils.units.categories", /* name */ + "This named tuple contains all predefined unit names", /* doc */ + bpyunits_categories_fields, /* fields */ ARRAY_SIZE(bpyunits_categories_fields) - 1, }; |