/**************************************************************** * A thin wrapper around ppembed utilities, plus a * custom init routine that creates and exports the * C name interface object for use in Python scripts. * Also creates and manages a dummy module "runpy" as * a namespace for running code strings and objects. * * Note that Python code uses a global called "cvar", * which is assigned to be a type instance object on start * up here, rather than directly refering to the importable * cinterface.so module object. We could use a simple module * object for the cvar interface if all C names we want to * expose are known ahead of time; here, we use a type instance * object for the names interface instead, to allow overloading * of the getattr/setattr type instance handlers, which can * either access the actual C vars directly, or call more * generic C lookup routines. This type-based interface lets * us get/set C vars using qualification syntax in Python * (cvar.name, cvar.name = val). With a simple module * interface, we're limited to function calls (cmod.get('name'), * cmod.getname(), etc.), since there's no notion of operator * overloading. The C extension module can also define * other functions besides the name interface type instance * constructor function; for instance, C can exports calls to * set a result instead of fetching presumably-set globals. *****************************************************************/ #include #include #include "runpy.h" char *RunPyError = NULL; static char* mynamespace = "runpy"; /******************************************************** * Call me first, to: * - make a new namespace for running strings, * - assign global "cvar" * to new cinterface.Cvar() type instance object, * - assign global "cinterface" * to preimported cinterface module object; * along the way, this initializes Python libs, and * dynamically loads the cinterface.so extension module * file (it's first imported here, by Python API calls); * roughly the same as doing this in every Python script: * >>> import cinterface * >>> cvar = cinterface.Cvar() * but note that this code will only work in Python code * run from a C program that defines CnameMapTable (and * CnameMessage)--the cinterface module must be able to * link to this C name when it is first imported; ********************************************************/ int RunPyInitialize() { int stat; PyObject *cinterfaceModule = NULL; PyObject *cvarTypeInstance = NULL; /* fetch module, call cinterface.Cvar(); imports the .so */ /* can't call C funcs in cinterface.so directly from C-- */ /* not yet loaded, so we need to go through the Python API */ cinterfaceModule = PP_Load_Module("cinterface"); if (cinterfaceModule == NULL) { RunPyError = "Can't import module cinterface"; return -1; } stat = PP_Run_Function("cinterface", "Cvar", "O", &cvarTypeInstance, "()"); if (stat != 0) { RunPyError = "Can't call cinterface.Cvar()"; return -1; } /* assign global names, visible to embedded code */ stat = PP_Make_Dummy_Module(mynamespace); if (stat != 0) { RunPyError = "Can't make module namespace"; return -1; } stat = PP_Set_Global(mynamespace, "cvar", "O", cvarTypeInstance); if (stat != 0) { RunPyError = "Can't set 'cvar' name"; return -1; } stat = PP_Set_Global(mynamespace, "cinterface", "O", cinterfaceModule); if (stat != 0) { RunPyError = "Can't set 'cinterface' name"; return -1; } return 0; /* success */ } /******************************************************** * Run passed-in Python code string, which can use both * the preimported "cinterface" module, and the prebuilt * "cvar" type instance object; the code is assumed to * do its work and communictae any results by calling * cinterface module functions, and/or by assigning to * attributes of the cvar type instance object; ********************************************************/ int RunPyExecCodeString(char *code) { int stat; stat = PP_Run_Codestr(PP_STATEMENT, code, mynamespace, "", NULL); if (stat != 0) { RunPyError = "Can't run code string"; return -1; } return 0; /* success */ } /***************************************************** * For efficiency, allow character strings of Python * code to be precompiled to bytecode, and run later *****************************************************/ PyObject *RunPyCompileCodeString(char *codestr) { PyObject *result = PP_Compile_Codestr(PP_STATEMENT, codestr); if (result == NULL) { RunPyError = "Can't compile code string"; } return result; /* caller must xdecref */ } int RunPyExecBytecode(PyObject *codeobj) { int stat = PP_Run_Bytecode(codeobj, mynamespace, "", NULL); if (stat != 0) { RunPyError = "Can't run code object"; return -1; } return 0; /* success */ } /***************************************************** * Print extra info about the most recent Python * exception, after an error occurs; this may provide * extra details, beyond the general RunPyError message; *****************************************************/ void RunPyPrintPythonErrorInfo() { PP_Fetch_Error_Text(); printf("Exception type: %s\n", PP_last_error_type); printf("Exception data: %s\n", PP_last_error_info); printf("Exception trace: %s\n", PP_last_error_trace); }