Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/python/generic/py_capi_utils.c')
-rw-r--r--source/blender/python/generic/py_capi_utils.c313
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
+
+/** \} */