diff options
Diffstat (limited to 'source/blender/python/generic')
-rw-r--r-- | source/blender/python/generic/bgl.c | 135 | ||||
-rw-r--r-- | source/blender/python/generic/bpy_internal_import.c | 25 | ||||
-rw-r--r-- | source/blender/python/generic/idprop_py_api.c | 6 | ||||
-rw-r--r-- | source/blender/python/generic/py_capi_utils.c | 187 | ||||
-rw-r--r-- | source/blender/python/generic/py_capi_utils.h | 43 | ||||
-rw-r--r-- | source/blender/python/generic/python_utildefines.h | 6 |
6 files changed, 308 insertions, 94 deletions
diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index ed4726e2ab7..5c938ef2ee3 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -476,6 +476,49 @@ int BGL_typeSize(int type) return -1; } +static int gl_buffer_type_from_py_format_char(char *typestr) +{ + if (ELEM(typestr[0], '<', '>', '|')) { + typestr += 1; + } + char format = typestr[0]; + char byte_num = typestr[1]; + + switch (format) { + case 't': + case 'b': + case 'h': + if (!byte_num) return GL_BYTE; + ATTR_FALLTHROUGH; + case 'i': + if (!byte_num) return GL_SHORT; + ATTR_FALLTHROUGH; + case 'l': + if (!byte_num || byte_num == '4') return GL_INT; + if (byte_num == '1') return GL_BYTE; + if (byte_num == '2') return GL_SHORT; + break; + case 'f': + if (!byte_num) return GL_FLOAT; + ATTR_FALLTHROUGH; + case 'd': + if (!byte_num || byte_num == '8') return GL_DOUBLE; + if (byte_num == '4') return GL_FLOAT; + break; + } + return -1; /* UNKNOWN */ +} + +static bool compare_dimensions(int ndim, int *dim1, Py_ssize_t *dim2) +{ + for (int i = 0; i < ndim; i++) { + if (dim1[i] != dim2[i]) { + return false; + } + } + return true; +} + /** \} */ @@ -634,6 +677,22 @@ PyTypeObject BGL_bufferType = { NULL /*tp_del*/ }; + +static Buffer *BGL_MakeBuffer_FromData(PyObject *parent, int type, int ndimensions, int *dimensions, void *buf) +{ + Buffer *buffer = (Buffer *)PyObject_NEW(Buffer, &BGL_bufferType); + + Py_XINCREF(parent); + buffer->parent = parent; + buffer->ndimensions = ndimensions; + buffer->dimensions = MEM_mallocN(ndimensions * sizeof(int), "Buffer dimensions"); + memcpy(buffer->dimensions, dimensions, ndimensions * sizeof(int)); + buffer->type = type; + buffer->buf.asvoid = buf; + + return buffer; +} + /** * Create a buffer object * @@ -645,30 +704,21 @@ Buffer *BGL_MakeBuffer(int type, int ndimensions, int *dimensions, void *initbuf { Buffer *buffer; void *buf = NULL; - int i, size, length; + int i, size = BGL_typeSize(type); - length = 1; for (i = 0; i < ndimensions; i++) { - length *= dimensions[i]; + size *= dimensions[i]; } - size = BGL_typeSize(type); + buf = MEM_mallocN(size, "Buffer buffer"); - buf = MEM_mallocN(length * size, "Buffer buffer"); - - buffer = (Buffer *)PyObject_NEW(Buffer, &BGL_bufferType); - buffer->parent = NULL; - buffer->ndimensions = ndimensions; - buffer->dimensions = MEM_mallocN(ndimensions * sizeof(int), "Buffer dimensions"); - memcpy(buffer->dimensions, dimensions, ndimensions * sizeof(int)); - buffer->type = type; - buffer->buf.asvoid = buf; + buffer = BGL_MakeBuffer_FromData(NULL, type, ndimensions, dimensions, buf); if (initbuffer) { - memcpy(buffer->buf.asvoid, initbuffer, length * size); + memcpy(buffer->buf.asvoid, initbuffer, size); } else { - memset(buffer->buf.asvoid, 0, length * size); + memset(buffer->buf.asvoid, 0, size); } return buffer; } @@ -678,7 +728,7 @@ Buffer *BGL_MakeBuffer(int type, int ndimensions, int *dimensions, void *initbuf static PyObject *Buffer_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds) { PyObject *length_ob = NULL, *init = NULL; - Buffer *buffer; + Buffer *buffer = NULL; int dimensions[MAX_DIMENSIONS]; int type; @@ -743,9 +793,32 @@ static PyObject *Buffer_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject return NULL; } - buffer = BGL_MakeBuffer(type, ndimensions, dimensions, NULL); - if (init && ndimensions) { - if (Buffer_ass_slice(buffer, 0, dimensions[0], init)) { + if (init && PyObject_CheckBuffer(init)) { + Py_buffer pybuffer; + + if (PyObject_GetBuffer(init, &pybuffer, PyBUF_ND | PyBUF_FORMAT) == -1) { + /* PyObject_GetBuffer raise a PyExc_BufferError */ + return NULL; + } + + if (type != gl_buffer_type_from_py_format_char(pybuffer.format)) { + PyErr_Format(PyExc_TypeError, + "`GL_TYPE` and `typestr` of object with buffer interface do not match. '%s'", pybuffer.format); + } + else if (ndimensions != pybuffer.ndim || + !compare_dimensions(ndimensions, dimensions, pybuffer.shape)) + { + PyErr_Format(PyExc_TypeError, "array size does not match"); + } + else { + buffer = BGL_MakeBuffer_FromData(init, type, pybuffer.ndim, dimensions, pybuffer.buf); + } + + PyBuffer_Release(&pybuffer); + } + else { + buffer = BGL_MakeBuffer(type, ndimensions, dimensions, NULL); + if (init && Buffer_ass_slice(buffer, 0, dimensions[0], init)) { Py_DECREF(buffer); return NULL; } @@ -778,27 +851,17 @@ static PyObject *Buffer_item(Buffer *self, int i) } } else { - Buffer *newbuf; - int j, length, size; + int j, offset = i * BGL_typeSize(self->type); - length = 1; for (j = 1; j < self->ndimensions; j++) { - length *= self->dimensions[j]; + offset *= self->dimensions[j]; } - size = BGL_typeSize(self->type); - - newbuf = (Buffer *)PyObject_NEW(Buffer, &BGL_bufferType); - - Py_INCREF(self); - newbuf->parent = (PyObject *)self; - - newbuf->ndimensions = self->ndimensions - 1; - newbuf->type = self->type; - newbuf->buf.asvoid = self->buf.asbyte + i * length * size; - newbuf->dimensions = MEM_mallocN(newbuf->ndimensions * sizeof(int), "Buffer dimensions"); - memcpy(newbuf->dimensions, self->dimensions + 1, newbuf->ndimensions * sizeof(int)); - return (PyObject *)newbuf; + return (PyObject *)BGL_MakeBuffer_FromData( + (PyObject *)self, self->type, + self->ndimensions - 1, + self->dimensions + 1, + self->buf.asbyte + offset); } return NULL; diff --git a/source/blender/python/generic/bpy_internal_import.c b/source/blender/python/generic/bpy_internal_import.c index ed2752d8372..ffac09efdde 100644 --- a/source/blender/python/generic/bpy_internal_import.c +++ b/source/blender/python/generic/bpy_internal_import.c @@ -248,8 +248,17 @@ PyObject *bpy_text_reimport(PyObject *module, int *found) if ((name = PyModule_GetName(module)) == NULL) return NULL; - if ((filepath = (char *)PyModule_GetFilename(module)) == NULL) - return NULL; + { + PyObject *module_file = PyModule_GetFilenameObject(module); + if (module_file == NULL) { + return NULL; + } + filepath = _PyUnicode_AsString(module_file); + Py_DECREF(module_file); + if (filepath == NULL) { + return NULL; + } + } /* look up the text object */ text = BLI_findstring(&maggie->text, BLI_path_basename(filepath), offsetof(ID, name) + 2); @@ -276,13 +285,13 @@ static PyObject *blender_import(PyObject *UNUSED(self), PyObject *args, PyObject int found = 0; PyObject *globals = NULL, *locals = NULL, *fromlist = NULL; int level = 0; /* relative imports */ - PyObject *newmodule; - //PyObject_Print(args, stderr, 0); - static const char *kwlist[] = {"name", "globals", "locals", "fromlist", "level", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kw, "s|OOOi:bpy_import_meth", (char **)kwlist, - &name, &globals, &locals, &fromlist, &level)) + + static const char *_keywords[] = {"name", "globals", "locals", "fromlist", "level", NULL}; + static _PyArg_Parser _parser = {"s|OOOi:bpy_import_meth", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kw, &_parser, + &name, &globals, &locals, &fromlist, &level)) { return NULL; } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 5d6a7c578a2..1153e0176df 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -386,7 +386,7 @@ static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob) static IDProperty *idp_from_PyLong(const char *name, PyObject *ob) { IDPropertyTemplate val = {0}; - val.i = _PyLong_AsInt(ob); + val.i = PyC_Long_AsI32(ob); if (val.i == -1 && PyErr_Occurred()) { return NULL; } @@ -499,7 +499,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) prop_data = IDP_Array(prop); for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; - if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { + if (((prop_data[i] = PyC_Long_AsI32(item)) == -1) && PyErr_Occurred()) { return NULL; } } @@ -1337,7 +1337,7 @@ static int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *value) } case IDP_INT: { - const int i = _PyLong_AsInt(value); + const int i = PyC_Long_AsI32(value); if (i == -1 && PyErr_Occurred()) { return -1; } diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 2e789d6d4b3..d49f9514b8c 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -85,7 +85,7 @@ int PyC_AsArray_FAST( /* could use is_double for 'long int' but no use now */ int *array_int = array; for (i = 0; i < length; i++) { - array_int[i] = PyLong_AsLong(value_fast_items[i]); + array_int[i] = PyC_Long_AsI32(value_fast_items[i]); } } else if (type == &PyBool_Type) { @@ -127,54 +127,52 @@ int PyC_AsArray( return ret; } +/* -------------------------------------------------------------------- */ +/** \name Typed Tuple Packing + * + * \note See #PyC_Tuple_Pack_* macros that take multiple arguments. + * + * \{ */ + /* array utility function */ -PyObject *PyC_FromArray(const void *array, int length, const PyTypeObject *type, - const bool is_double, const char *error_prefix) +PyObject *PyC_Tuple_PackArray_F32(const float *array, uint len) { - PyObject *tuple; - int i; - - tuple = PyTuple_New(length); - - /* for each type */ - if (type == &PyFloat_Type) { - if (is_double) { - const double *array_double = array; - for (i = 0; i < length; ++i) { - PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array_double[i])); - } - } - else { - const float *array_float = array; - for (i = 0; i < length; ++i) { - PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array_float[i])); - } - } - } - else if (type == &PyLong_Type) { - /* could use is_double for 'long int' but no use now */ - const int *array_int = array; - for (i = 0; i < length; ++i) { - PyTuple_SET_ITEM(tuple, i, PyLong_FromLong(array_int[i])); - } + PyObject *tuple = PyTuple_New(len); + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array[i])); } - else if (type == &PyBool_Type) { - const int *array_bool = array; - for (i = 0; i < length; ++i) { - PyTuple_SET_ITEM(tuple, i, PyBool_FromLong(array_bool[i])); - } + return tuple; +} + +PyObject *PyC_Tuple_PackArray_I32(const int *array, uint len) +{ + PyObject *tuple = PyTuple_New(len); + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM(tuple, i, PyLong_FromLong(array[i])); } - else { - Py_DECREF(tuple); - PyErr_Format(PyExc_TypeError, - "%s: internal error %s is invalid", - error_prefix, type->tp_name); - return NULL; + return tuple; +} + +PyObject *PyC_Tuple_PackArray_I32FromBool(const int *array, uint len) +{ + PyObject *tuple = PyTuple_New(len); + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM(tuple, i, PyBool_FromLong(array[i])); } + return tuple; +} +PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len) +{ + PyObject *tuple = PyTuple_New(len); + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM(tuple, i, PyBool_FromLong(array[i])); + } return tuple; } +/** \} */ + /** * Caller needs to ensure tuple is uninitialized. * Handy for filling a tuple with None for eg. @@ -203,6 +201,8 @@ void PyC_List_Fill(PyObject *list, PyObject *value) /** * Use with PyArg_ParseTuple's "O&" formatting. + * + * \see #PyC_Long_AsBool for a similar function to use outside of argument parsing. */ int PyC_ParseBool(PyObject *o, void *p) { @@ -300,7 +300,14 @@ void PyC_FileAndNum(const char **filename, int *lineno) if (mod_name) { PyObject *mod = PyDict_GetItem(PyImport_GetModuleDict(), mod_name); if (mod) { - *filename = PyModule_GetFilename(mod); + PyObject *mod_file = PyModule_GetFilenameObject(mod); + if (mod_file) { + *filename = _PyUnicode_AsString(mod_name); + Py_DECREF(mod_file); + } + else { + PyErr_Clear(); + } } /* unlikely, fallback */ @@ -1108,3 +1115,101 @@ bool PyC_RunString_AsString(const char *expr, const char *filename, char **r_val } #endif /* #ifndef MATH_STANDALONE */ + +/* -------------------------------------------------------------------- */ + +/** \name Int Conversion + * + * \note Python doesn't provide overflow checks for specific bit-widths. + * + * \{ */ + +/* Compiler optimizes out redundant checks. */ +#ifdef __GNUC__ +# pragma warning(push) +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +/** + * Don't use `bool` return type, so -1 can be used as an error value. + */ +int PyC_Long_AsBool(PyObject *value) +{ + int test = _PyLong_AsInt(value); + if (UNLIKELY((uint)test > 1)) { + PyErr_SetString(PyExc_TypeError, + "Python number not a bool (0/1)"); + return -1; + } + return test; +} + +int8_t PyC_Long_AsI8(PyObject *value) +{ + int test = _PyLong_AsInt(value); + if (UNLIKELY(test < INT8_MIN || test > INT8_MAX)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int8"); + return -1; + } + return (int8_t)test; +} + +int16_t PyC_Long_AsI16(PyObject *value) +{ + int test = _PyLong_AsInt(value); + if (UNLIKELY(test < INT16_MIN || test > INT16_MAX)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int16"); + return -1; + } + return (int16_t)test; +} + +/* Inlined in header: + * PyC_Long_AsI32 + * PyC_Long_AsI64 + */ + +uint8_t PyC_Long_AsU8(PyObject *value) +{ + ulong test = PyLong_AsUnsignedLong(value); + if (UNLIKELY(test > UINT8_MAX)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint8"); + return (uint8_t)-1; + } + return (uint8_t)test; +} + +uint16_t PyC_Long_AsU16(PyObject *value) +{ + ulong test = PyLong_AsUnsignedLong(value); + if (UNLIKELY(test > UINT16_MAX)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint16"); + return (uint16_t)-1; + } + return (uint16_t)test; +} + +uint32_t PyC_Long_AsU32(PyObject *value) +{ + ulong test = PyLong_AsUnsignedLong(value); + if (UNLIKELY(test > UINT32_MAX)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint32"); + return (uint32_t)-1; + } + return (uint32_t)test; +} + +/* Inlined in header: + * PyC_Long_AsU64 + */ + +#ifdef __GNUC__ +# pragma warning(pop) +#endif + +/** \} */ diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index 3f89e1d82a0..327d4e60954 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -24,10 +24,12 @@ * \ingroup pygen */ - #ifndef __PY_CAPI_UTILS_H__ #define __PY_CAPI_UTILS_H__ +#include "BLI_sys_types.h" +#include "BLI_utildefines_variadic.h" + void PyC_ObSpit(const char *name, PyObject *var); void PyC_LineSpit(void); void PyC_StackSpit(void); @@ -44,8 +46,21 @@ int PyC_AsArray_FAST( int PyC_AsArray( void *array, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, const bool is_double, const char *error_prefix); -PyObject * PyC_FromArray(const void *array, int length, const PyTypeObject *type, - const bool is_double, const char *error_prefix); + +PyObject *PyC_Tuple_PackArray_F32(const float *array, uint len); +PyObject *PyC_Tuple_PackArray_I32(const int *array, uint len); +PyObject *PyC_Tuple_PackArray_I32FromBool(const int *array, uint len); +PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len); + +#define PyC_Tuple_Pack_F32(...) \ + PyC_Tuple_PackArray_F32(((const float []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__)) +#define PyC_Tuple_Pack_I32(...) \ + PyC_Tuple_PackArray_I32(((const int []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__)) +#define PyC_Tuple_Pack_I32FromBool(...) \ + PyC_Tuple_PackArray_I32FromBool(((const int []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__)) +#define PyC_Tuple_Pack_Bool(...) \ + PyC_Tuple_PackArray_Bool(((const bool []){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__)) + void PyC_Tuple_Fill(PyObject *tuple, PyObject *value); void PyC_List_Fill(PyObject *list, PyObject *value); @@ -85,4 +100,26 @@ bool PyC_RunString_AsString(const char *expr, const char *filename, char **r_val int PyC_ParseBool(PyObject *o, void *p); + +/* Integer parsing (with overflow checks), -1 on error. */ +int PyC_Long_AsBool(PyObject *value); +int8_t PyC_Long_AsI8(PyObject *value); +int16_t PyC_Long_AsI16(PyObject *value); +#if 0 /* inline */ +int32_t PyC_Long_AsI32(PyObject *value); +int64_t PyC_Long_AsI64(PyObject *value); +#endif + +uint8_t PyC_Long_AsU8(PyObject *value); +uint16_t PyC_Long_AsU16(PyObject *value); +uint32_t PyC_Long_AsU32(PyObject *value); +#if 0 /* inline */ +uint64_t PyC_Long_AsU64(PyObject *value); +#endif + +/* inline so type signatures match as expected */ +Py_LOCAL_INLINE(int32_t) PyC_Long_AsI32(PyObject *value) { return (int32_t)_PyLong_AsInt(value); } +Py_LOCAL_INLINE(int64_t) PyC_Long_AsI64(PyObject *value) { return (int64_t)PyLong_AsLongLong(value); } +Py_LOCAL_INLINE(uint64_t) PyC_Long_AsU64(PyObject *value) { return (uint64_t)PyLong_AsUnsignedLongLong(value); } + #endif /* __PY_CAPI_UTILS_H__ */ diff --git a/source/blender/python/generic/python_utildefines.h b/source/blender/python/generic/python_utildefines.h index f7d3e7a8b4a..2d2d19c05f5 100644 --- a/source/blender/python/generic/python_utildefines.h +++ b/source/blender/python/generic/python_utildefines.h @@ -36,16 +36,16 @@ extern "C" { PyTupleObject *op = (PyTupleObject *)op_arg; \ PyObject **ob_items = op->ob_item; \ CHECK_TYPE_ANY(op_arg, PyObject *, PyTupleObject *); \ - BLI_assert(_VA_NARGS_COUNT(__VA_ARGS__) == PyTuple_GET_SIZE(op)); \ + BLI_assert(VA_NARGS_COUNT(__VA_ARGS__) == PyTuple_GET_SIZE(op)); \ ARRAY_SET_ITEMS(ob_items, __VA_ARGS__); \ } (void)0 /* wrap Py_INCREF & return the result, * use sparingly to avoid comma operator or temp var assignment */ -BLI_INLINE PyObject *Py_INCREF_RET(PyObject *op) { Py_INCREF(op); return op; } +Py_LOCAL_INLINE(PyObject *)Py_INCREF_RET(PyObject *op) { Py_INCREF(op); return op; } /* append & transfer ownership to the list, avoids inline Py_DECREF all over (which is quite a large macro) */ -BLI_INLINE int PyList_APPEND(PyObject *op, PyObject *v) +Py_LOCAL_INLINE(int) PyList_APPEND(PyObject *op, PyObject *v) { int ret = PyList_Append(op, v); Py_DecRef(v); |