diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2022-09-22 12:52:48 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2022-09-22 12:54:53 +0300 |
commit | 2db84a8ee7cd3873e11eb0c13abc09869caa0042 (patch) | |
tree | ea15209cc9742e23f36a7e692eb837723bfa64b3 /source/blender/python | |
parent | a41ee7ea1ae9bb1bcc537576d9ffdb3bca3201f3 (diff) |
Fix all merge conflicts
There was a lot of conflicts and to solve, I refresh manually the source folders.
Diffstat (limited to 'source/blender/python')
69 files changed, 857 insertions, 417 deletions
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 8075e4ecd22..aecefa97423 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -34,7 +34,7 @@ void BPY_pyconstraint_exec(struct bPythonConstraint *con, // void BPY_pyconstraint_settings(void *arg1, void *arg2); void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTarget *ct); void BPY_pyconstraint_update(struct Object *owner, struct bConstraint *con); -int BPY_is_pyconstraint(struct Text *text); +bool BPY_is_pyconstraint(struct Text *text); // void BPY_free_pyconstraint_links(struct Text *text); /* global interpreter lock */ diff --git a/source/blender/python/CMakeLists.txt b/source/blender/python/CMakeLists.txt index 9f6ee359d67..f82499ba1d4 100644 --- a/source/blender/python/CMakeLists.txt +++ b/source/blender/python/CMakeLists.txt @@ -1,5 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later +if(WITH_PYTHON_MODULE) + add_definitions(-DPy_ENABLE_SHARED) +endif() + add_subdirectory(intern) add_subdirectory(generic) add_subdirectory(gpu) diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c index 3ddab4bebd9..2e6d1698da9 100644 --- a/source/blender/python/bmesh/bmesh_py_api.c +++ b/source/blender/python/bmesh/bmesh_py_api.c @@ -160,7 +160,7 @@ static struct PyModuleDef BPy_BM_module_def = { BPy_BM_doc, /* m_doc */ 0, /* m_size */ BPy_BM_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_geometry.c b/source/blender/python/bmesh/bmesh_py_geometry.c index 5625812fbda..f2af8599807 100644 --- a/source/blender/python/bmesh/bmesh_py_geometry.c +++ b/source/blender/python/bmesh/bmesh_py_geometry.c @@ -66,7 +66,7 @@ static struct PyModuleDef BPy_BM_geometry_module_def = { BPy_BM_utils_doc, /* m_doc */ 0, /* m_size */ BPy_BM_geometry_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_ops.c b/source/blender/python/bmesh/bmesh_py_ops.c index 8b343b00342..37e2b009f55 100644 --- a/source/blender/python/bmesh/bmesh_py_ops.c +++ b/source/blender/python/bmesh/bmesh_py_ops.c @@ -267,7 +267,7 @@ static struct PyModuleDef BPy_BM_ops_module_def = { BPy_BM_ops_doc, /* m_doc */ 0, /* m_size */ BPy_BM_ops_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 972a782d650..364adb5458b 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -3740,7 +3740,7 @@ static struct PyModuleDef BPy_BM_types_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -4273,10 +4273,10 @@ char *BPy_BMElem_StringFromHType(const char htype) /* -------------------------------------------------------------------- */ /* keep at bottom */ -/* this function is called on free, it should stay quite fast */ +/* This function is called on free, it should stay quite fast */ static void bm_dealloc_editmode_warn(BPy_BMesh *self) { if (self->flag & BPY_BMFLAG_IS_WRAPPED) { - /* currently nop - this works without warnings now */ + /* Currently NOP - this works without warnings now. */ } } diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c index d4455fd3668..9f200734786 100644 --- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c @@ -298,7 +298,7 @@ static int mathutils_bmloopcol_set(BaseMathObject *bmo, int UNUSED(subtype)) static int mathutils_bmloopcol_get_index(BaseMathObject *bmo, int subtype, int UNUSED(index)) { - /* lazy, avoid repeteing the case statement */ + /* Lazy, avoid repeating the case statement. */ if (mathutils_bmloopcol_get(bmo, subtype) == -1) { return -1; } @@ -309,7 +309,7 @@ static int mathutils_bmloopcol_set_index(BaseMathObject *bmo, int subtype, int i { const float f = bmo->data[index]; - /* lazy, avoid repeteing the case statement */ + /* Lazy, avoid repeating the case statement. */ if (mathutils_bmloopcol_get(bmo, subtype) == -1) { return -1; } diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c index 1bee1342a07..6630eb4924e 100644 --- a/source/blender/python/bmesh/bmesh_py_utils.c +++ b/source/blender/python/bmesh/bmesh_py_utils.c @@ -822,7 +822,7 @@ static struct PyModuleDef BPy_BM_utils_module_def = { BPy_BM_utils_doc, /* m_doc */ 0, /* m_size */ BPy_BM_utils_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index 69bcfdfae4e..27b7ad28943 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -7,12 +7,11 @@ set(INC ../../gpu ../../makesdna ../../makesrna - ../../../../intern/glew-mx ../../../../intern/guardedalloc ) set(INC_SYS - ${GLEW_INCLUDE_PATH} + ${Epoxy_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} ) @@ -41,11 +40,9 @@ set(SRC ) set(LIB - ${GLEW_LIBRARY} + ${Epoxy_LIBRARIES} ${PYTHON_LINKFLAGS} ${PYTHON_LIBRARIES} ) -add_definitions(${GL_DEFINITIONS}) - blender_add_lib(bf_python_ext "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 92ed0c51b1f..36ab1e86d92 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -19,7 +19,7 @@ #include "../generic/py_capi_utils.h" -#include "glew-mx.h" +#include <epoxy/gl.h> #include "bgl.h" @@ -1082,18 +1082,34 @@ static PyObject *Buffer_repr(Buffer *self) /** \name OpenGL API Wrapping * \{ */ -#define BGL_Wrap(funcname, ret, arg_list) \ - static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ - { \ - arg_def arg_list; \ - ret_def_##ret; \ - if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \ +#ifdef WITH_OPENGL +# define BGL_Wrap(funcname, ret, arg_list) \ + static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ + { \ + arg_def arg_list; \ + ret_def_##ret; \ + if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \ + return NULL; \ + } \ + GPU_bgl_start(); \ + ret_set_##ret gl##funcname(arg_var arg_list); \ + ret_ret_##ret; \ + } +#else + +static void bgl_no_opengl_error(void) +{ + PyErr_SetString(PyExc_RuntimeError, "Built without OpenGL support"); +} + +# define BGL_Wrap(funcname, ret, arg_list) \ + static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ + { \ + (void)args; \ + bgl_no_opengl_error(); \ return NULL; \ - } \ - GPU_bgl_start(); \ - ret_set_##ret gl##funcname(arg_var arg_list); \ - ret_ret_##ret; \ - } + } +#endif /* GL_VERSION_1_0 */ BGL_Wrap(BlendFunc, void, (GLenum, GLenum)); @@ -1381,7 +1397,7 @@ static struct PyModuleDef BGL_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -1421,12 +1437,22 @@ static void py_module_dict_add_method(PyObject *submodule, #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Waddress" #endif -#define PY_MOD_ADD_METHOD(func) \ - { \ - static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ - py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \ - } \ - ((void)0) + +#ifdef WITH_OPENGL +# define PY_MOD_ADD_METHOD(func) \ + { \ + static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ + py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \ + } \ + ((void)0) +#else +# define PY_MOD_ADD_METHOD(func) \ + { \ + static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ + py_module_dict_add_method(submodule, dict, &method_def, false); \ + } \ + ((void)0) +#endif static void init_bgl_version_1_0_methods(PyObject *submodule, PyObject *dict) { @@ -2620,9 +2646,13 @@ static PyObject *Method_ShaderSource(PyObject *UNUSED(self), PyObject *args) return NULL; } +#ifdef WITH_OPENGL glShaderSource(shader, 1, (const char **)&source, NULL); - Py_RETURN_NONE; +#else + bgl_no_opengl_error(); + return NULL; +#endif } /** \} */ diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c index 0f0a2bf0ed1..19958a99df9 100644 --- a/source/blender/python/generic/bl_math_py_api.c +++ b/source/blender/python/generic/bl_math_py_api.c @@ -133,7 +133,7 @@ static struct PyModuleDef M_bl_math_module_def = { M_bl_math_doc, /* m_doc */ 0, /* m_size */ M_bl_math_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 060b7758ea9..11b71256327 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -465,7 +465,7 @@ static struct PyModuleDef BLF_module_def = { BLF_doc, /* m_doc */ 0, /* m_size */ BLF_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 3f880da2f56..333ab9487d1 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -2119,7 +2119,7 @@ static struct PyModuleDef IDProp_types_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -2168,7 +2168,7 @@ static struct PyModuleDef IDProp_module_def = { IDProp_module_doc, /* m_doc */ 0, /* m_size */ IDProp_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index e6d90c46866..056af83cc49 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -570,7 +570,7 @@ static struct PyModuleDef IMB_module_def = { IMB_doc, /* m_doc */ 0, /* m_size */ IMB_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -614,7 +614,7 @@ static struct PyModuleDef IMB_types_module_def = { IMB_types_doc, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 8230801e184..681c5404ed9 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -28,8 +28,8 @@ # include "BLI_string.h" -/* Only for BLI_strncpy_wchar_from_utf8, - * should replace with py funcs but too late in release now. */ +/* Only for #BLI_strncpy_wchar_from_utf8, + * should replace with Python functions but too late in release now. */ # include "BLI_string_utf8.h" #endif @@ -823,6 +823,36 @@ void PyC_Err_PrintWithFunc(PyObject *py_func) /** \name Exception Buffer Access * \{ */ +static void pyc_exception_buffer_handle_system_exit(PyObject *error_type, + PyObject *error_value, + PyObject *error_traceback) +{ + if (!PyErr_GivenExceptionMatches(error_type, PyExc_SystemExit)) { + return; + } + /* Inspecting, follow Python's logic in #_Py_HandleSystemExit & treat as a regular exception. */ + if (_Py_GetConfig()->inspect) { + return; + } + + /* NOTE(@campbellbarton): A `SystemExit` exception will exit immediately (unless inspecting). + * So print the error and exit now. This is necessary as the call to #PyErr_Print exits, + * the temporary `sys.stderr` assignment causes the output to be suppressed, failing silently. + * Instead, restore the error and print it. If Python changes it's behavior and doesn't exit in + * the future - continue to create the exception buffer, see: T99966. + * + * Arguably accessing a `SystemExit` exception as a buffer should be supported without exiting. + * (by temporarily enabling inspection for example) however - it's not obvious exactly when this + * should be enabled and complicates the Python API by introducing different kinds of execution. + * Since the rule of thumb is for Blender's embedded Python to match stand-alone Python, + * favor exiting when a `SystemExit` is raised. + * Especially since this exception more likely to be used for background/batch-processing + * utilities where exiting immediately makes sense, the possibility of this being called + * indirectly from python-drivers or modal-operators is less of a concern. */ + PyErr_Restore(error_type, error_value, error_traceback); + PyErr_Print(); +} + /* returns the exception string as a new PyUnicode object, depends on external traceback module */ # if 0 @@ -873,6 +903,8 @@ PyObject *PyC_ExceptionBuffer(void) PyErr_Fetch(&error_type, &error_value, &error_traceback); + pyc_exception_buffer_handle_system_exit(error_type, error_value, error_traceback); + /* import io * string_io = io.StringIO() */ @@ -896,7 +928,7 @@ PyObject *PyC_ExceptionBuffer(void) PySys_SetObject("stderr", string_io); PyErr_Restore(error_type, error_value, error_traceback); - /* Printing clears (call #PyErr_Clear as well to ensure it's cleared). */ + /* Printing clears (call #PyErr_Clear as well to ensure it's cleared). */ Py_XINCREF(error_type); Py_XINCREF(error_value); Py_XINCREF(error_traceback); @@ -944,6 +976,11 @@ PyObject *PyC_ExceptionBuffer_Simple(void) PyErr_Fetch(&error_type, &error_value, &error_traceback); + /* Since #PyErr_Print is not called it's not essential that `SystemExit` exceptions are handled. + * Do this to match the behavior of #PyC_ExceptionBuffer since requesting a brief exception + * shouldn't result in completely different behavior. */ + pyc_exception_buffer_handle_system_exit(error_type, error_value, error_traceback); + if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) { /* Special exception for syntax errors, * in these cases the full error is verbose and not very useful, diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index ecb6db2b82c..91ebef8d0b0 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -215,7 +215,6 @@ int PyC_CheckArgs_DeepCopy(PyObject *args); /* Integer parsing (with overflow checks), -1 on error. */ /** - * * Comparison with #PyObject_IsTrue * ================================ * diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index 8ccb29beb13..e9db5c8716b 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -8,12 +8,11 @@ set(INC ../../gpu ../../imbuf ../../makesdna - ../../../../intern/glew-mx ../../../../intern/guardedalloc ) set(INC_SYS - ${GLEW_INCLUDE_PATH} + ${Epoxy_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} ) @@ -59,10 +58,9 @@ set(SRC ) set(LIB + ${Epoxy_LIBRARIES} ${PYTHON_LINKFLAGS} ${PYTHON_LIBRARIES} ) -add_definitions(${GL_DEFINITIONS}) - blender_add_lib(bf_python_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index 533e5154d83..879e1c0ce8b 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -127,7 +127,7 @@ PyDoc_STRVAR(pygpu_batch_vertbuf_add_doc, " vertex buffer for vertex positions and vertex normals.\n" " Current a batch can have at most " STRINGIFY(GPU_BATCH_VBO_MAX_LEN) " vertex buffers.\n" "\n" -" :param buf: The vertex buffer that will be added to the batch.\n" +" :arg buf: The vertex buffer that will be added to the batch.\n" " :type buf: :class:`gpu.types.GPUVertBuf`\n" ); static PyObject *pygpu_batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf) @@ -171,7 +171,7 @@ PyDoc_STRVAR( " This function does not need to be called when you always\n" " set the shader when calling :meth:`gpu.types.GPUBatch.draw`.\n" "\n" - " :param program: The program/shader the batch will use in future draw calls.\n" + " :arg program: The program/shader the batch will use in future draw calls.\n" " :type program: :class:`gpu.types.GPUShader`\n"); static PyObject *pygpu_batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader) { @@ -209,7 +209,7 @@ PyDoc_STRVAR(pygpu_batch_draw_doc, "\n" " Run the drawing program with the parameters assigned to the batch.\n" "\n" - " :param program: Program that performs the drawing operations.\n" + " :arg program: Program that performs the drawing operations.\n" " If ``None`` is passed, the last program set to this batch will run.\n" " :type program: :class:`gpu.types.GPUShader`\n"); static PyObject *pygpu_batch_draw(BPyGPUBatch *self, PyObject *args) diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index 020535d002a..9a415b7f2c4 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -100,7 +100,7 @@ static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj, } else { PyErr_Format(PyExc_TypeError, - "invalid second argument argument expected a sequence " + "invalid second argument expected a sequence " "or an int, not a %.200s", Py_TYPE(shape_obj)->tp_name); } @@ -655,7 +655,7 @@ PyDoc_STRVAR( "\n" " :arg format: Format type to interpret the buffer.\n" " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n" - " :type type: str\n" + " :type format: str\n" " :arg dimensions: Array describing the dimensions.\n" " :type dimensions: int\n" " :arg data: Optional data array.\n" diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c index d52a97c0c84..46f1d4d49eb 100644 --- a/source/blender/python/gpu/gpu_py_element.c +++ b/source/blender/python/gpu/gpu_py_element.c @@ -180,7 +180,7 @@ PyDoc_STRVAR(pygpu_IndexBuf__tp_doc, " :arg type: The primitive type this index buffer is composed of.\n" " Possible values are `POINTS`, `LINES`, `TRIS` and `LINE_STRIP_ADJ`.\n" " :type type: str\n" - " :param seq: Indices this index buffer will contain.\n" + " :arg seq: Indices this index buffer will contain.\n" " Whether a 1D or 2D sequence is required depends on the type.\n" " Optionally the sequence can support the buffer protocol.\n" " :type seq: 1D or 2D sequence\n"); diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c index 33d9ff0b041..2de8c680b23 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.c +++ b/source/blender/python/gpu/gpu_py_framebuffer.c @@ -421,9 +421,10 @@ PyDoc_STRVAR(pygpu_framebuffer_viewport_set_doc, " Set the viewport for this framebuffer object.\n" " Note: The viewport state is not saved upon framebuffer rebind.\n" "\n" - " :param x, y: lower left corner of the viewport_set rectangle, in pixels.\n" - " :param xsize, ysize: width and height of the viewport_set.\n" - " :type x, y, xsize, ysize: int\n"); + " :arg x, y: lower left corner of the viewport_set rectangle, in pixels.\n" + " :type x, y: int\n" + " :arg xsize, ysize: width and height of the viewport_set.\n" + " :type xsize, ysize: int\n"); static PyObject *pygpu_framebuffer_viewport_set(BPyGPUFrameBuffer *self, PyObject *args, void *UNUSED(type)) @@ -462,16 +463,16 @@ PyDoc_STRVAR( "\n" " Read a block of pixels from the frame buffer.\n" "\n" - " :param x, y: Lower left corner of a rectangular block of pixels.\n" - " :param xsize, ysize: Dimensions of the pixel rectangle.\n" + " :arg x, y: Lower left corner of a rectangular block of pixels.\n" + " :arg xsize, ysize: Dimensions of the pixel rectangle.\n" " :type x, y, xsize, ysize: int\n" - " :param channels: Number of components to read.\n" + " :arg channels: Number of components to read.\n" " :type channels: int\n" - " :param slot: The framebuffer slot to read data from.\n" + " :arg slot: The framebuffer slot to read data from.\n" " :type slot: int\n" - " :param format: The format that describes the content of a single channel.\n" + " :arg format: The format that describes the content of a single channel.\n" " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n" - " :type type: str\n" + " :type format: str\n" " :arg data: Optional Buffer object to fill with the pixels values.\n" " :type data: :class:`gpu.types.Buffer`\n" " :return: The Buffer with the read pixels.\n" @@ -569,9 +570,10 @@ PyDoc_STRVAR(pygpu_framebuffer_read_depth_doc, "\n" " Read a pixel depth block from the frame buffer.\n" "\n" - " :param x, y: Lower left corner of a rectangular block of pixels.\n" - " :param xsize, ysize: Dimensions of the pixel rectangle.\n" - " :type x, y, xsize, ysize: int\n" + " :arg x, y: Lower left corner of a rectangular block of pixels.\n" + " :type x, y: int\n" + " :arg xsize, ysize: Dimensions of the pixel rectangle.\n" + " :type xsize, ysize: int\n" " :arg data: Optional Buffer object to fill with the pixels values.\n" " :type data: :class:`gpu.types.Buffer`\n" " :return: The Buffer with the read pixels.\n" @@ -686,7 +688,7 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = { PyDoc_STRVAR(pygpu_framebuffer__tp_doc, ".. class:: GPUFrameBuffer(depth_slot=None, color_slots=None)\n" "\n" - " This object gives access to framebuffer functionallities.\n" + " This object gives access to framebuffer functionalities.\n" " When a 'layer' is specified in a argument, a single layer of a 3D or array " "texture is attached to the frame-buffer.\n" " For cube map textures, layer is translated into a cube map face.\n" diff --git a/source/blender/python/gpu/gpu_py_matrix.c b/source/blender/python/gpu/gpu_py_matrix.c index a47e3dc8a5f..18925a101da 100644 --- a/source/blender/python/gpu/gpu_py_matrix.c +++ b/source/blender/python/gpu/gpu_py_matrix.c @@ -266,7 +266,7 @@ PyDoc_STRVAR(pygpu_matrix_multiply_matrix_doc, "\n" " Multiply the current stack matrix.\n" "\n" - " :param matrix: A 4x4 matrix.\n" + " :arg matrix: A 4x4 matrix.\n" " :type matrix: :class:`mathutils.Matrix`\n"); static PyObject *pygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value) { @@ -283,7 +283,7 @@ PyDoc_STRVAR(pygpu_matrix_scale_doc, "\n" " Scale the current stack matrix.\n" "\n" - " :param scale: Scale the current stack matrix.\n" + " :arg scale: Scale the current stack matrix.\n" " :type scale: sequence of 2 or 3 floats\n"); static PyObject *pygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value) { @@ -305,7 +305,7 @@ static PyObject *pygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value) PyDoc_STRVAR(pygpu_matrix_scale_uniform_doc, ".. function:: scale_uniform(scale)\n" "\n" - " :param scale: Scale the current stack matrix.\n" + " :arg scale: Scale the current stack matrix.\n" " :type scale: float\n"); static PyObject *pygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value) { @@ -323,7 +323,7 @@ PyDoc_STRVAR(pygpu_matrix_translate_doc, "\n" " Scale the current stack matrix.\n" "\n" - " :param offset: Translate the current stack matrix.\n" + " :arg offset: Translate the current stack matrix.\n" " :type offset: sequence of 2 or 3 floats\n"); static PyObject *pygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value) { @@ -373,7 +373,7 @@ PyDoc_STRVAR(pygpu_matrix_load_matrix_doc, "\n" " Load a matrix into the stack.\n" "\n" - " :param matrix: A 4x4 matrix.\n" + " :arg matrix: A 4x4 matrix.\n" " :type matrix: :class:`mathutils.Matrix`\n"); static PyObject *pygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value) { @@ -390,7 +390,7 @@ PyDoc_STRVAR(pygpu_matrix_load_projection_matrix_doc, "\n" " Load a projection matrix into the stack.\n" "\n" - " :param matrix: A 4x4 matrix.\n" + " :arg matrix: A 4x4 matrix.\n" " :type matrix: :class:`mathutils.Matrix`\n"); static PyObject *pygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value) { diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index 656024ae22c..b877e3ceb98 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -55,6 +55,34 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) return PyUnicode_FromString(GPU_platform_version()); } +PyDoc_STRVAR( + pygpu_platform_device_type_get_doc, + ".. function:: device_type_get()\n" + "\n" + " Get GPU device type.\n" + "\n" + " :return: Device type ('APPLE', 'NVIDIA', 'AMD', 'INTEL', 'SOFTWARE', 'UNKNOWN').\n" + " :rtype: str\n"); +static PyObject *pygpu_platform_device_type_get(PyObject *UNUSED(self)) +{ + if (GPU_type_matches(GPU_DEVICE_APPLE, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("APPLE"); + } + if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("NVIDIA"); + } + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("AMD"); + } + if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("INTEL"); + } + if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("SOFTWARE"); + } + return PyUnicode_FromString("UNKNOWN"); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -74,6 +102,10 @@ static struct PyMethodDef pygpu_platform__tp_methods[] = { (PyCFunction)pygpu_platform_version_get, METH_NOARGS, pygpu_platform_version_get_doc}, + {"device_type_get", + (PyCFunction)pygpu_platform_device_type_get, + METH_NOARGS, + pygpu_platform_device_type_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_select.c b/source/blender/python/gpu/gpu_py_select.c index 8869ea38e32..6d11e94433e 100644 --- a/source/blender/python/gpu/gpu_py_select.c +++ b/source/blender/python/gpu/gpu_py_select.c @@ -31,7 +31,7 @@ PyDoc_STRVAR(pygpu_select_load_id_doc, "\n" " Set the selection ID.\n" "\n" - " :param id: Number (32-bit uint).\n" + " :arg id: Number (32-bit uint).\n" " :type select: int\n"); static PyObject *pygpu_select_load_id(PyObject *UNUSED(self), PyObject *value) { diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 101b9f8e4c6..a57b00e671e 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -16,6 +16,7 @@ #include "GPU_uniform_buffer.h" #include "../generic/py_capi_utils.h" +#include "../generic/python_utildefines.h" #include "../mathutils/mathutils.h" #include "gpu_py.h" @@ -30,52 +31,40 @@ * \{ */ #define PYDOC_BUILTIN_SHADER_DESCRIPTION \ - "``2D_FLAT_COLOR``\n" \ + "``FLAT_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ - "``2D_IMAGE``\n" \ + "``IMAGE``\n" \ " :Attributes: vec3 pos, vec2 texCoord\n" \ " :Uniforms: sampler2D image\n" \ - "``2D_SMOOTH_COLOR``\n" \ - " :Attributes: vec3 pos, vec4 color\n" \ - " :Uniforms: none\n" \ - "``2D_UNIFORM_COLOR``\n" \ - " :Attributes: vec3 pos\n" \ - " :Uniforms: vec4 color\n" \ - "``3D_FLAT_COLOR``\n" \ - " :Attributes: vec3 pos, vec4 color\n" \ - " :Uniforms: none\n" \ - "``3D_IMAGE``\n" \ + "``IMAGE_COLOR``\n" \ " :Attributes: vec3 pos, vec2 texCoord\n" \ - " :Uniforms: sampler2D image\n" \ - "``3D_SMOOTH_COLOR``\n" \ + " :Uniforms: sampler2D image, vec4 color\n" \ + "``SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ - "``3D_UNIFORM_COLOR``\n" \ + "``UNIFORM_COLOR``\n" \ " :Attributes: vec3 pos\n" \ " :Uniforms: vec4 color\n" \ - "``3D_POLYLINE_FLAT_COLOR``\n" \ + "``POLYLINE_FLAT_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: vec2 viewportSize, float lineWidth\n" \ - "``3D_POLYLINE_SMOOTH_COLOR``\n" \ + "``POLYLINE_SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: vec2 viewportSize, float lineWidth\n" \ - "``3D_POLYLINE_UNIFORM_COLOR``\n" \ + "``POLYLINE_UNIFORM_COLOR``\n" \ " :Attributes: vec3 pos\n" \ " :Uniforms: vec2 viewportSize, float lineWidth\n" static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { - {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"}, - {GPU_SHADER_2D_IMAGE, "2D_IMAGE"}, - {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, - {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, - {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, - {GPU_SHADER_3D_IMAGE, "3D_IMAGE"}, - {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, - {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, - {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, - {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "3D_POLYLINE_SMOOTH_COLOR"}, - {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"}, + {GPU_SHADER_3D_FLAT_COLOR, "FLAT_COLOR"}, + {GPU_SHADER_3D_IMAGE, "IMAGE"}, + {GPU_SHADER_3D_IMAGE_COLOR, "IMAGE_COLOR"}, + {GPU_SHADER_3D_SMOOTH_COLOR, "SMOOTH_COLOR"}, + {GPU_SHADER_3D_UNIFORM_COLOR, "UNIFORM_COLOR"}, + {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "POLYLINE_FLAT_COLOR"}, + {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "POLYLINE_SMOOTH_COLOR"}, + {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "POLYLINE_UNIFORM_COLOR"}, {0, NULL}, }; @@ -174,7 +163,7 @@ PyDoc_STRVAR(pygpu_shader_uniform_from_name_doc, "\n" " Get uniform location by name.\n" "\n" - " :param name: Name of the uniform variable whose location is to be queried.\n" + " :arg name: Name of the uniform variable whose location is to be queried.\n" " :type name: str\n" " :return: Location of the uniform variable.\n" " :rtype: int\n"); @@ -195,16 +184,15 @@ static PyObject *pygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *ar return PyLong_FromLong(uniform); } -PyDoc_STRVAR( - pygpu_shader_uniform_block_from_name_doc, - ".. method:: uniform_block_from_name(name)\n" - "\n" - " Get uniform block location by name.\n" - "\n" - " :param name: Name of the uniform block variable whose location is to be queried.\n" - " :type name: str\n" - " :return: The location of the uniform block variable.\n" - " :rtype: int\n"); +PyDoc_STRVAR(pygpu_shader_uniform_block_from_name_doc, + ".. method:: uniform_block_from_name(name)\n" + "\n" + " Get uniform block location by name.\n" + "\n" + " :arg name: Name of the uniform block variable whose location is to be queried.\n" + " :type name: str\n" + " :return: The location of the uniform block variable.\n" + " :rtype: int\n"); static PyObject *pygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); @@ -256,11 +244,11 @@ PyDoc_STRVAR(pygpu_shader_uniform_vector_float_doc, "\n" " Set the buffer to fill the uniform.\n" "\n" - " :param location: Location of the uniform variable to be modified.\n" + " :arg location: Location of the uniform variable to be modified.\n" " :type location: int\n" - " :param buffer: The data that should be set. Can support the buffer protocol.\n" + " :arg buffer: The data that should be set. Can support the buffer protocol.\n" " :type buffer: sequence of floats\n" - " :param length: Size of the uniform data type:\n\n" + " :arg length: Size of the uniform data type:\n\n" " - 1: float\n" " - 2: vec2 or float[2]\n" " - 3: vec3 or float[3]\n" @@ -268,7 +256,7 @@ PyDoc_STRVAR(pygpu_shader_uniform_vector_float_doc, " - 9: mat3\n" " - 16: mat4\n" " :type length: int\n" - " :param count: Specifies the number of elements, vector or matrices that are to " + " :arg count: Specifies the number of elements, vector or matrices that are to " "be modified.\n" " :type count: int\n"); static PyObject *pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args) @@ -282,6 +270,7 @@ static PyObject *pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject return NULL; } + GPU_shader_bind(self->shader); GPU_shader_uniform_vector(self->shader, location, length, count, pybuffer.buf); PyBuffer_Release(&pybuffer); @@ -304,6 +293,7 @@ static PyObject *pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *a return NULL; } + GPU_shader_bind(self->shader); GPU_shader_uniform_vector_int(self->shader, location, length, count, pybuffer.buf); PyBuffer_Release(&pybuffer); @@ -312,14 +302,14 @@ static PyObject *pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *a } PyDoc_STRVAR(pygpu_shader_uniform_bool_doc, - ".. method:: uniform_bool(name, seq)\n" + ".. method:: uniform_bool(name, value)\n" "\n" " Specify the value of a uniform variable for the current program object.\n" "\n" - " :param name: Name of the uniform variable whose value is to be changed.\n" + " :arg name: Name of the uniform variable whose value is to be changed.\n" " :type name: str\n" - " :param seq: Value that will be used to update the specified uniform variable.\n" - " :type seq: sequence of bools\n"); + " :arg value: Value that will be used to update the specified uniform variable.\n" + " :type value: bool or sequence of bools\n"); static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) { const char *error_prefix = "GPUShader.uniform_bool"; @@ -335,15 +325,14 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) int values[4]; int length; - int ret; - { + int ret = -1; + if (PySequence_Check(params.seq)) { PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix); if (seq_fast == NULL) { PyErr_Format(PyExc_TypeError, "%s: expected a sequence, got %s", error_prefix, Py_TYPE(params.seq)->tp_name); - ret = -1; } else { length = PySequence_Fast_GET_SIZE(seq_fast); @@ -352,7 +341,6 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) "%s: invalid sequence length. expected 1..4, got %d", error_prefix, length); - ret = -1; } else { ret = PyC_AsArray_FAST( @@ -361,6 +349,15 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) Py_DECREF(seq_fast); } } + else if (((values[0] = (int)PyLong_AsLong(params.seq)) != -1) && ELEM(values[0], 0, 1)) { + length = 1; + ret = 0; + } + else { + PyErr_Format( + PyExc_ValueError, "expected a bool or sequence, got %s", Py_TYPE(params.seq)->tp_name); + } + if (ret == -1) { return NULL; } @@ -371,6 +368,7 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) return NULL; } + GPU_shader_bind(self->shader); GPU_shader_uniform_vector_int(self->shader, location, length, 1, values); Py_RETURN_NONE; @@ -381,9 +379,9 @@ PyDoc_STRVAR(pygpu_shader_uniform_float_doc, "\n" " Specify the value of a uniform variable for the current program object.\n" "\n" - " :param name: Name of the uniform variable whose value is to be changed.\n" + " :arg name: Name of the uniform variable whose value is to be changed.\n" " :type name: str\n" - " :param value: Value that will be used to update the specified uniform variable.\n" + " :arg value: Value that will be used to update the specified uniform variable.\n" " :type value: single number or sequence of numbers\n"); static PyObject *pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args) { @@ -440,6 +438,7 @@ static PyObject *pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args) return NULL; } + GPU_shader_bind(self->shader); GPU_shader_uniform_vector(self->shader, location, length, 1, values); Py_RETURN_NONE; @@ -450,9 +449,9 @@ PyDoc_STRVAR(pygpu_shader_uniform_int_doc, "\n" " Specify the value of a uniform variable for the current program object.\n" "\n" - " :param name: name of the uniform variable whose value is to be changed.\n" + " :arg name: name of the uniform variable whose value is to be changed.\n" " :type name: str\n" - " :param seq: Value that will be used to update the specified uniform variable.\n" + " :arg seq: Value that will be used to update the specified uniform variable.\n" " :type seq: sequence of numbers\n"); static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) { @@ -511,6 +510,7 @@ static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) return NULL; } + GPU_shader_bind(self->shader); GPU_shader_uniform_vector_int(self->shader, location, length, 1, values); Py_RETURN_NONE; @@ -521,9 +521,9 @@ PyDoc_STRVAR(pygpu_shader_uniform_sampler_doc, "\n" " Specify the value of a texture uniform variable for the current GPUShader.\n" "\n" - " :param name: name of the uniform variable whose texture is to be specified.\n" + " :arg name: name of the uniform variable whose texture is to be specified.\n" " :type name: str\n" - " :param texture: Texture to attach.\n" + " :arg texture: Texture to attach.\n" " :type texture: :class:`gpu.types.GPUTexture`\n"); static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args) { @@ -534,6 +534,7 @@ static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args return NULL; } + GPU_shader_bind(self->shader); int slot = GPU_shader_get_texture_binding(self->shader, name); GPU_texture_bind(py_texture->tex, slot); GPU_shader_uniform_1i(self->shader, name, slot); @@ -547,9 +548,9 @@ PyDoc_STRVAR( "\n" " Specify the value of an uniform buffer object variable for the current GPUShader.\n" "\n" - " :param name: name of the uniform variable whose UBO is to be specified.\n" + " :arg name: name of the uniform variable whose UBO is to be specified.\n" " :type name: str\n" - " :param ubo: Uniform Buffer to attach.\n" + " :arg ubo: Uniform Buffer to attach.\n" " :type texture: :class:`gpu.types.GPUUniformBuf`\n"); static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args) { @@ -568,21 +569,21 @@ static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args) return NULL; } + GPU_shader_bind(self->shader); GPU_uniformbuf_bind(py_ubo->ubo, binding); Py_RETURN_NONE; } -PyDoc_STRVAR( - pygpu_shader_attr_from_name_doc, - ".. method:: attr_from_name(name)\n" - "\n" - " Get attribute location by name.\n" - "\n" - " :param name: The name of the attribute variable whose location is to be queried.\n" - " :type name: str\n" - " :return: The location of an attribute variable.\n" - " :rtype: int\n"); +PyDoc_STRVAR(pygpu_shader_attr_from_name_doc, + ".. method:: attr_from_name(name)\n" + "\n" + " Get attribute location by name.\n" + "\n" + " :arg name: The name of the attribute variable whose location is to be queried.\n" + " :type name: str\n" + " :return: The location of an attribute variable.\n" + " :rtype: int\n"); static PyObject *pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) { const char *name = PyUnicode_AsUTF8(arg); @@ -600,20 +601,58 @@ static PyObject *pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) return PyLong_FromLong(attr); } -PyDoc_STRVAR(pygpu_shader_calc_format_doc, - ".. method:: calc_format()\n" +PyDoc_STRVAR(pygpu_shader_format_calc_doc, + ".. method:: format_calc()\n" "\n" " Build a new format based on the attributes of the shader.\n" "\n" " :return: vertex attribute format for the shader\n" " :rtype: :class:`gpu.types.GPUVertFormat`\n"); -static PyObject *pygpu_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg)) +static PyObject *pygpu_shader_format_calc(BPyGPUShader *self, PyObject *UNUSED(arg)) { BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL); GPU_vertformat_from_shader(&ret->fmt, self->shader); return (PyObject *)ret; } +PyDoc_STRVAR( + pygpu_shader_attrs_info_get_doc, + ".. method:: attrs_info_get()\n" + "\n" + " Information about the attributes used in the Shader.\n" + "\n" + " :return: tuples containing information about the attributes in order (name, type)\n" + " :rtype: tuple\n"); +static PyObject *pygpu_shader_attrs_info_get(BPyGPUShader *self, PyObject *UNUSED(arg)) +{ + uint attr_len = GPU_shader_get_attribute_len(self->shader); + int location_test = 0, attrs_added = 0; + ; + PyObject *ret = PyTuple_New(attr_len); + while (attrs_added < attr_len) { + char name[256]; + int type; + if (!GPU_shader_get_attribute_info(self->shader, location_test++, name, &type)) { + continue; + } + PyObject *py_type; + if (type != -1) { + py_type = PyUnicode_InternFromString( + PyC_StringEnum_FindIDFromValue(pygpu_attrtype_items, type)); + } + else { + py_type = Py_None; + Py_INCREF(py_type); + } + + PyObject *attr_info = PyTuple_New(2); + PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type); + PyTuple_SetItem(ret, attrs_added, attr_info); + attrs_added++; + } + return ret; +} + static struct PyMethodDef pygpu_shader__tp_methods[] = { {"bind", (PyCFunction)pygpu_shader_bind, METH_NOARGS, pygpu_shader_bind_doc}, {"uniform_from_name", @@ -657,9 +696,13 @@ static struct PyMethodDef pygpu_shader__tp_methods[] = { METH_O, pygpu_shader_attr_from_name_doc}, {"format_calc", - (PyCFunction)pygpu_shader_calc_format, + (PyCFunction)pygpu_shader_format_calc, METH_NOARGS, - pygpu_shader_calc_format_doc}, + pygpu_shader_format_calc_doc}, + {"attrs_info_get", + (PyCFunction)pygpu_shader_attrs_info_get, + METH_NOARGS, + pygpu_shader_attrs_info_get_doc}, {NULL, NULL, 0, NULL}, }; @@ -712,17 +755,17 @@ PyDoc_STRVAR( " ``fragOutput = blender_srgb_to_framebuffer_space(fragOutput)``\n" " to transform the output sRGB colors to the frame-buffer color-space.\n" "\n" - " :param vertexcode: Vertex shader code.\n" + " :arg vertexcode: Vertex shader code.\n" " :type vertexcode: str\n" - " :param fragcode: Fragment shader code.\n" + " :arg fragcode: Fragment shader code.\n" " :type value: str\n" - " :param geocode: Geometry shader code.\n" + " :arg geocode: Geometry shader code.\n" " :type value: str\n" - " :param libcode: Code with functions and presets to be shared between shaders.\n" + " :arg libcode: Code with functions and presets to be shared between shaders.\n" " :type value: str\n" - " :param defines: Preprocessor directives.\n" + " :arg defines: Preprocessor directives.\n" " :type value: str\n" - " :param name: Name of shader code, for debugging purposes.\n" + " :arg name: Name of shader code, for debugging purposes.\n" " :type value: str\n"); PyTypeObject BPyGPUShader_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUShader", @@ -741,6 +784,24 @@ PyTypeObject BPyGPUShader_Type = { /** \name gpu.shader Module API * \{ */ +static int pyc_parse_buitinshader_w_backward_compatibility(PyObject *o, void *p) +{ + struct PyC_StringEnum *e = p; + const char *value = PyUnicode_AsUTF8(o); + if (value && ELEM(value[0], u'2', u'3')) { + /* Deprecated enums that start with "3D_" or "2D_". */ + value += 3; + for (int i = 0; e->items[i].id; i++) { + if (STREQ(e->items[i].id, value)) { + e->value_found = e->items[i].value; + return 1; + } + } + } + + return PyC_ParseStringEnum(o, p); +} + PyDoc_STRVAR(pygpu_shader_unbind_doc, ".. function:: unbind()\n" "\n" @@ -763,9 +824,9 @@ PyDoc_STRVAR( "``CLIPPED`` value to the config parameter. Note that in this case you also need to " "manually set the value of ``mat4 ModelMatrix``.\n" "\n" - " :param shader_name: One of the builtin shader names.\n" + " :arg shader_name: One of the builtin shader names.\n" " :type shader_name: str\n" - " :param config: One of these types of shader configuration:\n" + " :arg config: One of these types of shader configuration:\n" "\n" " - ``DEFAULT``\n" " - ``CLIPPED``\n" @@ -791,7 +852,7 @@ static PyObject *pygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, - PyC_ParseStringEnum, + pyc_parse_buitinshader_w_backward_compatibility, &pygpu_bultinshader, PyC_ParseStringEnum, &pygpu_config)) { @@ -809,7 +870,7 @@ PyDoc_STRVAR(pygpu_shader_create_from_info_doc, "\n" " Create shader from a GPUShaderCreateInfo.\n" "\n" - " :param shader_info: GPUShaderCreateInfo\n" + " :arg shader_info: GPUShaderCreateInfo\n" " :type shader_info: :class:`bpy.types.GPUShaderCreateInfo`\n" " :return: Shader object corresponding to the given name.\n" " :rtype: :class:`bpy.types.GPUShader`\n"); diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h index b5944c4b3a0..ba40636981f 100644 --- a/source/blender/python/gpu/gpu_py_shader.h +++ b/source/blender/python/gpu/gpu_py_shader.h @@ -6,6 +6,10 @@ #pragma once +#ifndef __cplusplus +# include "../generic/py_capi_utils.h" +#endif + /* Make sure that there is always a reference count for PyObjects of type String as the strings are * passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */ #define USE_GPU_PY_REFERENCES @@ -31,6 +35,7 @@ extern "C" { /* gpu_py_shader_create_info.cc */ +extern const struct PyC_StringEnumItems pygpu_attrtype_items[]; extern PyTypeObject BPyGPUShaderCreateInfo_Type; extern PyTypeObject BPyGPUStageInterfaceInfo_Type; diff --git a/source/blender/python/gpu/gpu_py_shader_create_info.cc b/source/blender/python/gpu/gpu_py_shader_create_info.cc index e00d01174f4..f35c5a01145 100644 --- a/source/blender/python/gpu/gpu_py_shader_create_info.cc +++ b/source/blender/python/gpu/gpu_py_shader_create_info.cc @@ -58,7 +58,7 @@ static const struct PyC_FlagSet pygpu_qualifiers[] = { " - ``IVEC3``\n" \ " - ``IVEC4``\n" \ " - ``BOOL``\n" -static const struct PyC_StringEnumItems pygpu_attrtype_items[] = { +const struct PyC_StringEnumItems pygpu_attrtype_items[] = { {(int)Type::FLOAT, "FLOAT"}, {(int)Type::VEC2, "VEC2"}, {(int)Type::VEC3, "VEC3"}, @@ -189,11 +189,11 @@ PyDoc_STRVAR(pygpu_interface_info_smooth_doc, "\n" " Add an attribute with qualifier of type `smooth` to the interface block.\n" "\n" - " :param type: One of these types:\n" + " :arg type: One of these types:\n" "\n" PYDOC_TYPE_LIST "\n" " :type type: str\n" - " :param name: name of the attribute.\n" + " :arg name: name of the attribute.\n" " :type name: str\n"); static PyObject *pygpu_interface_info_smooth(BPyGPUStageInterfaceInfo *self, PyObject *args) { @@ -213,11 +213,11 @@ PyDoc_STRVAR(pygpu_interface_info_flat_doc, "\n" " Add an attribute with qualifier of type `flat` to the interface block.\n" "\n" - " :param type: One of these types:\n" + " :arg type: One of these types:\n" "\n" PYDOC_TYPE_LIST "\n" " :type type: str\n" - " :param name: name of the attribute.\n" + " :arg name: name of the attribute.\n" " :type name: str\n"); static PyObject *pygpu_interface_info_flat(BPyGPUStageInterfaceInfo *self, PyObject *args) { @@ -238,11 +238,11 @@ PyDoc_STRVAR( "\n" " Add an attribute with qualifier of type `no_perspective` to the interface block.\n" "\n" - " :param type: One of these types:\n" + " :arg type: One of these types:\n" "\n" PYDOC_TYPE_LIST "\n" " :type type: str\n" - " :param name: name of the attribute.\n" + " :arg name: name of the attribute.\n" " :type name: str\n"); static PyObject *pygpu_interface_info_no_perspective(BPyGPUStageInterfaceInfo *self, PyObject *args) @@ -370,7 +370,7 @@ PyDoc_STRVAR(pygpu_interface_info__tp_doc, "\n" " List of varyings between shader stages.\n\n" "\n" - " :param name: Name of the interface block.\n" + " :arg name: Name of the interface block.\n" " :type value: str\n"); constexpr PyTypeObject pygpu_interface_info_type() { @@ -403,13 +403,13 @@ PyDoc_STRVAR(pygpu_shader_info_vertex_in_doc, "\n" " Add a vertex shader input attribute.\n" "\n" - " :param slot: The attribute index.\n" + " :arg slot: The attribute index.\n" " :type slot: int\n" - " :param type: One of these types:\n" + " :arg type: One of these types:\n" "\n" PYDOC_TYPE_LIST "\n" " :type type: str\n" - " :param name: name of the attribute.\n" + " :arg name: name of the attribute.\n" " :type name: str\n"); static PyObject *pygpu_shader_info_vertex_in(BPyGPUShaderCreateInfo *self, PyObject *args) { @@ -436,7 +436,7 @@ PyDoc_STRVAR(pygpu_shader_info_vertex_out_doc, "\n" " Add a vertex shader output interface block.\n" "\n" - " :param interface: Object describing the block.\n" + " :arg interface: Object describing the block.\n" " :type interface: :class:`gpu.types.GPUStageInterfaceInfo`\n"); static PyObject *pygpu_shader_info_vertex_out(BPyGPUShaderCreateInfo *self, BPyGPUStageInterfaceInfo *o) @@ -462,15 +462,15 @@ PyDoc_STRVAR(pygpu_shader_info_fragment_out_doc, "\n" " Specify a fragment output corresponding to a framebuffer target slot.\n" "\n" - " :param slot: The attribute index.\n" + " :arg slot: The attribute index.\n" " :type slot: int\n" - " :param type: One of these types:\n" + " :arg type: One of these types:\n" "\n" PYDOC_TYPE_LIST "\n" " :type type: str\n" - " :param name: Name of the attribute.\n" + " :arg name: Name of the attribute.\n" " :type name: str\n" - " :param blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n" + " :arg blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n" " :type blend: str\n"); static PyObject *pygpu_shader_info_fragment_out(BPyGPUShaderCreateInfo *self, PyObject *args, @@ -521,12 +521,12 @@ PyDoc_STRVAR( "\n" " Specify a uniform variable whose type can be one of those declared in `typedef_source`.\n" "\n" - " :param slot: The uniform variable index.\n" + " :arg slot: The uniform variable index.\n" " :type slot: int\n" - " :param type_name: Name of the data type. It can be a struct type defined in the source " + " :arg type_name: Name of the data type. It can be a struct type defined in the source " "passed through the :meth:`gpu.types.GPUShaderCreateInfo.typedef_source`.\n" " :type type_name: str\n" - " :param name: The uniform variable name.\n" + " :arg name: The uniform variable name.\n" " :type name: str\n"); static PyObject *pygpu_shader_info_uniform_buf(BPyGPUShaderCreateInfo *self, PyObject *args) { @@ -556,19 +556,19 @@ PyDoc_STRVAR( "\n" " Specify an image resource used for arbitrary load and store operations.\n" "\n" - " :param slot: The image resource index.\n" + " :arg slot: The image resource index.\n" " :type slot: int\n" - " :param format: The GPUTexture format that is passed to the shader. Possible values are:\n" + " :arg format: The GPUTexture format that is passed to the shader. Possible values are:\n" "" PYDOC_TEX_FORMAT_ITEMS " :type format: str\n" - " :param type: The data type describing how the image is to be read in the shader. " + " :arg type: The data type describing how the image is to be read in the shader. " "Possible values are:\n" "\n" PYDOC_IMAGE_TYPES "\n" " :type type: str\n" - " :param name: The image resource name.\n" + " :arg name: The image resource name.\n" " :type name: str\n" - " :param qualifiers: Set containing values that describe how the image resource is to be " + " :arg qualifiers: Set containing values that describe how the image resource is to be " "read or written. Possible values are:\n" "" PYDOC_QUALIFIERS "" @@ -636,14 +636,14 @@ PyDoc_STRVAR( "\n" " Specify an image texture sampler.\n" "\n" - " :param slot: The image texture sampler index.\n" + " :arg slot: The image texture sampler index.\n" " :type slot: int\n" - " :param type: The data type describing the format of each sampler unit. Possible values " + " :arg type: The data type describing the format of each sampler unit. Possible values " "are:\n" "\n" PYDOC_IMAGE_TYPES "\n" " :type type: str\n" - " :param name: The image texture sampler name.\n" + " :arg name: The image texture sampler name.\n" " :type name: str\n"); static PyObject *pygpu_shader_info_sampler(BPyGPUShaderCreateInfo *self, PyObject *args) { @@ -673,6 +673,9 @@ static int constant_type_size(Type type) case Type::FLOAT: case Type::INT: case Type::UINT: + case Type::UCHAR4: + case Type::CHAR4: + case blender::gpu::shader::Type::VEC3_101010I2: return 4; break; case Type::VEC2: @@ -695,6 +698,18 @@ static int constant_type_size(Type type) case Type::MAT4: return 64; break; + case blender::gpu::shader::Type::UCHAR: + case blender::gpu::shader::Type::CHAR: + return 1; + break; + case blender::gpu::shader::Type::UCHAR2: + case blender::gpu::shader::Type::CHAR2: + return 2; + break; + case blender::gpu::shader::Type::UCHAR3: + case blender::gpu::shader::Type::CHAR3: + return 3; + break; } BLI_assert(false); return -1; @@ -733,13 +748,13 @@ PyDoc_STRVAR(pygpu_shader_info_push_constant_doc, "\n" " Specify a global access constant.\n" "\n" - " :param type: One of these types:\n" + " :arg type: One of these types:\n" "\n" PYDOC_TYPE_LIST "\n" " :type type: str\n" - " :param name: Name of the constant.\n" + " :arg name: Name of the constant.\n" " :type name: str\n" - " :param size: If not zero, indicates that the constant is an array with the " + " :arg size: If not zero, indicates that the constant is an array with the " "specified size.\n" " :type size: uint\n"); static PyObject *pygpu_shader_info_push_constant(BPyGPUShaderCreateInfo *self, @@ -797,7 +812,7 @@ PyDoc_STRVAR( "\n" " \"void main {gl_Position = vec4(pos, 1.0);}\"\n" "\n" - " :param source: The vertex shader source code.\n" + " :arg source: The vertex shader source code.\n" " :type source: str\n" "\n" " .. seealso:: `GLSL Cross Compilation " @@ -838,7 +853,7 @@ PyDoc_STRVAR( "\n" " \"void main {fragColor = vec4(0.0, 0.0, 0.0, 1.0);}\"\n" "\n" - " :param source: The fragment shader source code.\n" + " :arg source: The fragment shader source code.\n" " :type source: str\n" "\n" " .. seealso:: `GLSL Cross Compilation " @@ -879,7 +894,7 @@ PyDoc_STRVAR(pygpu_shader_info_typedef_source_doc, "\n" " \"struct MyType {int foo; float bar;};\"\n" "\n" - " :param source: The source code defining types.\n" + " :arg source: The source code defining types.\n" " :type source: str\n"); static PyObject *pygpu_shader_info_typedef_source(BPyGPUShaderCreateInfo *self, PyObject *o) { @@ -918,9 +933,9 @@ PyDoc_STRVAR(pygpu_shader_info_define_doc, "\n" " #define name value\n" "\n" - " :param name: Token name.\n" + " :arg name: Token name.\n" " :type name: str\n" - " :param value: Text that replaces token occurrences.\n" + " :arg value: Text that replaces token occurrences.\n" " :type value: str\n"); static PyObject *pygpu_shader_info_define(BPyGPUShaderCreateInfo *self, PyObject *args) { diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index fb69bb316c4..35ecc2aff40 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -72,7 +72,7 @@ PyDoc_STRVAR( "\n" " Defines the fixed pipeline blending equation.\n" "\n" - " :param mode: The type of blend mode.\n" + " :arg mode: The type of blend mode.\n" " * ``NONE`` No blending.\n" " * ``ALPHA`` The original color channels are interpolated according to the alpha " "value.\n" @@ -114,7 +114,7 @@ PyDoc_STRVAR(pygpu_state_clip_distances_set_doc, "\n" " Sets the number of `gl_ClipDistance` planes used for clip geometry.\n" "\n" - " :param distances_enabled: Number of clip distances enabled.\n" + " :arg distances_enabled: Number of clip distances enabled.\n" " :type distances_enabled: int\n"); static PyObject *pygpu_state_clip_distances_set(PyObject *UNUSED(self), PyObject *value) { @@ -136,7 +136,7 @@ PyDoc_STRVAR(pygpu_state_depth_test_set_doc, "\n" " Defines the depth_test equation.\n" "\n" - " :param mode: The depth test equation name.\n" + " :arg mode: The depth test equation name.\n" " Possible values are `NONE`, `ALWAYS`, `LESS`, `LESS_EQUAL`, `EQUAL`, " "`GREATER` and `GREATER_EQUAL`.\n" " :type mode: str\n"); @@ -166,7 +166,7 @@ PyDoc_STRVAR(pygpu_state_depth_mask_set_doc, "\n" " Write to depth component.\n" "\n" - " :param value: True for writing to the depth component.\n" + " :arg value: True for writing to the depth component.\n" " :type near: bool\n"); static PyObject *pygpu_state_depth_mask_set(PyObject *UNUSED(self), PyObject *value) { @@ -193,9 +193,10 @@ PyDoc_STRVAR(pygpu_state_viewport_set_doc, " Specifies the viewport of the active framebuffer.\n" " Note: The viewport state is not saved upon framebuffer rebind.\n" "\n" - " :param x, y: lower left corner of the viewport_set rectangle, in pixels.\n" - " :param width, height: width and height of the viewport_set.\n" - " :type x, y, xsize, ysize: int\n"); + " :arg x, y: lower left corner of the viewport_set rectangle, in pixels.\n" + " :type x, y: int\n" + " :arg xsize, ysize: width and height of the viewport_set.\n" + " :type xsize, ysize: int\n"); static PyObject *pygpu_state_viewport_set(PyObject *UNUSED(self), PyObject *args) { int x, y, xsize, ysize; @@ -230,7 +231,7 @@ PyDoc_STRVAR(pygpu_state_line_width_set_doc, "\n" " Specify the width of rasterized lines.\n" "\n" - " :param size: New width.\n" + " :arg size: New width.\n" " :type mode: float\n"); static PyObject *pygpu_state_line_width_set(PyObject *UNUSED(self), PyObject *value) { @@ -258,7 +259,7 @@ PyDoc_STRVAR(pygpu_state_point_size_set_doc, "\n" " Specify the diameter of rasterized points.\n" "\n" - " :param size: New diameter.\n" + " :arg size: New diameter.\n" " :type mode: float\n"); static PyObject *pygpu_state_point_size_set(PyObject *UNUSED(self), PyObject *value) { @@ -276,7 +277,7 @@ PyDoc_STRVAR(pygpu_state_color_mask_set_doc, "\n" " Enable or disable writing of frame buffer color components.\n" "\n" - " :param r, g, b, a: components red, green, blue, and alpha.\n" + " :arg r, g, b, a: components red, green, blue, and alpha.\n" " :type r, g, b, a: bool\n"); static PyObject *pygpu_state_color_mask_set(PyObject *UNUSED(self), PyObject *args) { @@ -294,7 +295,7 @@ PyDoc_STRVAR(pygpu_state_face_culling_set_doc, "\n" " Specify whether none, front-facing or back-facing facets can be culled.\n" "\n" - " :param mode: `NONE`, `FRONT` or `BACK`.\n" + " :arg mode: `NONE`, `FRONT` or `BACK`.\n" " :type mode: str\n"); static PyObject *pygpu_state_face_culling_set(PyObject *UNUSED(self), PyObject *value) { @@ -312,7 +313,7 @@ PyDoc_STRVAR(pygpu_state_front_facing_set_doc, "\n" " Specifies the orientation of front-facing polygons.\n" "\n" - " :param invert: True for clockwise polygons as front-facing.\n" + " :arg invert: True for clockwise polygons as front-facing.\n" " :type mode: bool\n"); static PyObject *pygpu_state_front_facing_set(PyObject *UNUSED(self), PyObject *value) { @@ -331,7 +332,7 @@ PyDoc_STRVAR(pygpu_state_program_point_size_set_doc, " If enabled, the derived point size is taken from the (potentially clipped) " "shader builtin gl_PointSize.\n" "\n" - " :param enable: True for shader builtin gl_PointSize.\n" + " :arg enable: True for shader builtin gl_PointSize.\n" " :type enable: bool\n"); static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyObject *value) { diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index 7a23f7ac91f..ae004341304 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -280,9 +280,9 @@ PyDoc_STRVAR( "\n" " Fill texture with specific value.\n" "\n" - " :param format: The format that describes the content of a single item.\n" + " :arg format: The format that describes the content of a single item.\n" " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n" - " :type type: str\n" + " :type format: str\n" " :arg value: sequence each representing the value to fill.\n" " :type value: sequence of 1, 2, 3 or 4 values\n"); static PyObject *pygpu_texture_clear(BPyGPUTexture *self, PyObject *args, PyObject *kwds) diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index a295bedeae2..fd36c0a2d71 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -261,9 +261,9 @@ PyDoc_STRVAR(pygpu_vertbuf_attr_fill_doc, "\n" " Insert data into the buffer for a single attribute.\n" "\n" - " :param id: Either the name or the id of the attribute.\n" + " :arg id: Either the name or the id of the attribute.\n" " :type id: int or str\n" - " :param data: Sequence of data that should be stored in the buffer\n" + " :arg data: Sequence of data that should be stored in the buffer\n" " :type data: sequence of floats, ints, vectors or matrices\n"); static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds) { @@ -292,7 +292,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py const char *name = PyUnicode_AsUTF8(identifier); id = GPU_vertformat_attr_id_get(format, name); if (id == -1) { - PyErr_SetString(PyExc_ValueError, "Unknown attribute name"); + PyErr_Format(PyExc_ValueError, "Unknown attribute '%s'", name); return NULL; } } @@ -323,14 +323,14 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self) } PyDoc_STRVAR(pygpu_vertbuf__tp_doc, - ".. class:: GPUVertBuf(len, format)\n" + ".. class:: GPUVertBuf(format, len)\n" "\n" " Contains a VBO.\n" "\n" - " :param len: Amount of vertices that will fit into this buffer.\n" - " :type type: `int`\n" - " :param format: Vertex format.\n" - " :type buf: :class:`gpu.types.GPUVertFormat`\n"); + " :arg format: Vertex format.\n" + " :type format: :class:`gpu.types.GPUVertFormat`\n" + " :arg len: Amount of vertices that will fit into this buffer.\n" + " :type len: int\n"); PyTypeObject BPyGPUVertBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertBuf", .tp_basicsize = sizeof(BPyGPUVertBuf), diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c index 40a0e5d1e9f..ac0ec6bdc01 100644 --- a/source/blender/python/gpu/gpu_py_vertex_format.c +++ b/source/blender/python/gpu/gpu_py_vertex_format.c @@ -63,15 +63,15 @@ PyDoc_STRVAR( "\n" " Add a new attribute to the format.\n" "\n" - " :param id: Name the attribute. Often `position`, `normal`, ...\n" + " :arg id: Name the attribute. Often `position`, `normal`, ...\n" " :type id: str\n" - " :param comp_type: The data type that will be used store the value in memory.\n" + " :arg comp_type: The data type that will be used store the value in memory.\n" " Possible values are `I8`, `U8`, `I16`, `U16`, `I32`, `U32`, `F32` and `I10`.\n" " :type comp_type: str\n" - " :param len: How many individual values the attribute consists of\n" + " :arg len: How many individual values the attribute consists of\n" " (e.g. 2 for uv coordinates).\n" " :type len: int\n" - " :param fetch_mode: How values from memory will be converted when used in the shader.\n" + " :arg fetch_mode: How values from memory will be converted when used in the shader.\n" " This is mainly useful for memory optimizations when you want to store values with\n" " reduced precision. E.g. you can store a float in only 1 byte but it will be\n" " converted to a normal 4 byte float when used.\n" diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 71138134370..9d2516969cf 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -174,8 +174,8 @@ if(WITH_CODEC_SNDFILE) add_definitions(-DWITH_SNDFILE) endif() -if(WITH_COMPOSITOR) - add_definitions(-DWITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_CYCLES) diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 2e97ae0fc1d..3b3ffb321c3 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -32,6 +32,7 @@ #include "bpy.h" #include "bpy_app.h" #include "bpy_capi_utils.h" +#include "bpy_driver.h" #include "bpy_library.h" #include "bpy_operator.h" #include "bpy_props.h" @@ -326,6 +327,49 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj return PyC_UnicodeFromByte(path ? path : ""); } +/* This is only exposed for tests, see: `tests/python/bl_pyapi_bpy_driver_secure_eval.py`. */ +PyDoc_STRVAR(bpy_driver_secure_code_test_doc, + ".. function:: _driver_secure_code_test(code)\n" + "\n" + " Test if the script should be considered trusted.\n" + "\n" + " :arg code: The code to test.\n" + " :type code: code\n" + " :arg namespace: The namespace of values which are allowed.\n" + " :type namespace: dict\n" + " :arg verbose: Print the reason for considering insecure to the ``stderr``.\n" + " :type verbose: bool\n" + " :return: True when the script is considered trusted.\n" + " :rtype: bool\n"); +static PyObject *bpy_driver_secure_code_test(PyObject *UNUSED(self), PyObject *args, PyObject *kw) +{ + PyObject *py_code; + PyObject *py_namespace = NULL; + const bool verbose = false; + static const char *_keywords[] = {"code", "namespace", "verbose", NULL}; + static _PyArg_Parser _parser = { + "O!" /* `expression` */ + "|$" /* Optional keyword only arguments. */ + "O!" /* `namespace` */ + "O&" /* `verbose` */ + ":driver_secure_code_test", + _keywords, + 0, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &PyCode_Type, + &py_code, + &PyDict_Type, + &py_namespace, + PyC_ParseBool, + &verbose)) { + return NULL; + } + return PyBool_FromLong(BPY_driver_secure_bytecode_test(py_code, py_namespace, verbose)); +} + PyDoc_STRVAR(bpy_escape_identifier_doc, ".. function:: escape_identifier(string)\n" "\n" @@ -492,65 +536,37 @@ static PyObject *bpy_rna_enum_items_static(PyObject *UNUSED(self)) return result; } -static PyMethodDef meth_bpy_script_paths = { - "script_paths", - (PyCFunction)bpy_script_paths, - METH_NOARGS, - bpy_script_paths_doc, -}; -static PyMethodDef meth_bpy_blend_paths = { - "blend_paths", - (PyCFunction)bpy_blend_paths, - METH_VARARGS | METH_KEYWORDS, - bpy_blend_paths_doc, -}; -static PyMethodDef meth_bpy_flip_name = { - "flip_name", - (PyCFunction)bpy_flip_name, - METH_VARARGS | METH_KEYWORDS, - bpy_flip_name_doc, -}; -static PyMethodDef meth_bpy_user_resource = { - "user_resource", - (PyCFunction)bpy_user_resource, - METH_VARARGS | METH_KEYWORDS, - NULL, -}; -static PyMethodDef meth_bpy_system_resource = { - "system_resource", - (PyCFunction)bpy_system_resource, - METH_VARARGS | METH_KEYWORDS, - bpy_system_resource_doc, -}; -static PyMethodDef meth_bpy_resource_path = { - "resource_path", - (PyCFunction)bpy_resource_path, - METH_VARARGS | METH_KEYWORDS, - bpy_resource_path_doc, -}; -static PyMethodDef meth_bpy_escape_identifier = { - "escape_identifier", - (PyCFunction)bpy_escape_identifier, - METH_O, - bpy_escape_identifier_doc, -}; -static PyMethodDef meth_bpy_unescape_identifier = { - "unescape_identifier", - (PyCFunction)bpy_unescape_identifier, - METH_O, - bpy_unescape_identifier_doc, -}; -static PyMethodDef meth_bpy_context_members = { - "context_members", - (PyCFunction)bpy_context_members, - METH_NOARGS, - bpy_context_members_doc, -}; -static PyMethodDef meth_bpy_rna_enum_items_static = { - "rna_enum_items_static", - (PyCFunction)bpy_rna_enum_items_static, - METH_NOARGS, - bpy_rna_enum_items_static_doc, +static PyMethodDef bpy_methods[] = { + {"script_paths", (PyCFunction)bpy_script_paths, METH_NOARGS, bpy_script_paths_doc}, + {"blend_paths", + (PyCFunction)bpy_blend_paths, + METH_VARARGS | METH_KEYWORDS, + bpy_blend_paths_doc}, + {"flip_name", (PyCFunction)bpy_flip_name, METH_VARARGS | METH_KEYWORDS, bpy_flip_name_doc}, + {"user_resource", (PyCFunction)bpy_user_resource, METH_VARARGS | METH_KEYWORDS, NULL}, + {"system_resource", + (PyCFunction)bpy_system_resource, + METH_VARARGS | METH_KEYWORDS, + bpy_system_resource_doc}, + {"resource_path", + (PyCFunction)bpy_resource_path, + METH_VARARGS | METH_KEYWORDS, + bpy_resource_path_doc}, + {"_driver_secure_code_test", + (PyCFunction)bpy_driver_secure_code_test, + METH_VARARGS | METH_KEYWORDS, + bpy_driver_secure_code_test_doc}, + {"escape_identifier", (PyCFunction)bpy_escape_identifier, METH_O, bpy_escape_identifier_doc}, + {"unescape_identifier", + (PyCFunction)bpy_unescape_identifier, + METH_O, + bpy_unescape_identifier_doc}, + {"context_members", (PyCFunction)bpy_context_members, METH_NOARGS, bpy_context_members_doc}, + {"rna_enum_items_static", + (PyCFunction)bpy_rna_enum_items_static, + METH_NOARGS, + bpy_rna_enum_items_static_doc}, + {NULL, NULL, 0, NULL}, }; static PyObject *bpy_import_test(const char *modname) @@ -632,37 +648,14 @@ void BPy_init_modules(struct bContext *C) /* Register methods and property get/set for RNA types. */ BPY_rna_types_extend_capi(); - /* utility func's that have nowhere else to go */ - PyModule_AddObject(mod, - meth_bpy_script_paths.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_script_paths, NULL)); - PyModule_AddObject( - mod, meth_bpy_blend_paths.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_blend_paths, NULL)); - PyModule_AddObject(mod, - meth_bpy_user_resource.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_user_resource, NULL)); - PyModule_AddObject(mod, - meth_bpy_system_resource.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_system_resource, NULL)); - PyModule_AddObject(mod, - meth_bpy_resource_path.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL)); - PyModule_AddObject(mod, - meth_bpy_escape_identifier.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL)); - PyModule_AddObject(mod, - meth_bpy_unescape_identifier.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); - PyModule_AddObject( - mod, meth_bpy_flip_name.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL)); - PyModule_AddObject(mod, - meth_bpy_context_members.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_context_members, NULL)); - PyModule_AddObject(mod, - meth_bpy_rna_enum_items_static.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_rna_enum_items_static, NULL)); + for (int i = 0; bpy_methods[i].ml_name; i++) { + PyMethodDef *m = &bpy_methods[i]; + /* Currently there is no need to support these. */ + BLI_assert((m->ml_flags & (METH_CLASS | METH_STATIC)) == 0); + PyModule_AddObject(mod, m->ml_name, (PyObject *)PyCFunction_New(m, NULL)); + } - /* register funcs (bpy_rna.c) */ + /* Register functions (`bpy_rna.c`). */ PyModule_AddObject(mod, meth_bpy_register_class.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_register_class, NULL)); diff --git a/source/blender/python/intern/bpy.h b/source/blender/python/intern/bpy.h index 313385b7f5d..f62542bb342 100644 --- a/source/blender/python/intern/bpy.h +++ b/source/blender/python/intern/bpy.h @@ -12,7 +12,7 @@ extern "C" { struct bContext; -/** Creates the bpy module and adds it to `sys.modules` for importing. */ +/** Creates the `bpy` module and adds it to `sys.modules` for importing. */ void BPy_init_modules(struct bContext *C); extern PyObject *bpy_package_py; diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 939473ceaa0..70cf231bc26 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -79,8 +79,6 @@ static PyStructSequence_Field app_info_fields[] = { {"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"}, - {"binary_path", - "The location of Blender's executable, useful for utilities that open new instances"}, {"background", "Boolean, True when blender is running without a user interface (started with -b)"}, {"factory_startup", "Boolean, True when blender is running with --factory-startup)"}, @@ -151,7 +149,6 @@ static PyObject *make_app_info(void) SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE)); SetStrItem(""); - SetStrItem(BKE_appdir_program_path()); SetObjItem(PyBool_FromLong(G.background)); SetObjItem(PyBool_FromLong(G.factory_startup)); @@ -345,6 +342,33 @@ static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void return PyC_UnicodeFromByte(G.autoexec_fail); } +PyDoc_STRVAR(bpy_app_binary_path_doc, + "The location of Blender's executable, useful for utilities that open new instances. " + "Read-only unless Blender is built as a Python module - in this case the value is " + "an empty string which script authors may point to a Blender binary."); +static PyObject *bpy_app_binary_path_get(PyObject *UNUSED(self), void *UNUSED(closure)) +{ + return PyC_UnicodeFromByte(BKE_appdir_program_path()); +} + +static int bpy_app_binary_path_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure)) +{ +#ifndef WITH_PYTHON_MODULE + PyErr_SetString(PyExc_AttributeError, + "bpy.app.binary_path is only writable when built as a Python module"); + return -1; +#endif + PyObject *value_coerce = NULL; + const char *filepath = PyC_UnicodeAsByte(value, &value_coerce); + if (filepath == NULL) { + PyErr_Format(PyExc_ValueError, "expected a string or bytes, got %s", Py_TYPE(value)->tp_name); + return -1; + } + BKE_appdir_program_path_init(filepath); + Py_XDECREF(value_coerce); + return 0; +} + static PyGetSetDef bpy_app_getsets[] = { {"debug", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG}, {"debug_ffmpeg", @@ -450,7 +474,14 @@ static PyGetSetDef bpy_app_getsets[] = { (void *)G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET}, {"autoexec_fail_message", bpy_app_autoexec_fail_message_get, NULL, NULL, NULL}, - /* End-of-list marker. */ + /* Support script authors setting the Blender binary path to use, otherwise this value + * is not known when built as a Python module. */ + {"binary_path", + bpy_app_binary_path_get, + bpy_app_binary_path_set, + bpy_app_binary_path_doc, + NULL}, + {NULL, NULL, NULL, NULL, NULL}, }; @@ -528,7 +559,7 @@ PyObject *BPY_app_struct(void) BlenderAppType.tp_hash = (hashfunc) _Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */ - /* kindof a hack ontop of PyStructSequence */ + /* Kind of a hack on top of #PyStructSequence. */ py_struct_seq_getset_init(); py_struct_seq_method_init(); diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index fe5111c37f2..a744f3fd4fa 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -18,7 +18,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {"codec_avi", NULL}, {"codec_ffmpeg", NULL}, {"codec_sndfile", NULL}, - {"compositor", NULL}, + {"compositor_cpu", NULL}, {"cycles", NULL}, {"cycles_osl", NULL}, {"freestyle", NULL}, @@ -104,7 +104,7 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU SetObjIncref(Py_True); #else SetObjIncref(Py_False); diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 3f884338bbb..918d96d9f44 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -166,7 +166,7 @@ static struct PyModuleDef M_AppIcons_module_def = { NULL, /* m_doc */ 0, /* m_size */ M_AppIcons_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c index 5a42ecfdbc8..4adc200357b 100644 --- a/source/blender/python/intern/bpy_app_timers.c +++ b/source/blender/python/intern/bpy_app_timers.c @@ -168,7 +168,7 @@ static struct PyModuleDef M_AppTimers_module_def = { NULL, /* m_doc */ 0, /* m_size */ M_AppTimers_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index cc64af2a489..04aa203d198 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -318,6 +318,7 @@ static const bool secure_opcodes[255] = { OK_OP(UNARY_INVERT), OK_OP(BINARY_SUBSCR), OK_OP(GET_LEN), + OK_OP(LIST_TO_TUPLE), OK_OP(RETURN_VALUE), OK_OP(SWAP), OK_OP(BUILD_TUPLE), @@ -332,6 +333,7 @@ static const bool secure_opcodes[255] = { OK_OP(POP_JUMP_FORWARD_IF_TRUE), OK_OP(LOAD_GLOBAL), OK_OP(IS_OP), + OK_OP(CONTAINS_OP), OK_OP(BINARY_OP), OK_OP(LOAD_FAST), OK_OP(STORE_FAST), @@ -342,6 +344,14 @@ static const bool secure_opcodes[255] = { OK_OP(LOAD_DEREF), OK_OP(STORE_DEREF), OK_OP(RESUME), + OK_OP(LIST_EXTEND), + OK_OP(SET_UPDATE), +/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it + * can be used to manipulate the name-space (potentially allowing malicious code). */ +# if 0 + OK_OP(DICT_MERGE), + OK_OP(DICT_UPDATE), +# endif OK_OP(POP_JUMP_BACKWARD_IF_NOT_NONE), OK_OP(POP_JUMP_BACKWARD_IF_NONE), OK_OP(POP_JUMP_BACKWARD_IF_FALSE), @@ -395,6 +405,7 @@ static const bool secure_opcodes[255] = { OK_OP(INPLACE_AND), OK_OP(INPLACE_XOR), OK_OP(INPLACE_OR), + OK_OP(LIST_TO_TUPLE), OK_OP(RETURN_VALUE), OK_OP(ROT_N), OK_OP(BUILD_TUPLE), @@ -410,12 +421,21 @@ static const bool secure_opcodes[255] = { OK_OP(POP_JUMP_IF_TRUE), OK_OP(LOAD_GLOBAL), OK_OP(IS_OP), + OK_OP(CONTAINS_OP), OK_OP(LOAD_FAST), OK_OP(STORE_FAST), OK_OP(DELETE_FAST), OK_OP(BUILD_SLICE), OK_OP(LOAD_DEREF), OK_OP(STORE_DEREF), + OK_OP(LIST_EXTEND), + OK_OP(SET_UPDATE), +/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it + * can be used to manipulate the name-space (potentially allowing malicious code). */ +# if 0 + OK_OP(DICT_MERGE), + OK_OP(DICT_UPDATE), +# endif /* Special cases. */ OK_OP(LOAD_CONST), /* Ok because constants are accepted. */ @@ -429,9 +449,10 @@ static const bool secure_opcodes[255] = { # undef OK_OP -static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, - PyObject *dict_arr[], - const char *error_prefix) +bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code, + PyObject *namespace_array[], + const bool verbose, + const char *error_prefix) { PyCodeObject *py_code = (PyCodeObject *)expr_code; @@ -439,21 +460,23 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, { for (int i = 0; i < PyTuple_GET_SIZE(py_code->co_names); i++) { PyObject *name = PyTuple_GET_ITEM(py_code->co_names, i); - + const char *name_str = PyUnicode_AsUTF8(name); bool contains_name = false; - for (int j = 0; dict_arr[j]; j++) { - if (PyDict_Contains(dict_arr[j], name)) { + for (int j = 0; namespace_array[j]; j++) { + if (PyDict_Contains(namespace_array[j], name)) { contains_name = true; break; } } - if (contains_name == false) { - fprintf(stderr, - "\t%s: restricted access disallows name '%s', " - "enable auto-execution to support\n", - error_prefix, - PyUnicode_AsUTF8(name)); + if ((contains_name == false) || (name_str[0] == '_')) { + if (verbose) { + fprintf(stderr, + "\t%s: restricted access disallows name '%s', " + "enable auto-execution to support\n", + error_prefix, + name_str); + } return false; } } @@ -485,11 +508,13 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, for (Py_ssize_t i = 0; i < code_len; i++) { const int opcode = _Py_OPCODE(codestr[i]); if (secure_opcodes[opcode] == false) { - fprintf(stderr, - "\t%s: restricted access disallows opcode '%d', " - "enable auto-execution to support\n", - error_prefix, - opcode); + if (verbose) { + fprintf(stderr, + "\t%s: restricted access disallows opcode '%d', " + "enable auto-execution to support\n", + error_prefix, + opcode); + } ok = false; break; } @@ -506,6 +531,26 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, return true; } +bool BPY_driver_secure_bytecode_test(PyObject *expr_code, PyObject *namespace, const bool verbose) +{ + + if (!bpy_pydriver_Dict) { + if (bpy_pydriver_create_dict() != 0) { + fprintf(stderr, "%s: couldn't create Python dictionary\n", __func__); + return false; + } + } + return BPY_driver_secure_bytecode_test_ex(expr_code, + (PyObject *[]){ + bpy_pydriver_Dict, + bpy_pydriver_Dict__whitelist, + namespace, + NULL, + }, + verbose, + __func__); +} + #endif /* USE_BYTECODE_WHITELIST */ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, @@ -697,14 +742,17 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, #ifdef USE_BYTECODE_WHITELIST if (is_recompile && expr_code) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) { - if (!bpy_driver_secure_bytecode_validate(expr_code, - (PyObject *[]){ - bpy_pydriver_Dict, - bpy_pydriver_Dict__whitelist, - driver_vars, - NULL, - }, - __func__)) { + if (!BPY_driver_secure_bytecode_test_ex( + expr_code, + (PyObject *[]){ + bpy_pydriver_Dict, + bpy_pydriver_Dict__whitelist, + driver_vars, + NULL, + }, + /* Always be verbose since this can give hints to why evaluation fails. */ + true, + __func__)) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) { G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL; BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", expr); diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h index 301c6b3662e..f0d9717dbbd 100644 --- a/source/blender/python/intern/bpy_driver.h +++ b/source/blender/python/intern/bpy_driver.h @@ -10,6 +10,8 @@ extern "C" { #endif +#include <stdbool.h> + /** * For faster execution we keep a special dictionary for py-drivers, with * the needed modules and aliases. @@ -21,6 +23,14 @@ int bpy_pydriver_create_dict(void); */ extern PyObject *bpy_pydriver_Dict; +extern bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code, + PyObject *namespace_array[], + const bool verbose, + const char *error_prefix); +extern bool BPY_driver_secure_bytecode_test(PyObject *expr_code, + PyObject *namespace, + const bool verbose); + #ifdef __cplusplus } #endif diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c index ad235e691c4..99720e7a1ba 100644 --- a/source/blender/python/intern/bpy_gizmo_wrap.c +++ b/source/blender/python/intern/bpy_gizmo_wrap.c @@ -150,7 +150,7 @@ void BPY_RNA_gizmo_wrapper(wmGizmoType *gzt, void *userdata) /* don't do translations here yet */ #if 0 - /* Use i18n context from rna_ext.srna if possible (py gizmogroups). */ + /* Use i18n context from rna_ext.srna if possible (py gizmo-groups). */ if (gt->rna_ext.srna) { RNA_def_struct_translation_context(gt->srna, RNA_struct_translation_context(gt->rna_ext.srna)); } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 939fa475344..3a095f4b9f3 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -11,6 +11,10 @@ #include <Python.h> #include <frameobject.h> +#ifdef WITH_PYTHON_MODULE +# include "pylifecycle.h" /* For `Py_Version`. */ +#endif + #include "MEM_guardedalloc.h" #include "CLG_log.h" @@ -585,6 +589,11 @@ void BPY_python_use_system_env(void) void BPY_python_backtrace(FILE *fp) { fputs("\n# Python backtrace\n", fp); + + /* Can happen in rare cases. */ + if (!_PyThreadState_UncheckedGet()) { + return; + } PyFrameObject *frame; if (!(frame = PyEval_GetFrame())) { return; @@ -763,7 +772,7 @@ static struct PyModuleDef bpy_proxy_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ bpy_module_free, /* m_free */ @@ -802,6 +811,50 @@ static void bpy_module_delay_init(PyObject *bpy_proxy) PyDict_Update(PyModule_GetDict(bpy_proxy), PyModule_GetDict(bpy_package_py)); } +/** + * Raise an error and return false if the Python version used to compile Blender + * isn't compatible with the interpreter loading the `bpy` module. + */ +static bool bpy_module_ensure_compatible_version(void) +{ + /* First check the Python version used matches the major version that Blender was built with. + * While this isn't essential, the error message in this case may be cryptic and misleading. + * NOTE: using `Py_LIMITED_API` would remove the need for this, in practice it's + * unlikely Blender will ever used the limited API though. */ +# if PY_VERSION_HEX >= 0x030b0000 /* Python 3.11 & newer. */ + const uint version_runtime = Py_Version; +# else + uint version_runtime; + { + uint version_runtime_major = 0, version_runtime_minor = 0; + const char *version_str = Py_GetVersion(); + if (sscanf(version_str, "%u.%u.", &version_runtime_major, &version_runtime_minor) != 2) { + /* Should never happen, raise an error to ensure this check never fails silently. */ + PyErr_Format(PyExc_ImportError, "Failed to extract the version from \"%s\"", version_str); + return false; + } + version_runtime = (version_runtime_major << 24) | (version_runtime_minor << 16); + } +# endif + + uint version_compile_major = PY_VERSION_HEX >> 24; + uint version_compile_minor = ((PY_VERSION_HEX & 0x00ff0000) >> 16); + uint version_runtime_major = version_runtime >> 24; + uint version_runtime_minor = ((version_runtime & 0x00ff0000) >> 16); + if ((version_compile_major != version_runtime_major) || + (version_compile_minor != version_runtime_minor)) { + PyErr_Format(PyExc_ImportError, + "The version of \"bpy\" was compiled with: " + "(%u.%u) is incompatible with: (%u.%u) used by the interpreter!", + version_compile_major, + version_compile_minor, + version_runtime_major, + version_runtime_minor); + return false; + } + return true; +} + static void dealloc_obj_dealloc(PyObject *self); static PyTypeObject dealloc_obj_Type; @@ -819,6 +872,10 @@ PyMODINIT_FUNC PyInit_bpy(void); PyMODINIT_FUNC PyInit_bpy(void) { + if (!bpy_module_ensure_compatible_version()) { + return NULL; /* The error has been set. */ + } + PyObject *bpy_proxy = PyModule_Create(&bpy_proxy_def); /* Problem: diff --git a/source/blender/python/intern/bpy_interface_atexit.c b/source/blender/python/intern/bpy_interface_atexit.c index 1ba3ab6a40b..cba9bd59abf 100644 --- a/source/blender/python/intern/bpy_interface_atexit.c +++ b/source/blender/python/intern/bpy_interface_atexit.c @@ -32,7 +32,7 @@ static PyObject *func_bpy_atregister = NULL; /* borrowed reference, `atexit` hol static void atexit_func_call(const char *func_name, PyObject *atexit_func_arg) { - /* NOTE(campbell): no error checking, if any of these fail we'll get a crash + /* NOTE(@campbellbarton): no error checking, if any of these fail we'll get a crash * this is intended, but if its problematic it could be changed. */ PyObject *atexit_mod = PyImport_ImportModuleLevel("atexit", NULL, NULL, NULL, 0); diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index 9754599eeed..f29a8d34359 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -108,7 +108,7 @@ static PyTypeObject bpy_lib_Type = { NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ - /* will only use these if this is a subtype of a py class */ + /* Will only use these if this is a sub-type of a Python class. */ PyObject_GenericGetAttr, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 95879b02295..2db8c08cfd4 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -289,7 +289,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList"); /* Own so these don't move into global reports. */ - BKE_reports_init(reports, RPT_STORE | RPT_OP_HOLD); + BKE_reports_init(reports, RPT_STORE | RPT_OP_HOLD | RPT_PRINT_HANDLED_BY_OWNER); #ifdef BPY_RELEASE_GIL /* release GIL, since a thread could be started from an operator diff --git a/source/blender/python/intern/bpy_path.c b/source/blender/python/intern/bpy_path.c index fbb47817389..f3a1a7cb1df 100644 --- a/source/blender/python/intern/bpy_path.c +++ b/source/blender/python/intern/bpy_path.c @@ -26,7 +26,7 @@ static struct PyModuleDef _bpy_path_module_def = { NULL, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 14be9ceda94..07ce44f5f3b 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -15,6 +15,7 @@ #include <float.h> /* FLT_MIN/MAX */ #include <stddef.h> +#include "RNA_path.h" #include "RNA_types.h" #include "BLI_bitmap.h" @@ -779,7 +780,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) return ret; } -/* NOTE(campbell): Regarding comparison `__cmp__`: +/* NOTE(@campbellbarton): Regarding comparison `__cmp__`: * checking the 'ptr->data' matches works in almost all cases, * however there are a few RNA properties that are fake sub-structs and * share the pointer with the parent, in those cases this happens 'a.b == a' @@ -1396,7 +1397,7 @@ PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop) buf = RNA_property_string_get_alloc(ptr, prop, buf_fixed, sizeof(buf_fixed), &buf_len); #ifdef USE_STRING_COERCE - /* Only file paths get special treatment, they may contain non utf-8 chars. */ + /* Only file paths get special treatment, they may contain non UTF-8 chars. */ if (subtype == PROP_BYTESTRING) { ret = PyBytes_FromStringAndSize(buf, buf_len); } @@ -2212,6 +2213,26 @@ static int pyrna_prop_collection_bool(BPy_PropertyRNA *self) } \ (void)0 +/** + * \param result: The result of calling a subscription operation on a collection (never NULL). + */ +static int pyrna_prop_collection_subscript_is_valid_or_error(const PyObject *value) +{ + if (value != Py_None) { + BLI_assert(BPy_StructRNA_Check(value)); + const BPy_StructRNA *value_pyrna = (const BPy_StructRNA *)value; + if (UNLIKELY(value_pyrna->ptr.type == NULL)) { + /* It's important to use a `TypeError` as that is what's returned when `__getitem__` is + * called on an object that doesn't support item access. */ + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not subscriptable (only iteration is supported)", + Py_TYPE(value)->tp_name); + return -1; + } + } + return 0; +} + /* Internal use only. */ static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_ssize_t keynum) { @@ -2222,8 +2243,35 @@ static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_s PYRNA_PROP_COLLECTION_ABS_INDEX(NULL); - if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) { - return pyrna_struct_CreatePyObject(&newptr); + if (RNA_property_collection_lookup_int_has_fn(self->prop)) { + if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) { + return pyrna_struct_CreatePyObject(&newptr); + } + } + else { + /* No callback defined, just iterate and find the nth item. */ + const int key = (int)keynum_abs; + PyObject *result = NULL; + bool found = false; + CollectionPropertyIterator iter; + RNA_property_collection_begin(&self->ptr, self->prop, &iter); + for (int i = 0; iter.valid; RNA_property_collection_next(&iter), i++) { + if (i == key) { + result = pyrna_struct_CreatePyObject(&iter.ptr); + found = true; + break; + } + } + /* It's important to end the iterator after `result` has been created + * so iterators may optionally invalidate items that were iterated over, see: T100286. */ + RNA_property_collection_end(&iter); + if (found) { + if (result && (pyrna_prop_collection_subscript_is_valid_or_error(result) == -1)) { + Py_DECREF(result); + result = NULL; /* The exception has been set. */ + } + return result; + } } const int len = RNA_property_collection_length(&self->ptr, self->prop); @@ -2305,8 +2353,45 @@ static PyObject *pyrna_prop_collection_subscript_str(BPy_PropertyRNA *self, cons PYRNA_PROP_CHECK_OBJ(self); - if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) { - return pyrna_struct_CreatePyObject(&newptr); + if (RNA_property_collection_lookup_string_has_fn(self->prop)) { + if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) { + return pyrna_struct_CreatePyObject(&newptr); + } + } + else { + /* No callback defined, just iterate and find the nth item. */ + const int keylen = strlen(keyname); + char name[256]; + int namelen; + PyObject *result = NULL; + bool found = false; + CollectionPropertyIterator iter; + RNA_property_collection_begin(&self->ptr, self->prop, &iter); + for (int i = 0; iter.valid; RNA_property_collection_next(&iter), i++) { + PropertyRNA *nameprop = RNA_struct_name_property(iter.ptr.type); + char *nameptr = RNA_property_string_get_alloc( + &iter.ptr, nameprop, name, sizeof(name), &namelen); + if ((keylen == namelen) && STREQ(nameptr, keyname)) { + found = true; + } + if ((char *)&name != nameptr) { + MEM_freeN(nameptr); + } + if (found) { + result = pyrna_struct_CreatePyObject(&iter.ptr); + break; + } + } + /* It's important to end the iterator after `result` has been created + * so iterators may optionally invalidate items that were iterated over, see: T100286. */ + RNA_property_collection_end(&iter); + if (found) { + if (result && (pyrna_prop_collection_subscript_is_valid_or_error(result) == -1)) { + Py_DECREF(result); + result = NULL; /* The exception has been set. */ + } + return result; + } } PyErr_Format(PyExc_KeyError, "bpy_prop_collection[key]: key \"%.200s\" not found", keyname); @@ -3989,7 +4074,7 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr) { const char *idname; - /* For looping over attrs and funcs. */ + /* For looping over attributes and functions. */ PointerRNA tptr; PropertyRNA *iterprop; @@ -5852,7 +5937,7 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat len = RNA_property_array_length(ptr, prop); } - /* Resolve the array from a new pytype. */ + /* Resolve the array from a new Python type. */ /* TODO(Kazanbas): make multi-dimensional sequences here. */ @@ -6402,7 +6487,7 @@ static PyObject *pyrna_func_doc_get(BPy_FunctionRNA *self, void *UNUSED(closure) PyTypeObject pyrna_struct_meta_idprop_Type = { PyVarObject_HEAD_INIT(NULL, 0) "bpy_struct_meta_idprop", /* tp_name */ - /* NOTE: would be PyTypeObject, but subtypes of Type must be PyHeapTypeObject's. */ + /* NOTE: would be PyTypeObject, but sub-types of Type must be PyHeapTypeObject's. */ sizeof(PyHeapTypeObject), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -7223,7 +7308,7 @@ static PyObject *pyrna_srna_PyBase(StructRNA *srna) //, PyObject *bpy_types_dic if (base && base != srna) { // printf("debug subtype %s %p\n", RNA_struct_identifier(srna), srna); py_base = pyrna_srna_Subtype(base); //, bpy_types_dict); - Py_DECREF(py_base); /* Srna owns, this is only to pass as an arg. */ + Py_DECREF(py_base); /* `srna` owns, this is only to pass as an argument. */ } if (py_base == NULL) { @@ -7783,7 +7868,7 @@ static struct PyModuleDef bpy_types_module_def = { bpy_types_module_doc, /* m_doc */ sizeof(struct BPy_TypesModule_State), /* m_size */ bpy_types_module_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ @@ -8253,7 +8338,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, continue; } - /* TODO(campbell): this is used for classmethod's too, + /* TODO(@campbellbarton): 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. */ @@ -8344,7 +8429,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, continue; } - /* TODO(campbell): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */ + /* TODO(@campbellbarton): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */ identifier = RNA_property_identifier(prop); item = PyObject_GetAttrString(py_class, identifier); @@ -8650,10 +8735,10 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param else if (ret_len == 1) { err = pyrna_py_to_prop(&funcptr, pret_single, retdata_single, ret, ""); - /* when calling operator funcs only gives Function.result with - * no line number since the func has finished calling on error, - * re-raise the exception with more info since it would be slow to - * create prefix on every call (when there are no errors) */ + /* When calling operator functions only gives `Function.result` with no line number + * since the function has finished calling on error, re-raise the exception with more + * information since it would be slow to create prefix on every call + * (when there are no errors). */ if (err == -1) { PyC_Err_Format_Prefix(PyExc_RuntimeError, "class %.200s, function %.200s: incompatible return value ", @@ -8761,7 +8846,7 @@ static void bpy_class_free(void *pyob_ptr) } /** - * \note This isn't essential to run on startup, since subtypes will lazy initialize. + * \note This isn't essential to run on startup, since sub-types will lazy initialize. * But keep running in debug mode so we get immediate notification of bad class hierarchy * or any errors in "bpy_types.py" at load time, so errors don't go unnoticed. */ diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index 91fa0ea2c8d..d7778da6213 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -218,11 +218,13 @@ void pyrna_struct_type_extend_capi(struct StructRNA *srna, struct PyMethodDef *py_method, struct PyGetSetDef *py_getset); -/* called before stopping python */ +/* Called before stopping Python. */ + void pyrna_alloc_types(void); void pyrna_free_types(void); -/* primitive type conversion */ +/* Primitive type conversion. */ + int pyrna_py_to_array( PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyObject *py, const char *error_prefix); int pyrna_py_to_array_index(PointerRNA *ptr, diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index fb744432d4b..d4a164d9482 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -32,6 +32,7 @@ #include "RNA_access.h" #include "RNA_enum_types.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "WM_api.h" @@ -487,7 +488,8 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb 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); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); result = true; } } diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index 8506ec97bc3..48ba028edf0 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -927,7 +927,7 @@ PyObject *pyrna_py_from_array_index(BPy_PropertyArrayRNA *self, /* just in case check */ len = RNA_property_multi_array_length(ptr, prop, arraydim); if (index >= len || index < 0) { - /* this shouldn't happen because higher level funcs must check for invalid index */ + /* This shouldn't happen because higher level functions must check for invalid index. */ CLOG_WARN(BPY_LOG_RNA, "invalid index %d for array with length=%d", index, len); PyErr_SetString(PyExc_IndexError, "out of range"); diff --git a/source/blender/python/intern/bpy_rna_data.c b/source/blender/python/intern/bpy_rna_data.c index cc0b4aa57d5..a3e865f4846 100644 --- a/source/blender/python/intern/bpy_rna_data.c +++ b/source/blender/python/intern/bpy_rna_data.c @@ -86,7 +86,7 @@ static PyTypeObject bpy_rna_data_context_Type = { NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ - /* will only use these if this is a subtype of a py class */ + /* Will only use these if this is a sub-type of a Python class. */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 61f439e5152..32ef7865e84 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -314,6 +314,8 @@ PyDoc_STRVAR( "\n" " Assigns callbacks to a gizmos property.\n" "\n" + " :arg target: Target property name.\n" + " :type target: string\n" " :arg get: Function that returns the value for this property (single value or sequence).\n" " :type get: callable\n" " :arg set: Function that takes a single value argument and applies it.\n" diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c index fd6cc93ed32..e0a4356dc18 100644 --- a/source/blender/python/intern/bpy_rna_operator.c +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -84,7 +84,7 @@ PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc, " When message is callable, " "additional user defined positional arguments are passed to the message function.\n" "\n" - " :param message: The message or a function that returns the message.\n" + " :arg message: The message or a function that returns the message.\n" " :type message: string or a callable that returns a string or None.\n"); static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args) diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index c3a07847aff..2b830eb9ffe 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -185,16 +185,16 @@ PyDoc_STRVAR( " It will be called every time the specified region in the space type will be drawn.\n" " Note: All arguments are positional only for now.\n" "\n" - " :param callback:\n" + " :arg callback:\n" " A function that will be called when the region is drawn.\n" " It gets the specified arguments as input.\n" " :type callback: function\n" - " :param args: Arguments that will be passed to the callback.\n" + " :arg args: Arguments that will be passed to the callback.\n" " :type args: tuple\n" - " :param region_type: The region type the callback draws in; usually ``WINDOW``. " + " :arg region_type: The region type the callback draws in; usually ``WINDOW``. " "(:class:`bpy.types.Region.type`)\n" " :type region_type: str\n" - " :param draw_type: Usually ``POST_PIXEL`` for 2D drawing and ``POST_VIEW`` for 3D drawing. " + " :arg draw_type: Usually ``POST_PIXEL`` for 2D drawing and ``POST_VIEW`` for 3D drawing. " "In some cases ``PRE_VIEW`` can be used. ``BACKDROP`` can be used for backdrops in the node " "editor.\n" " :type draw_type: str\n" @@ -206,9 +206,9 @@ PyDoc_STRVAR(pyrna_draw_handler_remove_doc, "\n" " Remove a draw handler that was added previously.\n" "\n" - " :param handler: The draw handler that should be removed.\n" + " :arg handler: The draw handler that should be removed.\n" " :type handler: object\n" - " :param region_type: Region type the callback was added to.\n" + " :arg region_type: Region type the callback was added to.\n" " :type region_type: str\n"); static struct PyMethodDef pyrna_space_methods[] = { diff --git a/source/blender/python/intern/bpy_traceback.c b/source/blender/python/intern/bpy_traceback.c index 1f2458c752f..428263fd4c4 100644 --- a/source/blender/python/intern/bpy_traceback.c +++ b/source/blender/python/intern/bpy_traceback.c @@ -25,7 +25,7 @@ static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce) return PyBytes_AS_STRING(*coerce); } -/* copied from pythonrun.c, 3.10.0 */ +/* Copied from `pythonrun.c`, 3.10.0 */ _Py_static_string(PyId_string, "<string>"); static int parse_syntax_error(PyObject *err, diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index 1e5856a3285..075a68f31f9 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -90,7 +90,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type, const char **str_iter; PyStructSequence_Field *desc; - /* initialize array */ + /* Initialize array. */ /* We really populate the contexts' fields here! */ for (str_iter = str_items, desc = py_sseq_desc->fields; *str_iter; str_iter++, desc++) { desc->name = (char *)*str_iter; @@ -101,7 +101,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type, PyStructSequence_InitType(py_type, py_sseq_desc); - /* initialize pytype */ + /* Initialize the Python type. */ py_struct_seq = PyStructSequence_New(py_type); BLI_assert(py_struct_seq != NULL); diff --git a/source/blender/python/intern/stubs.c b/source/blender/python/intern/stubs.c index c29f9188eea..f860bdc36ee 100644 --- a/source/blender/python/intern/stubs.c +++ b/source/blender/python/intern/stubs.c @@ -25,7 +25,7 @@ void BPY_pyconstraint_exec(struct bPythonConstraint *con, void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTarget *ct) { } -int BPY_is_pyconstraint(struct Text *text) +bool BPY_is_pyconstraint(struct Text *text) { return 0; } diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 1aa2cec861c..7cba95a1f85 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -710,7 +710,7 @@ void BaseMathObject_dealloc(BaseMathObject *self) BaseMathObject_clear(self); } - Py_TYPE(self)->tp_free(self); // PyObject_DEL(self); /* breaks subtypes. */ + Py_TYPE(self)->tp_free(self); // PyObject_DEL(self); /* breaks sub-types. */ } /*----------------------------MODULE INIT-------------------------*/ @@ -724,7 +724,7 @@ static struct PyModuleDef M_Mathutils_module_def = { M_Mathutils_doc, /* m_doc */ 0, /* m_size */ M_Mathutils_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h index 84386e99d18..27d84a80601 100644 --- a/source/blender/python/mathutils/mathutils.h +++ b/source/blender/python/mathutils/mathutils.h @@ -6,7 +6,7 @@ * \ingroup pymathutils */ -/* Can cast different mathutils types to this, use for generic funcs */ +/* Can cast different mathutils types to this, use for generic functions. */ #include "BLI_compiler_attrs.h" @@ -21,7 +21,7 @@ extern char BaseMathObject_owner_doc[]; ((struct_name *)((base_type ? (base_type)->tp_alloc(base_type, 0) : \ _PyObject_GC_New(&(root_type))))) -/** BaseMathObject.flag */ +/** #BaseMathObject.flag */ enum { /** * Do not own the memory used in this vector, @@ -43,9 +43,9 @@ enum { float *_data; \ /** If this vector references another object, otherwise NULL, *Note* this owns its reference */ \ PyObject *cb_user; \ - /** Which user funcs do we adhere to, RNA, etc */ \ + /** Which user functions do we adhere to, RNA, etc */ \ unsigned char cb_type; \ - /** Subtype: location, rotation... \ + /** Sub-type: location, rotation... \ * to avoid defining many new functions for every attribute of the same type */ \ unsigned char cb_subtype; \ /** Wrapped data type. */ \ diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 88e8d880360..a66178baefc 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -14,7 +14,9 @@ #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" -#include "IMB_colormanagement.h" +#ifndef MATH_STANDALONE +# include "IMB_colormanagement.h" +#endif #ifndef MATH_STANDALONE # include "BLI_dynstr.h" @@ -92,6 +94,8 @@ static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /** \name Color Methods: Color Space Conversion * \{ */ +#ifndef MATH_STANDALONE + PyDoc_STRVAR(Color_from_scene_linear_to_srgb_doc, ".. function:: from_scene_linear_to_srgb()\n" "\n" @@ -204,6 +208,8 @@ static PyObject *Color_from_rec709_linear_to_scene_linear(ColorObject *self) return Color_CreatePyObject(col, Py_TYPE(self)); } +#endif /* MATH_STANDALONE */ + /** \} */ /* -------------------------------------------------------------------- */ @@ -1050,7 +1056,8 @@ static struct PyMethodDef Color_methods[] = { /* base-math methods */ {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, - /* Color-space methods. */ +/* Color-space methods. */ +#ifndef MATH_STANDALONE {"from_scene_linear_to_srgb", (PyCFunction)Color_from_scene_linear_to_srgb, METH_NOARGS, @@ -1083,6 +1090,8 @@ static struct PyMethodDef Color_methods[] = { (PyCFunction)Color_from_rec709_linear_to_scene_linear, METH_NOARGS, Color_from_rec709_linear_to_scene_linear_doc}, +#endif /* MATH_STANDALONE */ + {NULL, NULL, 0, NULL}, }; @@ -1102,7 +1111,7 @@ PyDoc_STRVAR( " the OpenColorIO configuration. The notable exception is user interface theming colors, " " which are in sRGB color space.\n" "\n" - " :param rgb: (r, g, b) color values\n" + " :arg rgb: (r, g, b) color values\n" " :type rgb: 3d vector\n"); PyTypeObject color_Type = { PyVarObject_HEAD_INIT(NULL, 0) "Color", /* tp_name */ diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index f49868dfba7..8f950bce344 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -774,9 +774,9 @@ PyDoc_STRVAR( "\n" " .. seealso:: `Euler angles <https://en.wikipedia.org/wiki/Euler_angles>`__ on Wikipedia.\n" "\n" - " :param angles: Three angles, in radians.\n" + " :arg angles: Three angles, in radians.\n" " :type angles: 3d vector\n" - " :param order: Optional order of the angles, a permutation of ``XYZ``.\n" + " :arg order: Optional order of the angles, a permutation of ``XYZ``.\n" " :type order: str\n"); PyTypeObject euler_Type = { PyVarObject_HEAD_INIT(NULL, 0) "Euler", /* tp_name */ diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 8cd7a5c7d87..af01c571fe9 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -290,6 +290,18 @@ static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), return NULL; } +static bool matrix_is_identity(MatrixObject *self) +{ + for (int row = 0; row < self->row_num; row++) { + for (int col = 0; col < self->col_num; col++) { + if (MATRIX_ITEM(self, row, col) != ((row != col) ? 0.0f : 1.0f)) { + return false; + } + } + } + return true; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1232,12 +1244,11 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self) return NULL; } if (self->row_num == 3) { - mat3_to_quat(quat, (float(*)[3])self->matrix); + mat3_to_quat(quat, (const float(*)[3])self->matrix); } else { mat4_to_quat(quat, (const float(*)[4])self->matrix); } - return Quaternion_CreatePyObject(quat, NULL); } @@ -1876,7 +1887,7 @@ static PyObject *Matrix_decompose(MatrixObject *self) } mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix); - mat3_to_quat(quat, rot); + mat3_normalized_to_quat_fast(quat, rot); ret = PyTuple_New(3); PyTuple_SET_ITEMS(ret, @@ -3104,6 +3115,16 @@ static PyObject *Matrix_median_scale_get(MatrixObject *self, void *UNUSED(closur return PyFloat_FromDouble(mat3_to_scale(mat)); } +PyDoc_STRVAR(Matrix_is_identity_doc, + "True if this is an identity matrix (read-only).\n\n:type: bool"); +static PyObject *Matrix_is_identity_get(MatrixObject *self, void *UNUSED(closure)) +{ + if (BaseMath_ReadCallback(self) == -1) { + return NULL; + } + return PyBool_FromLong(matrix_is_identity(self)); +} + PyDoc_STRVAR(Matrix_is_negative_doc, "True if this matrix results in a negative scale, 3x3 and 4x4 only, " "(read-only).\n\n:type: bool"); @@ -3187,6 +3208,7 @@ static PyGetSetDef Matrix_getseters[] = { NULL}, {"row", (getter)Matrix_row_get, (setter)NULL, Matrix_row_doc, NULL}, {"col", (getter)Matrix_col_get, (setter)NULL, Matrix_col_doc, NULL}, + {"is_identity", (getter)Matrix_is_identity_get, (setter)NULL, Matrix_is_identity_doc, NULL}, {"is_negative", (getter)Matrix_is_negative_get, (setter)NULL, Matrix_is_negative_doc, NULL}, {"is_orthogonal", (getter)Matrix_is_orthogonal_get, @@ -3299,8 +3321,7 @@ PyDoc_STRVAR( " This object gives access to Matrices in Blender, supporting square and rectangular\n" " matrices from 2x2 up to 4x4.\n" "\n" - " :param rows: Sequence of rows.\n" - " When omitted, a 4x4 identity matrix is constructed.\n" + " :arg rows: Sequence of rows. When omitted, a 4x4 identity matrix is constructed.\n" " :type rows: 2d number sequence\n"); PyTypeObject matrix_Type = { PyVarObject_HEAD_INIT(NULL, 0) "Matrix", /*tp_name*/ diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 6994a313237..d405d5e63ce 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -543,7 +543,6 @@ static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value) length = normalize_qt_qt(tquat, self->quat); quat_to_mat3(self_rmat, tquat); mul_m3_m3m3(rmat, other_rmat, self_rmat); - mat3_to_quat(self->quat, rmat); mul_qt_fl(self->quat, length); /* maintain length after rotating */ @@ -1663,9 +1662,9 @@ PyDoc_STRVAR(quaternion_doc, "\n" " This object gives access to Quaternions in Blender.\n" "\n" - " :param seq: size 3 or 4\n" + " :arg seq: size 3 or 4\n" " :type seq: :class:`Vector`\n" - " :param angle: rotation angle, in radians\n" + " :arg angle: rotation angle, in radians\n" " :type angle: float\n" "\n" " The constructor takes arguments in various forms:\n" diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 0c9cbd6ccfa..cf684341d4f 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -3188,7 +3188,7 @@ PyDoc_STRVAR(vector_doc, "\n" " This object gives access to Vectors in Blender.\n" "\n" - " :param seq: Components of the vector, must be a sequence of at least two\n" + " :arg seq: Components of the vector, must be a sequence of at least two\n" " :type seq: sequence of numbers\n"); PyTypeObject vector_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index ead255a6716..4bdb1adcdde 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -1148,12 +1148,12 @@ static PyObject *C_BVHTree_FromObject(PyObject *UNUSED(cls), PyObject *args, PyO coords = MEM_mallocN(sizeof(*coords) * (size_t)coords_len, __func__); tris = MEM_mallocN(sizeof(*tris) * (size_t)tris_len, __func__); - MVert *mv = mesh->mvert; - for (int i = 0; i < mesh->totvert; i++, mv++) { - copy_v3_v3(coords[i], mv->co); + const MVert *verts = BKE_mesh_verts(mesh); + for (int i = 0; i < mesh->totvert; i++) { + copy_v3_v3(coords[i], verts[i].co); } - mloop = mesh->mloop; + mloop = BKE_mesh_loops(mesh); } { @@ -1294,7 +1294,7 @@ static struct PyModuleDef bvhtree_moduledef = { py_bvhtree_doc, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 1e492574903..28deebcf5ac 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1790,7 +1790,7 @@ static struct PyModuleDef M_Geometry_module_def = { M_Geometry_doc, /* m_doc */ 0, /* m_size */ M_Geometry_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c index 6523b3f7259..10f42d9b070 100644 --- a/source/blender/python/mathutils/mathutils_interpolate.c +++ b/source/blender/python/mathutils/mathutils_interpolate.c @@ -93,7 +93,7 @@ static struct PyModuleDef M_Interpolate_module_def = { M_Interpolate_doc, /* m_doc */ 0, /* m_size */ M_Interpolate_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c index 66c48697b6b..f5a27c6f90f 100644 --- a/source/blender/python/mathutils/mathutils_kdtree.c +++ b/source/blender/python/mathutils/mathutils_kdtree.c @@ -428,7 +428,7 @@ static struct PyModuleDef kdtree_moduledef = { py_kdtree_doc, /* m_doc */ 0, /* m_size */ NULL, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c index e1282e90c48..3a3297f27f7 100644 --- a/source/blender/python/mathutils/mathutils_noise.c +++ b/source/blender/python/mathutils/mathutils_noise.c @@ -1089,7 +1089,7 @@ static struct PyModuleDef M_Noise_module_def = { M_Noise_doc, /* m_doc */ 0, /* m_size */ M_Noise_methods, /* m_methods */ - NULL, /* m_reload */ + NULL, /* m_slots */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ |