/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/python/generic/py_capi_utils.c * \ingroup pygen * * Extend upon CPython's API, filling in some gaps, these functions use PyC_ * prefix to distinguish them apart from CPython. * * \note * This module should only depend on CPython, however it currently uses * BLI_string_utf8() for unicode conversion. */ #include #include #include "BLI_utildefines.h" /* for bool */ #include "py_capi_utils.h" /* only for BLI_strncpy_wchar_from_utf8, should replace with py funcs but too late in release now */ #include "BLI_string_utf8.h" #ifdef _WIN32 #include "BLI_path_util.h" /* BLI_setenv() */ #include "BLI_math_base.h" /* finite() */ #endif /* array utility function */ int PyC_AsArray(void *array, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, const bool is_double, const char *error_prefix) { PyObject *value_fast; Py_ssize_t value_len; Py_ssize_t i; if (!(value_fast = PySequence_Fast(value, error_prefix))) { return -1; } value_len = PySequence_Fast_GET_SIZE(value_fast); if (value_len != length) { Py_DECREF(value); PyErr_Format(PyExc_TypeError, "%.200s: invalid sequence length. expected %d, got %d", error_prefix, length, value_len); return -1; } /* for each type */ if (type == &PyFloat_Type) { if (is_double) { double *array_double = array; for (i = 0; i < length; i++) { array_double[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i)); } } else { float *array_float = array; for (i = 0; i < length; i++) { array_float[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i)); } } } else if (type == &PyLong_Type) { /* 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(PySequence_Fast_GET_ITEM(value_fast, i)); } } else if (type == &PyBool_Type) { int *array_bool = array; for (i = 0; i < length; i++) { array_bool[i] = (PyLong_AsLong(PySequence_Fast_GET_ITEM(value_fast, i)) != 0); } } else { Py_DECREF(value_fast); PyErr_Format(PyExc_TypeError, "%s: internal error %s is invalid", error_prefix, type->tp_name); return -1; } Py_DECREF(value_fast); if (PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "%s: one or more items could not be used as a %s", error_prefix, type->tp_name); return -1; } return 0; } /* array utility function */ PyObject *PyC_FromArray(const void *array, int length, const PyTypeObject *type, const bool is_double, const char *error_prefix) { 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])); } } 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])); } } else { Py_DECREF(tuple); PyErr_Format(PyExc_TypeError, "%s: internal error %s is invalid", error_prefix, type->tp_name); return NULL; } return tuple; } /** * Caller needs to ensure tuple is uninitialized. * Handy for filling a typle with None for eg. */ void PyC_Tuple_Fill(PyObject *tuple, PyObject *value) { unsigned int tot = PyTuple_GET_SIZE(tuple); unsigned int i; for (i = 0; i < tot; i++) { PyTuple_SET_ITEM(tuple, i, value); Py_INCREF(value); } } /* for debugging */ void PyC_ObSpit(const char *name, PyObject *var) { fprintf(stderr, "<%s> : ", name); if (var == NULL) { fprintf(stderr, ""); } 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, ""); } fprintf(stderr, "\n"); } void PyC_LineSpit(void) { const char *filename; int lineno; /* Note, allow calling from outside python (RNA) */ if (!PyC_IsInterpreterActive()) { fprintf(stderr, "python line lookup failed, interpreter inactive\n"); return; } PyErr_Clear(); PyC_FileAndNum(&filename, &lineno); fprintf(stderr, "%s:%d\n", filename, lineno); } void PyC_StackSpit(void) { /* Note, allow calling from outside python (RNA) */ if (!PyC_IsInterpreterActive()) { fprintf(stderr, "python line lookup failed, interpreter inactive\n"); return; } else { /* lame but handy */ PyGILState_STATE gilstate = PyGILState_Ensure(); PyRun_SimpleString("__import__('traceback').print_stack()"); PyGILState_Release(gilstate); } } void PyC_FileAndNum(const char **filename, int *lineno) { PyFrameObject *frame; if (filename) *filename = NULL; if (lineno) *lineno = -1; if (!(frame = PyThreadState_GET()->frame)) { return; } /* when executing a script */ if (filename) { *filename = _PyUnicode_AsString(frame->f_code->co_filename); } /* when executing a module */ if (filename && *filename == NULL) { /* try an alternative method to get the filename - module based * references below are all borrowed (double checked) */ PyObject *mod_name = PyDict_GetItemString(PyEval_GetGlobals(), "__name__"); if (mod_name) { PyObject *mod = PyDict_GetItem(PyImport_GetModuleDict(), mod_name); if (mod) { *filename = PyModule_GetFilename(mod); } /* unlikely, fallback */ if (*filename == NULL) { *filename = _PyUnicode_AsString(mod_name); } } } if (lineno) { *lineno = PyFrame_GetLineNumber(frame); } } void PyC_FileAndNum_Safe(const char **filename, int *lineno) { if (!PyC_IsInterpreterActive()) { return; } PyC_FileAndNum(filename, lineno); } /* Would be nice if python had this built in */ PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...) { Py_ssize_t i; PyObject *item = o; const char *attr; va_list vargs; va_start(vargs, n); for (i = 0; i < n; i++) { attr = va_arg(vargs, char *); item = PyObject_GetAttrString(item, attr); if (item) Py_DECREF(item); else /* python will set the error value here */ break; } va_end(vargs); Py_XINCREF(item); /* final value has is increfed, to match PyObject_GetAttrString */ return item; } PyObject *PyC_FrozenSetFromStrings(const char **strings) { const char **str; PyObject *ret; ret = PyFrozenSet_New(NULL); for (str = strings; *str; str++) { PyObject *py_str = PyUnicode_FromString(*str); PySet_Add(ret, py_str); Py_DECREF(py_str); } return ret; } /* similar to PyErr_Format(), * * implementation - we cant actually preprend the existing exception, * because it could have _any_ arguments given to it, so instead we get its * __str__ output and raise our own exception including it. */ PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *format, ...) { PyObject *error_value_prefix; va_list args; va_start(args, format); error_value_prefix = PyUnicode_FromFormatV(format, args); /* can fail and be NULL */ va_end(args); if (PyErr_Occurred()) { PyObject *error_type, *error_value, *error_traceback; PyErr_Fetch(&error_type, &error_value, &error_traceback); PyErr_Format(exception_type_prefix, "%S, %.200s(%S)", error_value_prefix, Py_TYPE(error_value)->tp_name, error_value ); } else { PyErr_SetObject(exception_type_prefix, error_value_prefix ); } Py_XDECREF(error_value_prefix); /* dumb to always return NULL but matches PyErr_Format */ return NULL; } /* returns the exception string as a new PyUnicode object, depends on external traceback module */ #if 0 /* this version uses traceback module but somehow fails on UI errors */ PyObject *PyC_ExceptionBuffer(void) { PyObject *traceback_mod = NULL; PyObject *format_tb_func = NULL; PyObject *ret = NULL; if (!(traceback_mod = PyImport_ImportModule("traceback"))) { goto error_cleanup; } else if (!(format_tb_func = PyObject_GetAttrString(traceback_mod, "format_exc"))) { goto error_cleanup; } ret = PyObject_CallObject(format_tb_func, NULL); if (ret == Py_None) { Py_DECREF(ret); ret = NULL; } error_cleanup: /* could not import the module so print the error and close */ Py_XDECREF(traceback_mod); Py_XDECREF(format_tb_func); return ret; } #else /* verbose, non-threadsafe version */ PyObject *PyC_ExceptionBuffer(void) { PyObject *stdout_backup = PySys_GetObject("stdout"); /* borrowed */ PyObject *stderr_backup = PySys_GetObject("stderr"); /* borrowed */ PyObject *string_io = NULL; PyObject *string_io_buf = NULL; PyObject *string_io_mod = NULL; PyObject *string_io_getvalue = NULL; PyObject *error_type, *error_value, *error_traceback; if (!PyErr_Occurred()) return NULL; PyErr_Fetch(&error_type, &error_value, &error_traceback); PyErr_Clear(); /* import io * string_io = io.StringIO() */ if (!(string_io_mod = PyImport_ImportModule("io"))) { goto error_cleanup; } else if (!(string_io = PyObject_CallMethod(string_io_mod, "StringIO", NULL))) { goto error_cleanup; } else if (!(string_io_getvalue = PyObject_GetAttrString(string_io, "getvalue"))) { goto error_cleanup; } Py_INCREF(stdout_backup); // since these were borrowed we don't want them freed when replaced. Py_INCREF(stderr_backup); PySys_SetObject("stdout", string_io); // both of these are freed when restoring PySys_SetObject("stderr", string_io); PyErr_Restore(error_type, error_value, error_traceback); PyErr_Print(); /* print the error */ PyErr_Clear(); string_io_buf = PyObject_CallObject(string_io_getvalue, NULL); PySys_SetObject("stdout", stdout_backup); PySys_SetObject("stderr", stderr_backup); Py_DECREF(stdout_backup); /* now sys owns the ref again */ Py_DECREF(stderr_backup); Py_DECREF(string_io_mod); Py_DECREF(string_io_getvalue); Py_DECREF(string_io); /* free the original reference */ PyErr_Clear(); return string_io_buf; error_cleanup: /* could not import the module so print the error and close */ Py_XDECREF(string_io_mod); Py_XDECREF(string_io); PyErr_Restore(error_type, error_value, error_traceback); PyErr_Print(); /* print the error */ PyErr_Clear(); return NULL; } #endif /* string conversion, escape non-unicode chars, coerce must be set to NULL */ const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce) { const char *result; result = _PyUnicode_AsString(py_str); if (result) { /* 99% of the time this is enough but we better support non unicode * chars since blender doesnt limit this */ return result; } else { PyErr_Clear(); if (PyBytes_Check(py_str)) { return PyBytes_AS_STRING(py_str); } #ifdef WIN32 /* bug [#31856] oddly enough, Python3.2 --> 3.3 on Windows will throw an * exception here this needs to be fixed in python: * see: bugs.python.org/issue15859 */ else if (!PyUnicode_Check(py_str)) { PyErr_BadArgument(); return NULL; } #endif else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) { return PyBytes_AS_STRING(*coerce); } else { /* leave error raised from EncodeFS */ return NULL; } } } PyObject *PyC_UnicodeFromByteAndSize(const char *str, Py_ssize_t size) { PyObject *result = PyUnicode_FromStringAndSize(str, size); if (result) { /* 99% of the time this is enough but we better support non unicode * chars since blender doesnt limit this */ return result; } else { PyErr_Clear(); /* this means paths will always be accessible once converted, on all OS's */ result = PyUnicode_DecodeFSDefaultAndSize(str, size); return result; } } PyObject *PyC_UnicodeFromByte(const char *str) { return PyC_UnicodeFromByteAndSize(str, strlen(str)); } /***************************************************************************** * Description: This function creates a new Python dictionary object. * note: dict is owned by sys.modules["__main__"] module, reference is borrowed * note: important we use the dict from __main__, this is what python expects * for 'pickle' to work as well as strings like this... * >> foo = 10 * >> print(__import__("__main__").foo) * * note: this overwrites __main__ which gives problems with nested calls. * be sure to run PyC_MainModule_Backup & PyC_MainModule_Restore if there is * any chance that python is in the call stack. ****************************************************************************/ PyObject *PyC_DefaultNameSpace(const char *filename) { PyInterpreterState *interp = PyThreadState_GET()->interp; PyObject *mod_main = PyModule_New("__main__"); PyDict_SetItemString(interp->modules, "__main__", mod_main); Py_DECREF(mod_main); /* sys.modules owns now */ PyModule_AddStringConstant(mod_main, "__name__", "__main__"); if (filename) { /* __file__ mainly for nice UI'ness * note: this wont map to a real file when executing text-blocks and buttons. */ PyModule_AddObject(mod_main, "__file__", PyC_UnicodeFromByte(filename)); } PyModule_AddObject(mod_main, "__builtins__", interp->builtins); Py_INCREF(interp->builtins); /* AddObject steals a reference */ return PyModule_GetDict(mod_main); } /* restore MUST be called after this */ void PyC_MainModule_Backup(PyObject **main_mod) { PyInterpreterState *interp = PyThreadState_GET()->interp; *main_mod = PyDict_GetItemString(interp->modules, "__main__"); Py_XINCREF(*main_mod); /* don't free */ } void PyC_MainModule_Restore(PyObject *main_mod) { PyInterpreterState *interp = PyThreadState_GET()->interp; PyDict_SetItemString(interp->modules, "__main__", main_mod); Py_XDECREF(main_mod); } /* must be called before Py_Initialize, expects output of BKE_appdir_folder_id(BLENDER_PYTHON, NULL) */ void PyC_SetHomePath(const char *py_path_bundle) { if (py_path_bundle == NULL) { /* Common enough to have bundled *nix python but complain on OSX/Win */ #if defined(__APPLE__) || defined(_WIN32) fprintf(stderr, "Warning! bundled python not found and is expected on this platform. " "(if you built with CMake: 'install' target may have not been built)\n"); #endif return; } /* set the environment path */ printf("found bundled python: %s\n", py_path_bundle); #ifdef __APPLE__ /* OSX allow file/directory names to contain : character (represented as / in the Finder) * but current Python lib (release 3.1.1) doesn't handle these correctly */ if (strchr(py_path_bundle, ':')) printf("Warning : Blender application is located in a path containing : or / chars\ \nThis may make python import function fail\n"); #endif #if 0 /* disable for now [#31506] - campbell */ #ifdef _WIN32 /* cmake/MSVC debug build crashes without this, why only * in this case is unknown.. */ { /*BLI_setenv("PYTHONPATH", py_path_bundle)*/; } #endif #endif { static wchar_t py_path_bundle_wchar[1024]; /* cant use this, on linux gives bug: #23018, TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 22008 */ /* mbstowcs(py_path_bundle_wchar, py_path_bundle, FILE_MAXDIR); */ BLI_strncpy_wchar_from_utf8(py_path_bundle_wchar, py_path_bundle, ARRAY_SIZE(py_path_bundle_wchar)); Py_SetPythonHome(py_path_bundle_wchar); // printf("found python (wchar_t) '%ls'\n", py_path_bundle_wchar); } } bool PyC_IsInterpreterActive(void) { return (((PyThreadState *)_Py_atomic_load_relaxed(&_PyThreadState_Current)) != NULL); } /* Would be nice if python had this built in * See: http://wiki.blender.org/index.php/Dev:Doc/Tools/Debugging/PyFromC */ void PyC_RunQuicky(const char *filepath, int n, ...) { FILE *fp = fopen(filepath, "r"); if (fp) { PyGILState_STATE gilstate = PyGILState_Ensure(); va_list vargs; int *sizes = PyMem_MALLOC(sizeof(int) * (n / 2)); int i; PyObject *py_dict = PyC_DefaultNameSpace(filepath); PyObject *values = PyList_New(n / 2); /* namespace owns this, don't free */ PyObject *py_result, *ret; PyObject *struct_mod = PyImport_ImportModule("struct"); PyObject *calcsize = PyObject_GetAttrString(struct_mod, "calcsize"); /* struct.calcsize */ PyObject *pack = PyObject_GetAttrString(struct_mod, "pack"); /* struct.pack */ PyObject *unpack = PyObject_GetAttrString(struct_mod, "unpack"); /* struct.unpack */ Py_DECREF(struct_mod); va_start(vargs, n); for (i = 0; i * 2 < n; i++) { const char *format = va_arg(vargs, char *); void *ptr = va_arg(vargs, void *); ret = PyObject_CallFunction(calcsize, "s", format); if (ret) { sizes[i] = PyLong_AsLong(ret); Py_DECREF(ret); ret = PyObject_CallFunction(unpack, "sy#", format, (char *)ptr, sizes[i]); } if (ret == NULL) { printf("PyC_InlineRun error, line:%d\n", __LINE__); PyErr_Print(); PyErr_Clear(); PyList_SET_ITEM(values, i, Py_None); /* hold user */ Py_INCREF(Py_None); sizes[i] = 0; } else { if (PyTuple_GET_SIZE(ret) == 1) { /* convenience, convert single tuples into single values */ PyObject *tmp = PyTuple_GET_ITEM(ret, 0); Py_INCREF(tmp); Py_DECREF(ret); ret = tmp; } PyList_SET_ITEM(values, i, ret); /* hold user */ } } va_end(vargs); /* set the value so we can access it */ PyDict_SetItemString(py_dict, "values", values); py_result = PyRun_File(fp, filepath, Py_file_input, py_dict, py_dict); fclose(fp); if (py_result) { /* we could skip this but then only slice assignment would work * better not be so strict */ values = PyDict_GetItemString(py_dict, "values"); if (values && PyList_Check(values)) { /* don't use the result */ Py_DECREF(py_result); py_result = NULL; /* now get the values back */ va_start(vargs, n); for (i = 0; i * 2 < n; i++) { const char *format = va_arg(vargs, char *); void *ptr = va_arg(vargs, void *); PyObject *item; PyObject *item_new; /* prepend the string formatting and remake the tuple */ item = PyList_GET_ITEM(values, i); if (PyTuple_CheckExact(item)) { int ofs = PyTuple_GET_SIZE(item); item_new = PyTuple_New(ofs + 1); while (ofs--) { PyObject *member = PyTuple_GET_ITEM(item, ofs); PyTuple_SET_ITEM(item_new, ofs + 1, member); Py_INCREF(member); } PyTuple_SET_ITEM(item_new, 0, PyUnicode_FromString(format)); } else { item_new = Py_BuildValue("sO", format, item); } ret = PyObject_Call(pack, item_new, NULL); if (ret) { /* copy the bytes back into memory */ memcpy(ptr, PyBytes_AS_STRING(ret), sizes[i]); Py_DECREF(ret); } else { printf("PyC_InlineRun error on arg '%d', line:%d\n", i, __LINE__); PyC_ObSpit("failed converting:", item_new); PyErr_Print(); PyErr_Clear(); } Py_DECREF(item_new); } va_end(vargs); } else { printf("PyC_InlineRun error, 'values' not a list, line:%d\n", __LINE__); } } else { printf("PyC_InlineRun error line:%d\n", __LINE__); PyErr_Print(); PyErr_Clear(); } Py_DECREF(calcsize); Py_DECREF(pack); Py_DECREF(unpack); PyMem_FREE(sizes); PyGILState_Release(gilstate); } else { fprintf(stderr, "%s: '%s' missing\n", __func__, filepath); } } /* generic function to avoid depending on RNA */ void *PyC_RNA_AsPointer(PyObject *value, const char *type_name) { PyObject *as_pointer; PyObject *pointer; if (STREQ(Py_TYPE(value)->tp_name, type_name) && (as_pointer = PyObject_GetAttrString(value, "as_pointer")) != NULL && PyCallable_Check(as_pointer)) { void *result = NULL; /* must be a 'type_name' object */ pointer = PyObject_CallObject(as_pointer, NULL); Py_DECREF(as_pointer); if (!pointer) { PyErr_SetString(PyExc_SystemError, "value.as_pointer() failed"); return NULL; } result = PyLong_AsVoidPtr(pointer); Py_DECREF(pointer); if (!result) { PyErr_SetString(PyExc_SystemError, "value.as_pointer() failed"); } return result; } else { PyErr_Format(PyExc_TypeError, "expected '%.200s' type found '%.200s' instead", type_name, Py_TYPE(value)->tp_name); return NULL; } } /* PyC_FlagSet_* functions - so flags/sets can be interchanged in a generic way */ #include "BLI_dynstr.h" #include "MEM_guardedalloc.h" char *PyC_FlagSet_AsString(PyC_FlagSet *item) { DynStr *dynstr = BLI_dynstr_new(); PyC_FlagSet *e; char *cstring; for (e = item; item->identifier; item++) { BLI_dynstr_appendf(dynstr, (e == item) ? "'%s'" : ", '%s'", item->identifier); } cstring = BLI_dynstr_get_cstring(dynstr); BLI_dynstr_free(dynstr); return cstring; } int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *value) { for ( ; item->identifier; item++) { if (STREQ(item->identifier, identifier)) { *value = item->value; return 1; } } return 0; } int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, const char *identifier, int *value, const char *error_prefix) { if (PyC_FlagSet_ValueFromID_int(item, identifier, value) == 0) { const char *enum_str = PyC_FlagSet_AsString(item); PyErr_Format(PyExc_ValueError, "%s: '%.200s' not found in (%s)", error_prefix, identifier, enum_str); MEM_freeN((void *)enum_str); return -1; } return 0; } int PyC_FlagSet_ToBitfield(PyC_FlagSet *items, PyObject *value, int *r_value, const char *error_prefix) { /* set of enum items, concatenate all values with OR */ int ret, flag = 0; /* set looping */ Py_ssize_t pos = 0; Py_ssize_t hash = 0; PyObject *key; if (!PySet_Check(value)) { PyErr_Format(PyExc_TypeError, "%.200s expected a set, not %.200s", error_prefix, Py_TYPE(value)->tp_name); return -1; } *r_value = 0; while (_PySet_NextEntry(value, &pos, &key, &hash)) { const char *param = _PyUnicode_AsString(key); if (param == NULL) { PyErr_Format(PyExc_TypeError, "%.200s set must contain strings, not %.200s", error_prefix, Py_TYPE(key)->tp_name); return -1; } if (PyC_FlagSet_ValueFromID(items, param, &ret, error_prefix) < 0) { return -1; } flag |= ret; } *r_value = flag; return 0; } PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag) { PyObject *ret = PySet_New(NULL); PyObject *pystr; for ( ; items->identifier; items++) { if (items->value & flag) { pystr = PyUnicode_FromString(items->identifier); PySet_Add(ret, pystr); Py_DECREF(pystr); } } return ret; } /** * \return -1 on error, else 0 * * \note it is caller's responsibility to acquire & release GIL! */ int PyC_RunString_AsNumber(const char *expr, double *value, const char *filename) { PyObject *py_dict, *mod, *retval; int error_ret = 0; PyObject *main_mod = NULL; PyC_MainModule_Backup(&main_mod); py_dict = PyC_DefaultNameSpace(filename); mod = PyImport_ImportModule("math"); if (mod) { PyDict_Merge(py_dict, PyModule_GetDict(mod), 0); /* 0 - don't overwrite existing values */ Py_DECREF(mod); } else { /* highly unlikely but possibly */ PyErr_Print(); PyErr_Clear(); } retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict); if (retval == NULL) { error_ret = -1; } else { double val; if (PyTuple_Check(retval)) { /* Users my have typed in 10km, 2m * add up all values */ int i; val = 0.0; for (i = 0; i < PyTuple_GET_SIZE(retval); i++) { const double val_item = PyFloat_AsDouble(PyTuple_GET_ITEM(retval, i)); if (val_item == -1 && PyErr_Occurred()) { val = -1; break; } val += val_item; } } else { val = PyFloat_AsDouble(retval); } Py_DECREF(retval); if (val == -1 && PyErr_Occurred()) { error_ret = -1; } else if (!finite(val)) { *value = 0.0; } else { *value = val; } } PyC_MainModule_Restore(main_mod); return error_ret; }