diff options
Diffstat (limited to 'source/blender/python/generic/py_capi_utils.c')
-rw-r--r-- | source/blender/python/generic/py_capi_utils.c | 313 |
1 files changed, 256 insertions, 57 deletions
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 7b2d58a1268..6f265b2ae87 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -38,6 +38,8 @@ #include "python_utildefines.h" +#include "BLI_string.h" + #ifndef MATH_STANDALONE /* only for BLI_strncpy_wchar_from_utf8, should replace with py funcs but too late in release now */ #include "BLI_string_utf8.h" @@ -85,7 +87,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 +129,61 @@ 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])); - } - } + PyObject *tuple = PyTuple_New(len); + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array[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])); - } + return tuple; +} + +PyObject *PyC_Tuple_PackArray_F64(const double *array, uint len) +{ + 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 +212,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) { @@ -225,22 +236,49 @@ int PyC_ParseBool(PyObject *o, void *p) /* for debugging */ void PyC_ObSpit(const char *name, PyObject *var) { + const char *null_str = "<null>"; fprintf(stderr, "<%s> : ", name); if (var == NULL) { - fprintf(stderr, "<NIL>"); + fprintf(stderr, "%s\n", null_str); } else { PyObject_Print(var, stderr, 0); - fprintf(stderr, " ref:%d ", (int)var->ob_refcnt); - fprintf(stderr, " ptr:%p", (void *)var); - - fprintf(stderr, " type:"); - if (Py_TYPE(var)) - fprintf(stderr, "%s", Py_TYPE(var)->tp_name); - else - fprintf(stderr, "<NIL>"); + const PyTypeObject *type = Py_TYPE(var); + fprintf(stderr, + " ref:%d, ptr:%p, type: %s\n", + (int)var->ob_refcnt, (void *)var, type ? type->tp_name : null_str); + } +} + +/** + * A version of #PyC_ObSpit that writes into a string (and doesn't take a name argument). + * Use for logging. + */ +void PyC_ObSpitStr(char *result, size_t result_len, PyObject *var) +{ + /* No name, creator of string can manage that. */ + const char *null_str = "<null>"; + if (var == NULL) { + BLI_snprintf(result, result_len, "%s", null_str); + } + else { + const PyTypeObject *type = Py_TYPE(var); + PyObject *var_str = PyObject_Repr(var); + if (var_str == NULL) { + /* We could print error here, but this may be used for generating errors - so don't for now. */ + PyErr_Clear(); + } + BLI_snprintf( + result, result_len, + " ref=%d, ptr=%p, type=%s, value=%.200s", + (int)var->ob_refcnt, + (void *)var, + type ? type->tp_name : null_str, + var_str ? _PyUnicode_AsString(var_str) : "<error>"); + if (var_str != NULL) { + Py_DECREF(var_str); + } } - fprintf(stderr, "\n"); } void PyC_LineSpit(void) @@ -300,7 +338,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 */ @@ -405,6 +450,25 @@ PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *for return NULL; } +/** + * Use for Python callbacks run directly from C, + * when we can't use normal methods of raising exceptions. + */ +void PyC_Err_PrintWithFunc(PyObject *py_func) +{ + /* since we return to C code we can't leave the error */ + PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(py_func); + PyErr_Print(); + PyErr_Clear(); + + /* use py style error */ + fprintf(stderr, "File \"%s\", line %d, in %s\n", + _PyUnicode_AsString(f_code->co_filename), + f_code->co_firstlineno, + _PyUnicode_AsString(((PyFunctionObject *)py_func)->func_name) + ); +} + /* returns the exception string as a new PyUnicode object, depends on external traceback module */ #if 0 @@ -918,11 +982,11 @@ char *PyC_FlagSet_AsString(PyC_FlagSet *item) return cstring; } -int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *value) +int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *r_value) { for ( ; item->identifier; item++) { if (STREQ(item->identifier, identifier)) { - *value = item->value; + *r_value = item->value; return 1; } } @@ -930,9 +994,9 @@ int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int * return 0; } -int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, const char *identifier, int *value, const char *error_prefix) +int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, const char *identifier, int *r_value, const char *error_prefix) { - if (PyC_FlagSet_ValueFromID_int(item, identifier, value) == 0) { + if (PyC_FlagSet_ValueFromID_int(item, identifier, r_value) == 0) { const char *enum_str = PyC_FlagSet_AsString(item); PyErr_Format(PyExc_ValueError, "%s: '%.200s' not found in (%s)", @@ -1006,7 +1070,7 @@ PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag) * * \note it is caller's responsibility to acquire & release GIL! */ -bool PyC_RunString_AsNumber(const char *expr, double *value, const char *filename) +bool PyC_RunString_AsNumber(const char *expr, const char *filename, double *r_value) { PyObject *py_dict, *mod, *retval; bool ok = true; @@ -1058,10 +1122,10 @@ bool PyC_RunString_AsNumber(const char *expr, double *value, const char *filenam ok = false; } else if (!isfinite(val)) { - *value = 0.0; + *r_value = 0.0; } else { - *value = val; + *r_value = val; } } @@ -1070,4 +1134,139 @@ bool PyC_RunString_AsNumber(const char *expr, double *value, const char *filenam return ok; } +bool PyC_RunString_AsString(const char *expr, const char *filename, char **r_value) +{ + PyObject *py_dict, *retval; + bool ok = true; + PyObject *main_mod = NULL; + + PyC_MainModule_Backup(&main_mod); + + py_dict = PyC_DefaultNameSpace(filename); + + retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict); + + if (retval == NULL) { + ok = false; + } + else { + const char *val; + Py_ssize_t val_len; + + val = _PyUnicode_AsStringAndSize(retval, &val_len); + if (val == NULL && PyErr_Occurred()) { + ok = false; + } + else { + char *val_alloc = MEM_mallocN(val_len + 1, __func__); + memcpy(val_alloc, val, val_len + 1); + *r_value = val_alloc; + } + + Py_DECREF(retval); + } + + PyC_MainModule_Restore(main_mod); + + return ok; +} + #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 + +/** \} */ |