diff options
Diffstat (limited to 'source/blender/python/intern')
-rw-r--r-- | source/blender/python/intern/CMakeLists.txt | 9 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app.c | 19 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app_handlers.c | 4 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app_opensubdiv.c | 109 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app_opensubdiv.h | 32 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app_sdl.c | 2 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_interface.c | 16 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator_wrap.c | 4 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator_wrap.h | 4 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_props.c | 136 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_props.h | 4 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.c | 88 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.h | 1 |
13 files changed, 361 insertions, 67 deletions
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 038c1e7eb10..be4db6477fe 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -55,6 +55,7 @@ set(SRC bpy_app_handlers.c bpy_app_ocio.c bpy_app_oiio.c + bpy_app_opensubdiv.c bpy_app_openvdb.c bpy_app_sdl.c bpy_app_translations.c @@ -89,6 +90,7 @@ set(SRC bpy_app_handlers.h bpy_app_ocio.h bpy_app_oiio.h + bpy_app_opensubdiv.h bpy_app_openvdb.h bpy_app_sdl.h bpy_app_translations.h @@ -295,6 +297,13 @@ if(WITH_OPENIMAGEIO) ) endif() +if(WITH_OPENSUBDIV) + add_definitions(-DWITH_OPENSUBDIV) + list(APPEND INC + ../../../../intern/opensubdiv + ) +endif() + if(WITH_PLAYER) add_definitions(-DWITH_PLAYER) endif() diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index ed7cec2f2d5..e47bf21f04b 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -37,6 +37,7 @@ #include "bpy_app_ffmpeg.h" #include "bpy_app_ocio.h" #include "bpy_app_oiio.h" +#include "bpy_app_opensubdiv.h" #include "bpy_app_openvdb.h" #include "bpy_app_sdl.h" #include "bpy_app_build_options.h" @@ -88,6 +89,7 @@ static PyStructSequence_Field app_info_fields[] = { {(char *)"version_cycle", (char *)"The release status of this build alpha/beta/rc/release"}, {(char *)"binary_path", (char *)"The location of blenders executable, useful for utilities that spawn new instances"}, {(char *)"background", (char *)"Boolean, True when blender is running without a user interface (started with -b)"}, + {(char *)"factory_startup", (char *)"Boolean, True when blender is running with --factory-startup)"}, /* buildinfo */ {(char *)"build_date", (char *)"The date this blender instance was built"}, @@ -109,6 +111,7 @@ static PyStructSequence_Field app_info_fields[] = { {(char *)"ffmpeg", (char *)"FFmpeg library information backend"}, {(char *)"ocio", (char *)"OpenColorIO library information backend"}, {(char *)"oiio", (char *)"OpenImageIO library information backend"}, + {(char *)"opensubdiv", (char *)"OpenSubdiv library information backend"}, {(char *)"openvdb", (char *)"OpenVDB library information backend"}, {(char *)"sdl", (char *)"SDL library information backend"}, {(char *)"build_options", (char *)"A set containing most important enabled optional build features"}, @@ -117,9 +120,21 @@ static PyStructSequence_Field app_info_fields[] = { {NULL}, }; +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.translations.rst\n" +); + static PyStructSequence_Desc app_info_desc = { (char *)"bpy.app", /* name */ - (char *)"This module contains application values that remain unchanged during runtime.", /* doc */ + bpy_app_doc, /* doc */ app_info_fields, /* fields */ ARRAY_SIZE(app_info_fields) - 1 }; @@ -151,6 +166,7 @@ static PyObject *make_app_info(void) SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE)); SetStrItem(BKE_appdir_program_path()); SetObjItem(PyBool_FromLong(G.background)); + SetObjItem(PyBool_FromLong(G.factory_startup)); /* build info, use bytes since we can't assume _any_ encoding: * see patch [#30154] for issue */ @@ -188,6 +204,7 @@ static PyObject *make_app_info(void) 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()); diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index 1cc2d6f1307..fdc2371c259 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -126,7 +126,7 @@ static PyObject *bpy_app_handlers_persistent_new(PyTypeObject *UNUSED(type), PyO /* dummy type because decorators can't be PyCFunctions */ static PyTypeObject BPyPersistent_Type = { -#if defined(_MSC_VER) || defined(FREE_WINDOWS) +#if defined(_MSC_VER) PyVarObject_HEAD_INIT(NULL, 0) #else PyVarObject_HEAD_INIT(&PyType_Type, 0) @@ -206,7 +206,7 @@ PyObject *BPY_app_handlers_struct(void) { PyObject *ret; -#if defined(_MSC_VER) || defined(FREE_WINDOWS) +#if defined(_MSC_VER) BPyPersistent_Type.ob_base.ob_base.ob_type = &PyType_Type; #endif diff --git a/source/blender/python/intern/bpy_app_opensubdiv.c b/source/blender/python/intern/bpy_app_opensubdiv.c new file mode 100644 index 00000000000..7f269baf2b0 --- /dev/null +++ b/source/blender/python/intern/bpy_app_opensubdiv.c @@ -0,0 +1,109 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_app_opensubdiv.c + * \ingroup pythonintern + */ + +#include <Python.h> +#include "BLI_utildefines.h" + +#include "bpy_app_opensubdiv.h" + +#ifdef WITH_OPENSUBDIV +# include "opensubdiv_capi.h" +#endif + +static PyTypeObject BlenderAppOpenSubdivType; + +static PyStructSequence_Field app_opensubdiv_info_fields[] = { + {(char *)"supported", (char *)("Boolean, True when Blender is built with OpenSubdiv support")}, + {(char *)("version"), (char *)("The OpenSubdiv version as a tuple of 3 numbers")}, + {(char *)("version_string"), (char *)("The OpenSubdiv version formatted as a string")}, + {NULL} +}; + +static PyStructSequence_Desc app_opensubdiv_info_desc = { + (char *)"bpy.app.opensubdiv", /* name */ + (char *)"This module contains information about OpenSubdiv blender is linked against", /* doc */ + app_opensubdiv_info_fields, /* fields */ + ARRAY_SIZE(app_opensubdiv_info_fields) - 1 +}; + +static PyObject *make_opensubdiv_info(void) +{ + PyObject *opensubdiv_info; + int pos = 0; + + opensubdiv_info = PyStructSequence_New(&BlenderAppOpenSubdivType); + if (opensubdiv_info == NULL) { + return NULL; + } + +#ifndef WITH_OPENSUBDIV +#define SetStrItem(str) \ + PyStructSequence_SET_ITEM(opensubdiv_info, pos++, PyUnicode_FromString(str)) +#endif + +#define SetObjItem(obj) \ + PyStructSequence_SET_ITEM(opensubdiv_info, pos++, obj) + +#ifdef WITH_OPENSUBDIV + int curversion = openSubdiv_getVersionHex(); + SetObjItem(PyBool_FromLong(1)); + SetObjItem(Py_BuildValue("(iii)", + curversion / 10000, (curversion / 100) % 100, curversion % 100)); + SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d", + curversion / 10000, (curversion / 100) % 100, curversion % 100)); +#else + SetObjItem(PyBool_FromLong(0)); + SetObjItem(Py_BuildValue("(iii)", 0, 0, 0)); + SetStrItem("Unknown"); +#endif + + if (PyErr_Occurred()) { + Py_CLEAR(opensubdiv_info); + return NULL; + } + +#undef SetStrItem +#undef SetObjItem + + return opensubdiv_info; +} + +PyObject *BPY_app_opensubdiv_struct(void) +{ + PyObject *ret; + + PyStructSequence_InitType(&BlenderAppOpenSubdivType, &app_opensubdiv_info_desc); + + ret = make_opensubdiv_info(); + + /* prevent user from creating new instances */ + BlenderAppOpenSubdivType.tp_init = NULL; + BlenderAppOpenSubdivType.tp_new = NULL; + /* without this we can't do set(sys.modules) [#29635] */ + BlenderAppOpenSubdivType.tp_hash = (hashfunc)_Py_HashPointer; + + return ret; +} diff --git a/source/blender/python/intern/bpy_app_opensubdiv.h b/source/blender/python/intern/bpy_app_opensubdiv.h new file mode 100644 index 00000000000..b1da218b168 --- /dev/null +++ b/source/blender/python/intern/bpy_app_opensubdiv.h @@ -0,0 +1,32 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_app_opensubdiv.h + * \ingroup pythonintern + */ + +#ifndef __BPY_APP_OPENSUBDIV_H__ +#define __BPY_APP_OPENSUBDIV_H__ + +PyObject *BPY_app_opensubdiv_struct(void); + +#endif /* __BPY_APP_OPENSUBDIV_H__ */ diff --git a/source/blender/python/intern/bpy_app_sdl.c b/source/blender/python/intern/bpy_app_sdl.c index 2f4d8e6c325..76dab775953 100644 --- a/source/blender/python/intern/bpy_app_sdl.c +++ b/source/blender/python/intern/bpy_app_sdl.c @@ -56,7 +56,7 @@ static PyStructSequence_Field app_sdl_info_fields[] = { {(char *)"available", (char *)("Boolean, True when SDL is available. This is False when " "either *supported* is False, or *dynload* is True and " "Blender cannot find the correct library.")}, - {NULL} + {NULL} }; static PyStructSequence_Desc app_sdl_info_desc = { diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 55e477b0214..20cfd364a0c 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -480,11 +480,20 @@ static bool python_script_exec( * object, but as written in the Python/C API Ref Manual, chapter 2, * 'FILE structs for different C libraries can be different and * incompatible'. - * So now we load the script file data to a buffer */ + * So now we load the script file data to a buffer. + * + * Note on use of 'globals()', it's important not copy the dictionary because + * tools may inspect 'sys.modules["__main__"]' for variables defined in the code + * where using a copy of 'globals()' causes code execution + * to leave the main namespace untouched. see: T51444 + * + * This leaves us with the problem of variables being included, + * currently this is worked around using 'dict.__del__' it's ugly but works. + */ { const char *pystring = - "ns = globals().copy()\n" - "with open(__file__, 'rb') as f: exec(compile(f.read(), __file__, 'exec'), ns)"; + "with open(__file__, 'rb') as f:" + "exec(compile(f.read(), __file__, 'exec'), globals().__delitem__('f') or globals())"; fclose(fp); @@ -860,6 +869,7 @@ static void bpy_module_delay_init(PyObject *bpy_proxy) BLI_strncpy(filename_abs, filename_rel, sizeof(filename_abs)); BLI_path_cwd(filename_abs, sizeof(filename_abs)); + Py_DECREF(filename_obj); argv[0] = filename_abs; argv[1] = NULL; diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 11e27ca3e3c..90719905a79 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -118,7 +118,7 @@ static void operator_properties_init(wmOperatorType *ot) } -void operator_wrapper(wmOperatorType *ot, void *userdata) +void BPY_RNA_operator_wrapper(wmOperatorType *ot, void *userdata) { /* take care not to overwrite anything set in * WM_operatortype_append_ptr before opfunc() is called */ @@ -134,7 +134,7 @@ void operator_wrapper(wmOperatorType *ot, void *userdata) operator_properties_init(ot); } -void macro_wrapper(wmOperatorType *ot, void *userdata) +void BPY_RNA_operator_macro_wrapper(wmOperatorType *ot, void *userdata) { wmOperatorType *data = (wmOperatorType *)userdata; diff --git a/source/blender/python/intern/bpy_operator_wrap.h b/source/blender/python/intern/bpy_operator_wrap.h index 05a566a1485..0828c58e2bd 100644 --- a/source/blender/python/intern/bpy_operator_wrap.h +++ b/source/blender/python/intern/bpy_operator_wrap.h @@ -33,7 +33,7 @@ struct wmOperatorType; PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args); /* exposed to rna/wm api */ -void operator_wrapper(struct wmOperatorType *ot, void *userdata); -void macro_wrapper(struct wmOperatorType *ot, void *userdata); +void BPY_RNA_operator_wrapper(struct wmOperatorType *ot, void *userdata); +void BPY_RNA_operator_macro_wrapper(struct wmOperatorType *ot, void *userdata); #endif diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index f7348ac2250..362c0281b36 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -50,11 +50,13 @@ #include "../generic/py_capi_utils.h" /* initial definition of callback slots we'll probably have more than 1 */ -#define BPY_DATA_CB_SLOT_SIZE 3 - -#define BPY_DATA_CB_SLOT_UPDATE 0 -#define BPY_DATA_CB_SLOT_GET 1 -#define BPY_DATA_CB_SLOT_SET 2 +enum { + BPY_DATA_CB_SLOT_UPDATE = 0, + BPY_DATA_CB_SLOT_GET = 1, + BPY_DATA_CB_SLOT_SET = 2, + BPY_DATA_CB_SLOT_POLL = 3, + BPY_DATA_CB_SLOT_SIZE = 4, +}; extern BPy_StructRNA *bpy_context_module; @@ -71,6 +73,9 @@ static EnumPropertyItem property_flag_items[] = { " :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', 'PROPORTIONAL'," \ "'TEXTEDIT_UPDATE'].\n" \ " :type options: set\n" \ +" :arg poll: function to be called to determine whether an item is valid for this property.\n" \ +" The function must take 2 values (self,object) and return Bool.\n" \ +" :type poll: function\n" \ static EnumPropertyItem property_flag_enum_items[] = { {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, @@ -389,6 +394,51 @@ static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA * } } +static int bpy_prop_poll_cb(struct PointerRNA *self, PointerRNA candidate, struct PropertyRNA *prop) +{ + PyObject *py_self; + PyObject *py_candidate; + PyObject *py_func; + PyObject **py_data = RNA_property_py_data_get(prop); + PyObject *args; + PyObject *ret; + bool result; + const int is_write_ok = pyrna_write_check(); + PyGILState_STATE gilstate = PyGILState_Ensure(); + + BLI_assert(self != NULL); + + py_self = pyrna_struct_as_instance(self); + py_candidate = pyrna_struct_as_instance(&candidate); + py_func = py_data[BPY_DATA_CB_SLOT_POLL]; + + if (!is_write_ok) + pyrna_write_set(true); + + args = PyTuple_New(2); + PyTuple_SET_ITEM(args, 0, py_self); + PyTuple_SET_ITEM(args, 1, py_candidate); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + printf_func_error(py_func); + result = false; + } + else { + result = PyObject_IsTrue(ret); + Py_DECREF(ret); + } + + PyGILState_Release(gilstate); + if (!is_write_ok) + pyrna_write_set(false); + + return result; +} + static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values) { PyObject **py_data = RNA_property_py_data_get(prop); @@ -1598,6 +1648,16 @@ static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject * } } +static void bpy_prop_callback_assign_pointer(struct PropertyRNA *prop, PyObject *poll_cb) +{ + if (poll_cb && poll_cb != Py_None) { + PyObject **py_data = bpy_prop_py_data_get(prop); + + RNA_def_property_poll_runtime(prop, (void *) bpy_prop_poll_cb); + py_data[BPY_DATA_CB_SLOT_POLL] = poll_cb; + } +} + static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb) { BooleanPropertyGetFunc rna_get_cb = NULL; @@ -1904,7 +1964,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge " :type set: function\n" \ #define BPY_PROPDEF_TYPE_DOC \ -" :arg type: A subclass of :class:`bpy.types.PropertyGroup`.\n" \ +" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \ " :type type: class\n" \ #if 0 @@ -2637,7 +2697,8 @@ PyDoc_STRVAR(BPy_EnumProperty_doc, " :icon: An icon string identifier or integer icon value\n" " (e.g. returned by :class:`bpy.types.UILayout.icon`)\n" " :number: Unique value used as the identifier for this item (stored in file data).\n" -" Use when the identifier may need to change.\n" +" Use when the identifier may need to change. If the *ENUM_FLAG* option is used,\n" +" the values are bitmasks and should be powers of two.\n" "\n" " When an item only contains 4 items they define ``(identifier, name, description, number)``.\n" "\n" @@ -2772,7 +2833,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) Py_RETURN_NONE; } -static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix) +StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix) { StructRNA *srna; @@ -2782,25 +2843,18 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix PyObject *msg = PyC_ExceptionBuffer(); const char *msg_char = _PyUnicode_AsString(msg); PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup, failed with: %s", + "%.200s expected an RNA type, failed with: %s", error_prefix, msg_char); Py_DECREF(msg); } else { PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup, failed with type '%s'", + "%.200s expected an RNA type, failed with type '%s'", error_prefix, Py_TYPE(value)->tp_name); } return NULL; } - if (!RNA_struct_is_a(srna, &RNA_PropertyGroup)) { - PyErr_Format(PyExc_TypeError, - "%.200s expected an RNA type derived from PropertyGroup", - error_prefix); - return NULL; - } - return srna; } @@ -2809,6 +2863,7 @@ PyDoc_STRVAR(BPy_PointerProperty_doc, "name=\"\", " "description=\"\", " "options={'ANIMATABLE'}, " + "poll=None, " "update=None)\n" "\n" " Returns a new pointer property definition.\n" @@ -2819,14 +2874,14 @@ BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_UPDATE_DOC ); -static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) +PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; BPY_PROPDEF_HEAD(PointerProperty); if (srna) { - static const char *kwlist[] = {"attr", "type", "name", "description", "options", "update", NULL}; + static const char *kwlist[] = {"attr", "type", "name", "description", "options", "poll", "update", NULL}; const char *id = NULL, *name = NULL, *description = ""; int id_len; PropertyRNA *prop; @@ -2834,33 +2889,47 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k PyObject *type = Py_None; PyObject *pyopts = NULL; int opts = 0; - PyObject *update_cb = NULL; + PyObject *update_cb = NULL, *poll_cb = NULL; if (!PyArg_ParseTupleAndKeywords(args, kw, - "s#O|ssO!O:PointerProperty", + "s#O|ssO!OOO:PointerProperty", (char **)kwlist, &id, &id_len, &type, &name, &description, &PySet_Type, &pyopts, - &update_cb)) + &poll_cb, &update_cb)) { return NULL; } BPY_PROPDEF_CHECK(PointerProperty, property_flag_items); - ptype = pointer_type_from_py(type, "PointerProperty(...):"); + ptype = pointer_type_from_py(type, "PointerProperty(...)"); if (!ptype) return NULL; - + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) { + PyErr_Format(PyExc_TypeError, + "PointerProperty(...) expected an RNA type derived from %.200s or %.200s", + RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; } - + if (bpy_prop_callback_check(poll_cb, "poll", 2) == -1) { + return NULL; + } prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description); if (pyopts) { bpy_prop_assign_flag(prop, opts); } + + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); + } + } bpy_prop_callback_assign_update(prop, update_cb); + bpy_prop_callback_assign_pointer(prop, poll_cb); RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; @@ -2879,7 +2948,7 @@ BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC ); -static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) +PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2910,17 +2979,30 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject if (!ptype) return NULL; + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) { + PyErr_Format(PyExc_TypeError, + "CollectionProperty(...) expected an RNA type derived from %.200s", + RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } + prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description); if (pyopts) { bpy_prop_assign_flag(prop, opts); } + + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); + } + } RNA_def_property_duplicate_pointers(srna, prop); } Py_RETURN_NONE; } PyDoc_STRVAR(BPy_RemoveProperty_doc, -".. function:: RemoveProperty(cls, attr="")\n" +".. function:: RemoveProperty(cls, attr)\n" "\n" " Removes a dynamically defined property.\n" "\n" diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h index c9934ca0cf3..614c1b4b708 100644 --- a/source/blender/python/intern/bpy_props.h +++ b/source/blender/python/intern/bpy_props.h @@ -30,6 +30,10 @@ PyObject *BPY_rna_props(void); +PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw); +PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw); +StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix); + #define PYRNA_STACK_ARRAY 32 #endif diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 2fd46ab94f0..eda880d4dce 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -806,7 +806,7 @@ static PyObject *pyrna_struct_richcmp(PyObject *a, PyObject *b, int op) switch (op) { case Py_NE: ok = !ok; - /* fall-through */ + ATTR_FALLTHROUGH; case Py_EQ: res = ok ? Py_False : Py_True; break; @@ -836,7 +836,7 @@ static PyObject *pyrna_prop_richcmp(PyObject *a, PyObject *b, int op) switch (op) { case Py_NE: ok = !ok; - /* fall-through */ + ATTR_FALLTHROUGH; case Py_EQ: res = ok ? Py_False : Py_True; break; @@ -1934,16 +1934,10 @@ static int pyrna_py_to_prop( } else { /* data == NULL, assign to RNA */ - if (value == Py_None) { - PointerRNA valueptr = {{NULL}}; - RNA_property_pointer_set(ptr, prop, valueptr); - } - else if (RNA_struct_is_a(param->ptr.type, ptr_type)) { - RNA_property_pointer_set(ptr, prop, param->ptr); - } - else { + if (value == Py_None || RNA_struct_is_a(param->ptr.type, ptr_type)) + RNA_property_pointer_set(ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr); + else raise_error = true; - } } if (raise_error) { @@ -3277,6 +3271,20 @@ static int pyrna_struct_ass_subscript(BPy_StructRNA *self, PyObject *key, PyObje return -1; } + if (value && BPy_StructRNA_Check(value)) { + BPy_StructRNA *val = (BPy_StructRNA *)value; + if (val && self->ptr.type && val->ptr.type) { + if (!RNA_struct_idprops_datablock_allowed(self->ptr.type) && + RNA_struct_idprops_contains_datablock(val->ptr.type)) + { + PyErr_SetString( + PyExc_TypeError, + "bpy_struct[key] = val: datablock id properties not supported for this type"); + return -1; + } + } + } + return BPy_Wrap_SetMapItem(group, key, value); } @@ -5160,7 +5168,7 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat ret = Matrix_CreatePyObject(data, 3, 3, NULL); break; } - /* fall-through */ + ATTR_FALLTHROUGH; #endif default: ret = PyTuple_New(len); @@ -5684,7 +5692,7 @@ PyTypeObject pyrna_struct_meta_idprop_Type = { NULL, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ NULL, /* struct PyGetSetDef *tp_getset; */ -#if defined(_MSC_VER) || defined(FREE_WINDOWS) +#if defined(_MSC_VER) NULL, /* defer assignment */ #else &PyType_Type, /* struct _typeobject *tp_base; */ @@ -6259,7 +6267,7 @@ static PyTypeObject pyrna_prop_collection_iter_Type = { NULL, /* reprfunc tp_str; */ /* will only use these if this is a subtype of a py class */ -#if defined(_MSC_VER) || defined(FREE_WINDOWS) +#if defined(_MSC_VER) NULL, /* defer assignment */ #else PyObject_GenericGetAttr, /* getattrofunc tp_getattro; */ @@ -6292,7 +6300,7 @@ static PyTypeObject pyrna_prop_collection_iter_Type = { #endif /*** Added in release 2.2 ***/ /* Iterators */ -#if defined(_MSC_VER) || defined(FREE_WINDOWS) +#if defined(_MSC_VER) NULL, /* defer assignment */ #else PyObject_SelfIter, /* getiterfunc tp_iter; */ @@ -6745,7 +6753,7 @@ PyObject *pyrna_id_CreatePyObject(ID *id) bool pyrna_id_FromPyObject(PyObject *obj, ID **id) { - if (BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *)obj)->ptr.type))) { + if (pyrna_id_CheckPyObject(obj)) { *id = ((BPy_StructRNA *)obj)->ptr.id.data; return true; } @@ -6755,6 +6763,11 @@ bool pyrna_id_FromPyObject(PyObject *obj, ID **id) } } +bool pyrna_id_CheckPyObject(PyObject *obj) +{ + return BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *) obj)->ptr.type)); +} + void BPY_rna_init(void) { #ifdef USE_MATHUTILS /* register mathutils callbacks, ok to run more than once. */ @@ -6763,7 +6776,7 @@ void BPY_rna_init(void) #endif /* for some reason MSVC complains of these */ -#if defined(_MSC_VER) || defined(FREE_WINDOWS) +#if defined(_MSC_VER) pyrna_struct_meta_idprop_Type.tp_base = &PyType_Type; pyrna_prop_collection_iter_Type.tp_iter = PyObject_SelfIter; @@ -7089,6 +7102,21 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item args_fake = PyTuple_New(1); PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject); + PyObject *type = PyDict_GetItemString(py_kw, "type"); + StructRNA *type_srna = srna_from_self(type, ""); + if (type_srna) { + if (!RNA_struct_idprops_datablock_allowed(srna) && + (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty || + *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) && + RNA_struct_idprops_contains_datablock(type_srna)) + { + PyErr_Format(PyExc_ValueError, + "bpy_struct \"%.200s\" doesn't support datablock properties \n", + RNA_struct_identifier(srna)); + return -1; + } + } + py_ret = PyObject_Call(py_func, args_fake, py_kw); if (py_ret) { @@ -7237,15 +7265,12 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v { const ListBase *lb; Link *link; - FunctionRNA *func; - PropertyRNA *prop; const char *class_type = RNA_struct_identifier(srna); StructRNA *srna_base = RNA_struct_base(srna); PyObject *py_class = (PyObject *)py_data; PyObject *base_class = RNA_struct_py_type_get(srna); PyObject *item; - int i, flag, arg_count, func_arg_count, func_arg_min_count = 0; - bool is_staticmethod; + int i, arg_count, func_arg_count, func_arg_min_count = 0; const char *py_class_name = ((PyTypeObject *)py_class)->tp_name; /* __name__ */ if (srna_base) { @@ -7266,9 +7291,12 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v lb = RNA_struct_type_functions(srna); i = 0; for (link = lb->first; link; link = link->next) { - func = (FunctionRNA *)link; - flag = RNA_function_flag(func); - is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); + 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 its 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; @@ -7294,7 +7322,8 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v if (is_staticmethod) { if (PyMethod_Check(item) == 0) { PyErr_Format(PyExc_TypeError, - "expected %.200s, %.200s class \"%.200s\" attribute to be a method, not a %.200s", + "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; } @@ -7303,7 +7332,8 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v else { if (PyFunction_Check(item) == 0) { PyErr_Format(PyExc_TypeError, - "expected %.200s, %.200s class \"%.200s\" attribute to be a function, not a %.200s", + "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; } @@ -7315,7 +7345,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v 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), + * '@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++; @@ -7346,8 +7376,8 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v lb = RNA_struct_type_properties(srna); for (link = lb->first; link; link = link->next) { const char *identifier; - prop = (PropertyRNA *)link; - flag = RNA_property_flag(prop); + PropertyRNA *prop = (PropertyRNA *)link; + const int flag = RNA_property_flag(prop); if (!(flag & PROP_REGISTER)) continue; diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index e38d4f095d6..605f79b1ad8 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -179,6 +179,7 @@ PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop); /* extern'd by other modules which don't deal closely with RNA */ PyObject *pyrna_id_CreatePyObject(struct ID *id); bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id); +bool pyrna_id_CheckPyObject(PyObject *obj); /* operators also need this to set args */ int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, const bool all_args, const char *error_prefix); |