#include "config.h" #ifdef HAVE_PYTHON # include # if PY_MAJOR_VERSION >= 3 # include # endif /* PY_MAJOR_VERSION >= 3 */ #endif /* HAVE_PYTHON */ #include #include #include #include #include #include "pci.h" #include "debug.h" #include "pcilib.h" #include "py.h" #include "error.h" #ifdef HAVE_PYTHON # define PCILIB_PYTHON_WRAPPER "pcipywrap" typedef struct pcilib_script_s pcilib_script_t; struct pcilib_script_s { const char *name; /**< Script name */ PyObject *module; /**< PyModule object, contains script enviroment */ UT_hash_handle hh; /**< hash */ }; struct pcilib_py_s { int finalyze; /**< Indicates, that we are initialized from wrapper and should not destroy Python resources in destructor */ PyObject *main_module; /**< Main interpreter */ PyObject *pywrap_module; /**< Pcilib python wrapper */ PyObject *threading_module; /**< Threading module */ PyObject *global_dict; /**< Dictionary of main interpreter */ PyObject *pcilib_pywrap; /**< pcilib wrapper context */ pcilib_script_t *script_hash; /**< Hash with loaded scripts */ # if PY_MAJOR_VERSION >= 3 int status; /**< Indicates if python was initialized successfuly (0) or error have occured */ pthread_t pth; /**< Helper thread for Python initialization */ pthread_cond_t cond; /**< Condition informing about initialization success and request for clean-up */ pthread_mutex_t lock; /**< Condition lock */ // PyInterpreterState *istate; // PyThreadState *tstate; # endif /* PY_MAJOR_VERSION > 3 */ }; #endif /* HAVE_PYTHON */ void pcilib_log_python_error(const char *file, int line, pcilib_log_flags_t flags, pcilib_log_priority_t prio, const char *msg, ...) { va_list va; const char *type = NULL; const char *val = NULL; #ifdef HAVE_PYTHON PyGILState_STATE gstate; PyObject *pytype = NULL; PyObject *pyval = NULL; PyObject *pystr = NULL; PyObject *pytraceback = NULL; gstate = PyGILState_Ensure(); if (PyErr_Occurred()) { PyErr_Fetch(&pytype, &pyval, &pytraceback); PyErr_NormalizeException(&pytype, &pyval, &pytraceback); if (pyval) pystr = PyObject_Str(pyval); # if PY_MAJOR_VERSION >= 3 if (pytype) { if (PyUnicode_Check(pytype)) type = PyUnicode_AsUTF8(pytype); else type = PyExceptionClass_Name(pytype); } if (pystr) { val = PyUnicode_AsUTF8(pystr); } # else /* PY_MAJOR_VERSION >= 3 */ if (pytype) { if (PyString_Check(pytype)) type = PyString_AsString(pytype); else type = PyExceptionClass_Name(pytype); } if (pystr) { val = PyString_AsString(pystr); } # endif /*PY_MAJOR_VERSION >= 3*/ } PyGILState_Release(gstate); #endif /* HAVE_PYTHON */ va_start(va, msg); if (type) { char *str; size_t len = 32; if (msg) len += strlen(msg); if (type) len += strlen(type); if (val) len += strlen(val); str = alloca(len * sizeof(char)); if (str) { if (msg&&val) sprintf(str, "%s <%s: %s>", msg, type, val); else if (msg) sprintf(str, "%s <%s>", msg, type); else if (val) sprintf(str, "Python error %s: %s", type, val); else sprintf(str, "Python error %s", type); pcilib_log_vmessage(file, line, flags, prio, str, va); } } else { pcilib_log_vmessage(file, line, flags, prio, msg, va); } va_end(va); #ifdef HAVE_PYTHON if (pystr) Py_DECREF(pystr); if (pytype) Py_DECREF(pytype); if (pyval) Py_DECREF(pyval); if (pytraceback) Py_DECREF(pytraceback); #endif /* HAVE_PYTHON */ } #ifdef HAVE_PYTHON static int pcilib_py_load_default_modules(pcilib_t *ctx) { PyGILState_STATE gstate = PyGILState_Ensure(); ctx->py->main_module = PyImport_AddModule("__parser__"); if (!ctx->py->main_module) { PyGILState_Release(gstate); pcilib_python_warning("Error importing python parser"); return PCILIB_ERROR_FAILED; } ctx->py->global_dict = PyModule_GetDict(ctx->py->main_module); if (!ctx->py->global_dict) { PyGILState_Release(gstate); pcilib_python_warning("Error locating global python dictionary"); return PCILIB_ERROR_FAILED; } ctx->py->pywrap_module = PyImport_ImportModule(PCILIB_PYTHON_WRAPPER); if (!ctx->py->pywrap_module) { PyGILState_Release(gstate); pcilib_python_warning("Error importing pcilib python wrapper"); return PCILIB_ERROR_FAILED; } /** * We need to load threading module here, otherwise if any of the scripts * will use threading, on cleanup Python3 will complain: * Exception KeyError: KeyError(140702199305984,) in ignored * The idea to load threading module is inspired by * http://stackoverflow.com/questions/8774958/keyerror-in-module-threading-after-a-successful-py-test-run */ ctx->py->threading_module = PyImport_ImportModule("threading"); if (!ctx->py->threading_module) { PyGILState_Release(gstate); pcilib_python_warning("Error importing threading python module"); return PCILIB_ERROR_FAILED; } PyObject *mod_name = PyUnicode_FromString(PCILIB_PYTHON_WRAPPER); PyObject *pyctx = PyCapsule_New(ctx, "pcilib", NULL); ctx->py->pcilib_pywrap = PyObject_CallMethodObjArgs(ctx->py->pywrap_module, mod_name, pyctx, NULL); Py_XDECREF(pyctx); Py_XDECREF(mod_name); if (!ctx->py->pcilib_pywrap) { PyGILState_Release(gstate); pcilib_python_warning("Error initializing python wrapper"); return PCILIB_ERROR_FAILED; } PyGILState_Release(gstate); return 0; } static void pcilib_py_clean_default_modules(pcilib_t *ctx) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); if (ctx->py->pcilib_pywrap) Py_DECREF(ctx->py->pcilib_pywrap); if (ctx->py->threading_module) Py_DECREF(ctx->py->threading_module); if (ctx->py->pywrap_module) Py_DECREF(ctx->py->pywrap_module); // Crashes Python2 // if (ctx->py->main_module) Py_DECREF(ctx->py->main_module); PyGILState_Release(gstate); } # if PY_MAJOR_VERSION >= 3 /** * Python3 specially treats the main thread intializing Python. It crashes if * the Lock is released and any Python code is executed under the GIL compaling * that GIL is not locked. Python3 assumes that the main thread most of the time * holds the Lock, only shortly giving it away to other threads and re-obtaining * it hereafter. This is not possible to do with GILs, but instead (probably) * PyEval_{Save,Restore}Thread() should be used. On other hand, the other threads * are working fine with GILs. This makes things complicated as we need to know * if we are running in main thread or not. * To simplify matters, during initalization we start a new thread which will * performa actual initialization of Python and, hence, act as main thread. * We only intialize here. No python code is executed afterwards. So we don't * need to care about special locking mechanisms in main thread. Instead all * our user threads can use GILs normally. * See more details here: * http://stackoverflow.com/questions/24499393/cpython-locking-the-gil-in-the-main-thread * http://stackoverflow.com/questions/15470367/pyeval-initthreads-in-python-3-how-when-to-call-it-the-saga-continues-ad-naus */ static void *pcilib_py_run_init_thread(void *arg) { PyThreadState *state; pcilib_t *ctx = (pcilib_t*)arg; pcilib_py_t *py = ctx->py; Py_Initialize(); PyEval_InitThreads(); // state = PyThreadState_Get(); // py->istate = state->interp; py->status = pcilib_py_load_default_modules(ctx); state = PyEval_SaveThread(); // Ensure that main thread waiting for our signal pthread_mutex_lock(&(py->lock)); // Inform the parent thread that initialization is finished pthread_cond_signal(&(py->cond)); // Wait untill cleanup is requested pthread_cond_wait(&(py->cond), &(py->lock)); pthread_mutex_unlock(&(py->lock)); PyEval_RestoreThread(state); pcilib_py_clean_default_modules(ctx); Py_Finalize(); return NULL; } # endif /* PY_MAJOR_VERSION < 3 */ #endif /* HAVE_PYTHON */ int pcilib_init_py(pcilib_t *ctx) { #ifdef HAVE_PYTHON int err = 0; ctx->py = (pcilib_py_t*)malloc(sizeof(pcilib_py_t)); if (!ctx->py) return PCILIB_ERROR_MEMORY; memset(ctx->py, 0, sizeof(pcilib_py_t)); if (!Py_IsInitialized()) { # if PY_MAJOR_VERSION < 3 Py_Initialize(); // Since python is being initializing from c programm, it needs to initialize threads to work properly with c threads PyEval_InitThreads(); PyEval_ReleaseLock(); err = pcilib_py_load_default_modules(ctx); # else /* PY_MAJOR_VERSION < 3 */ int err = pthread_mutex_init(&(ctx->py->lock), NULL); if (err) return PCILIB_ERROR_FAILED; err = pthread_cond_init(&(ctx->py->cond), NULL); if (err) { pthread_mutex_destroy(&(ctx->py->lock)); return PCILIB_ERROR_FAILED; } err = pthread_mutex_lock(&(ctx->py->lock)); if (err) { pthread_cond_destroy(&(ctx->py->cond)); pthread_mutex_destroy(&(ctx->py->lock)); return PCILIB_ERROR_FAILED; } // Create initalizer thread and wait until it releases the Lock err = pthread_create(&(ctx->py->pth), NULL, pcilib_py_run_init_thread, (void*)ctx); if (err) { pthread_mutex_unlock(&(ctx->py->lock)); pthread_cond_destroy(&(ctx->py->cond)); pthread_mutex_destroy(&(ctx->py->lock)); return PCILIB_ERROR_FAILED; } // Wait until initialized and keep the lock afterwards until free executed pthread_cond_wait(&(ctx->py->cond), &(ctx->py->lock)); err = ctx->py->status; // ctx->py->tstate = PyThreadState_New(ctx->py->istate); # endif /* PY_MAJOR_VERSION < 3 */ ctx->py->finalyze = 1; } else { err = pcilib_py_load_default_modules(ctx); } if (err) { pcilib_free_py(ctx); return err; } #endif /* HAVE_PYTHON */ return 0; } void pcilib_free_py(pcilib_t *ctx) { #ifdef HAVE_PYTHON if (ctx->py) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); if (ctx->py->script_hash) { pcilib_script_t *script, *script_tmp; HASH_ITER(hh, ctx->py->script_hash, script, script_tmp) { Py_DECREF(script->module); HASH_DEL(ctx->py->script_hash, script); free(script); } ctx->py->script_hash = NULL; } PyGILState_Release(gstate); #if PY_MAJOR_VERSION < 3 pcilib_py_clean_default_modules(ctx); #endif /* PY_MAJOR_VERSION < 3 */ if (ctx->py->finalyze) { #if PY_MAJOR_VERSION < 3 Py_Finalize(); #else /* PY_MAJOR_VERSION < 3 */ // PyThreadState_Delete(ctx->py->tstate); // singal python init thread to stop and wait it to finish pthread_cond_signal(&(ctx->py->cond)); pthread_mutex_unlock(&(ctx->py->lock)); pthread_join(ctx->py->pth, NULL); // destroy synchronization primitives pthread_cond_destroy(&(ctx->py->cond)); pthread_mutex_destroy(&(ctx->py->lock)); #endif /* PY_MAJOR_VERSION < 3 */ } free(ctx->py); ctx->py = NULL; } #endif /* HAVE_PYTHON */ } int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) { #ifdef HAVE_PYTHON int err = 0; PyObject *pypath, *pynewdir; PyObject *pydict, *pystr, *pyret = NULL; char *script_dir; if (!ctx->py) return 0; const char *model_dir = getenv("PCILIB_MODEL_DIR"); if (!model_dir) model_dir = PCILIB_MODEL_DIR; if (!dir) dir = ctx->model; if (*dir == '/') { script_dir = (char*)dir; } else { script_dir = alloca(strlen(model_dir) + strlen(dir) + 2); if (!script_dir) return PCILIB_ERROR_MEMORY; sprintf(script_dir, "%s/%s", model_dir, dir); } PyGILState_STATE gstate = PyGILState_Ensure(); pypath = PySys_GetObject("path"); if (!pypath) { PyGILState_Release(gstate); pcilib_python_warning("Can't get python path"); return PCILIB_ERROR_FAILED; } pynewdir = PyUnicode_FromString(script_dir); if (!pynewdir) { PyGILState_Release(gstate); pcilib_python_warning("Can't create python string"); return PCILIB_ERROR_MEMORY; } // Checking if the directory already in the path? pydict = PyDict_New(); if (pydict) { pystr = PyUnicode_FromString("cur"); if (pystr) { PyDict_SetItem(pydict, pystr, pynewdir); Py_DECREF(pystr); } pystr = PyUnicode_FromString("path"); if (pystr) { PyDict_SetItem(pydict, pystr, pypath); Py_DECREF(pystr); } pyret = PyRun_String("cur in path", Py_eval_input, ctx->py->global_dict, pydict); Py_DECREF(pydict); } if ((pyret == Py_False)&&(PyList_Append(pypath, pynewdir) == -1)) err = PCILIB_ERROR_FAILED; if (pyret) Py_DECREF(pyret); Py_DECREF(pynewdir); PyGILState_Release(gstate); if (err) { pcilib_python_warning("Can't add directory (%s) to python path", script_dir); return err; } #endif /* HAVE_PYTHON */ return 0; } int pcilib_py_load_script(pcilib_t *ctx, const char *script_name) { #ifdef HAVE_PYTHON PyObject* pymodule; pcilib_script_t *module = NULL; PyGILState_STATE gstate; if (!ctx->py) return 0; char *module_name = strdupa(script_name); if (!module_name) return PCILIB_ERROR_MEMORY; char *py = strrchr(module_name, '.'); if ((!py)||(strcasecmp(py, ".py"))) { pcilib_error("Invalid script name (%s) is specified", script_name); return PCILIB_ERROR_INVALID_ARGUMENT; } *py = 0; HASH_FIND_STR(ctx->py->script_hash, script_name, module); if (module) return 0; gstate = PyGILState_Ensure(); pymodule = PyImport_ImportModule(module_name); if (!pymodule) { PyGILState_Release(gstate); pcilib_python_error("Error importing script (%s)", script_name); return PCILIB_ERROR_FAILED; } module = (pcilib_script_t*)malloc(sizeof(pcilib_script_t)); if (!module) { Py_DECREF(pymodule); PyGILState_Release(gstate); return PCILIB_ERROR_MEMORY; } PyGILState_Release(gstate); module->module = pymodule; module->name = script_name; HASH_ADD_KEYPTR(hh, ctx->py->script_hash, module->name, strlen(module->name), module); #endif /* HAVE_PYTHON */ return 0; } int pcilib_py_get_transform_script_properties(pcilib_t *ctx, const char *script_name, pcilib_access_mode_t *mode_ret) { pcilib_access_mode_t mode = 0; #ifdef HAVE_PYTHON PyObject *dict; PyObject *pystr; pcilib_script_t *module; PyGILState_STATE gstate; if (!ctx->py) { if (mode_ret) *mode_ret = mode; return 0; } HASH_FIND_STR(ctx->py->script_hash, script_name, module); if(!module) { pcilib_error("Script (%s) is not loaded yet", script_name); return PCILIB_ERROR_NOTFOUND; } gstate = PyGILState_Ensure(); dict = PyModule_GetDict(module->module); if (!dict) { PyGILState_Release(gstate); pcilib_python_error("Error getting dictionary for script (%s)", script_name); return PCILIB_ERROR_FAILED; } pystr = PyUnicode_FromString("read_from_register"); if (pystr) { if (PyDict_Contains(dict, pystr)) mode |= PCILIB_ACCESS_R; Py_DECREF(pystr); } pystr = PyUnicode_FromString("write_to_register"); if (pystr) { if (PyDict_Contains(dict, pystr)) mode |= PCILIB_ACCESS_W; Py_DECREF(pystr); } PyGILState_Release(gstate); #endif /* HAVE_PYTHON */ // pcilib_py_load_default_modules(py->ctx); if (mode_ret) *mode_ret = mode; return 0; } pcilib_py_object *pcilib_get_value_as_pyobject(pcilib_t* ctx, pcilib_value_t *val, int *ret) { #ifdef HAVE_PYTHON int err = 0; PyObject *res = NULL; PyGILState_STATE gstate; long ival; double fval; if (!ctx->py) return NULL; gstate = PyGILState_Ensure(); switch(val->type) { case PCILIB_TYPE_LONG: ival = pcilib_get_value_as_int(ctx, val, &err); if (!err) res = (PyObject*)PyLong_FromLong(ival); break; case PCILIB_TYPE_DOUBLE: fval = pcilib_get_value_as_float(ctx, val, &err); if (!err) res = (PyObject*)PyFloat_FromDouble(fval); break; default: PyGILState_Release(gstate); pcilib_error("Can't convert pcilib value of type (%lu) to PyObject", val->type); if (ret) *ret = PCILIB_ERROR_NOTSUPPORTED; return NULL; } PyGILState_Release(gstate); if (err) { if (ret) *ret = err; return NULL; } else if (!res) { if (ret) *ret = PCILIB_ERROR_MEMORY; return res; } if (ret) *ret = 0; return res; #else /* HAVE_PYTHON */ pcilib_error("Python is not supported"); return NULL; #endif /* HAVE_PYTHON */ } int pcilib_set_value_from_pyobject(pcilib_t* ctx, pcilib_value_t *val, pcilib_py_object *pval) { #ifdef HAVE_PYTHON int err = 0; PyObject *pyval = (PyObject*)pval; PyGILState_STATE gstate; if (!ctx->py) return PCILIB_ERROR_NOTINITIALIZED; gstate = PyGILState_Ensure(); if (PyLong_Check(pyval)) { err = pcilib_set_value_from_int(ctx, val, PyLong_AsLong(pyval)); } else if (PyFloat_Check(pyval)) { err = pcilib_set_value_from_float(ctx, val, PyFloat_AsDouble(pyval)); #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(pyval)) { err = pcilib_set_value_from_int(ctx, val, PyInt_AsLong(pyval)); } else if (PyString_Check(pyval)) { err = pcilib_set_value_from_string(ctx, val, PyString_AsString(pyval)); } else if (PyUnicode_Check(pyval)) { PyObject *buf = PyUnicode_AsASCIIString(pyval); if (buf) { err = pcilib_set_value_from_string(ctx, val, PyString_AsString(buf)); Py_DecRef(buf); } else { err = PCILIB_ERROR_FAILED; } #else /* PY_MAJOR_VERSION < 3 */ } else if (PyUnicode_Check(pyval)) { err = pcilib_set_value_from_string(ctx, val, PyUnicode_AsUTF8(pyval)); #endif /* PY_MAJOR_VERSION < 3 */ } else { PyGILState_Release(gstate); pcilib_error("Can't convert PyObject to polymorphic pcilib value"); return PCILIB_ERROR_NOTSUPPORTED; } PyGILState_Release(gstate); return err; #else /* HAVE_PYTHON */ pcilib_error("Python is not supported"); return PCILIB_ERROR_NOTSUPPORTED; #endif /* HAVE_PYTHON */ } #ifdef HAVE_PYTHON static char *pcilib_py_parse_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *value) { int i; int err = 0; pcilib_value_t val = {0}; pcilib_register_value_t regval; char save; char *reg, *cur; size_t offset = 0; size_t size; char *src; char *dst; size_t max_repl = 2 + 2 * sizeof(pcilib_value_t); // the text representation of largest integer if (value) { err = pcilib_copy_value(ctx, &val, value); if (err) return NULL; err = pcilib_convert_value_type(ctx, &val, PCILIB_TYPE_STRING); if (err) return NULL; if (strlen(val.sval) > max_repl) max_repl = strlen(val.sval); } size = ((max_repl + 1) / 3 + 1) * strlen(codestr); // minimum register length is 3 symbols ($a + delimiter space), it is replaces with (max_repl+1) symbols src = strdup(codestr); dst = (char*)malloc(size); // allocating maximum required space if ((!src)||(!dst)) { if (src) free(src); if (dst) free(dst); pcilib_error("Failed to allocate memory for string formulas"); return NULL; } cur = src; reg = strchr(src, '$'); while (reg) { strcpy(dst + offset, cur); offset += reg - cur; // find the end of the register name reg++; if (*reg == '{') { reg++; for (i = 0; (reg[i])&&(reg[i] != '}'); i++); if (!reg[i]) { pcilib_error("Python formula (%s) contains unterminated variable reference", codestr); err = PCILIB_ERROR_INVALID_DATA; break; } } else { for (i = 0; isalnum(reg[i])||(reg[i] == '_'); i++); } save = reg[i]; reg[i] = 0; // determine replacement value if (!strcasecmp(reg, "value")) { if (!value) { pcilib_error("Python formula (%s) relies on the value of register, but it is not provided", codestr); err = PCILIB_ERROR_INVALID_REQUEST; break; } strcpy(dst + offset, val.sval); } else { if (*reg == '/') { pcilib_value_t val = {0}; err = pcilib_get_property(ctx, reg, &val); if (err) break; err = pcilib_convert_value_type(ctx, &val, PCILIB_TYPE_STRING); if (err) break; sprintf(dst + offset, "%s", val.sval); } else { err = pcilib_read_register(ctx, NULL, reg, ®val); if (err) break; sprintf(dst + offset, "0x%lx", regval); } } offset += strlen(dst + offset); if (save == '}') i++; else reg[i] = save; // Advance to the next register if any cur = reg + i; reg = strchr(cur, '$'); } strcpy(dst + offset, cur); free(src); if (err) { free(dst); return NULL; } return dst; } #endif /* HAVE_PYTHON */ int pcilib_py_eval_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *value) { #ifdef HAVE_PYTHON int err; PyGILState_STATE gstate; char *code; PyObject* obj; if (!ctx->py) return PCILIB_ERROR_NOTINITIALIZED; code = pcilib_py_parse_string(ctx, codestr, value); if (!code) { pcilib_error("Failed to parse registers in the code: %s", codestr); return PCILIB_ERROR_FAILED; } gstate = PyGILState_Ensure(); obj = PyRun_String(code, Py_eval_input, ctx->py->global_dict, ctx->py->global_dict); PyGILState_Release(gstate); if (!obj) { pcilib_error("Failed to run the Python code: %s", code); free(code); return PCILIB_ERROR_FAILED; } pcilib_debug(VIEWS, "Evaluating a Python string \'%s\' to %lf=\'%s\'", codestr, PyFloat_AsDouble(obj), code); err = pcilib_set_value_from_float(ctx, value, PyFloat_AsDouble(obj)); Py_DECREF(obj); free(code); return err; #else /* HAVE_PYTHON */ pcilib_error("Current build not support python."); return PCILIB_ERROR_NOTAVAILABLE; #endif /* HAVE_PYTHON */ } int pcilib_py_eval_func(pcilib_t *ctx, const char *script_name, const char *func_name, pcilib_value_t *val) { #ifdef HAVE_PYTHON int err = 0; PyObject *pyfunc; PyObject *pyval = NULL, *pyret; pcilib_script_t *module = NULL; if (!ctx->py) return PCILIB_ERROR_NOTINITIALIZED; HASH_FIND_STR(ctx->py->script_hash, script_name, module); if (!module) { pcilib_error("Script (%s) is not loaded", script_name); return PCILIB_ERROR_NOTFOUND; } if (val) { pyval = pcilib_get_value_as_pyobject(ctx, val, &err); if (err) return err; } PyGILState_STATE gstate = PyGILState_Ensure(); pyfunc = PyUnicode_FromString(func_name); if (!pyfunc) { if (pyval) Py_DECREF(pyval); PyGILState_Release(gstate); return PCILIB_ERROR_MEMORY; } pyret = PyObject_CallMethodObjArgs(module->module, pyfunc, ctx->py->pcilib_pywrap, pyval, NULL); Py_DECREF(pyfunc); Py_DECREF(pyval); if (!pyret) { PyGILState_Release(gstate); pcilib_python_error("Error executing function (%s) of python script (%s)", func_name, script_name); return PCILIB_ERROR_FAILED; } if ((val)&&(pyret != Py_None)) err = pcilib_set_value_from_pyobject(ctx, val, pyret); Py_DECREF(pyret); PyGILState_Release(gstate); return err; #else /* HAVE_PYTHON */ pcilib_error("Python is not supported"); return PCILIB_ERROR_NOTSUPPORTED; #endif /* HAVE_PYTHON */ }