622 lines
25 KiB
C
622 lines
25 KiB
C
/*
|
|
PPEMBED, VERSION 2.0
|
|
AN ENHANCED PYTHON EMBEDDED-CALL INTERFACE
|
|
|
|
Copyright 1996-2000, by Mark Lutz, and O'Reilly and Associates.
|
|
Permission to use, copy, modify, and distribute this software
|
|
for any purpose and without fee is hereby granted. This software
|
|
is provided on an as is basis, without warranties of any kind.
|
|
*/
|
|
|
|
#include <FCConfig.h>
|
|
#include "PyTools.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <compile.h>
|
|
#include <eval.h>
|
|
|
|
|
|
#if PY_VERSION_HEX <= 0x02050000
|
|
#error "Use Python2.5.x or higher"
|
|
#endif
|
|
|
|
|
|
/*****************************************************************************
|
|
* RUN EMBEDDED OBJECT METHODS, ACCESS OBJECT ATTRIBUTES
|
|
* handles attribute fetch, debugging, input/output conversions;
|
|
* there is no module to reload here: assumes a known object;
|
|
*****************************************************************************/
|
|
|
|
int
|
|
PP_Run_Method(PyObject *pobject, const char *method,
|
|
const char *resfmt, void *cresult, /* convert to c/c++ */
|
|
const char *argfmt, ... /* arg,... */ ) /* convert to python */
|
|
{
|
|
PyObject *pmeth, *pargs, *presult;
|
|
va_list argslist; /* "pobject.method(args)" */
|
|
va_start(argslist, argfmt);
|
|
|
|
Py_Initialize(); /* init if first time */
|
|
pmeth = PyObject_GetAttrString(pobject, method);
|
|
if (pmeth == NULL) /* get callable object */
|
|
return -1; /* bound method? has self */
|
|
/* handle zero args different */
|
|
// if(resfmt == 0 || strcmp(resfmt,"") == 0)
|
|
// pargs = Py_BuildValue("()");
|
|
// else
|
|
pargs = Py_VaBuildValue(argfmt, argslist); /* args: c->python */
|
|
|
|
if (pargs == NULL) {
|
|
Py_DECREF(pmeth);
|
|
return -1;
|
|
}
|
|
if (PP_DEBUG) /* debug it too? */
|
|
presult = PP_Debug_Function(pmeth, pargs);
|
|
else
|
|
presult = PyEval_CallObject(pmeth, pargs); /* run interpreter */
|
|
|
|
Py_DECREF(pmeth);
|
|
Py_DECREF(pargs);
|
|
// if(cresult != 0 && resfmt != 0)
|
|
return PP_Convert_Result(presult, resfmt, cresult); /* to C format */
|
|
// else
|
|
// return 0;
|
|
}
|
|
|
|
|
|
int
|
|
PP_Get_Member(PyObject *pobject, const char *attrname,
|
|
const char *resfmt, void *cresult) /* convert to c/c++ */
|
|
{
|
|
PyObject *pmemb; /* "pobject.attrname" */
|
|
Py_Initialize();
|
|
pmemb = PyObject_GetAttrString(pobject, attrname); /* incref'd */
|
|
return PP_Convert_Result(pmemb, resfmt, cresult); /* to C form, decrefs */
|
|
}
|
|
|
|
|
|
int
|
|
PP_Set_Member(PyObject *pobject, const char *attrname,
|
|
const char *argfmt, ... /* arg,... */ ) /* convert to python */
|
|
{
|
|
int result;
|
|
PyObject *pval;
|
|
va_list argslist; /* "pobject.attrname = v" */
|
|
va_start(argslist, argfmt);
|
|
Py_Initialize(); /* init if first time */
|
|
pval = Py_VaBuildValue(argfmt, argslist); /* input: C->Python */
|
|
if (pval == NULL)
|
|
return -1;
|
|
result = PyObject_SetAttrString(pobject, attrname, pval); /* setattr */
|
|
Py_DECREF(pval);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* RUN EMBEDDED MODULE FUNCTIONS
|
|
* handles module (re)import, debugging, input/output conversions;
|
|
* note: also useful for calling classes (and C type constructors) at the
|
|
* top-level of a module to make Python instances: use class-name (or type
|
|
* constructor function name) and 'O' result convert-code to get raw object;
|
|
* use argfmt="()" for no args, cresult='NULL' for no result (procedure);
|
|
* New tools: support for calling known Python objects directly;
|
|
*****************************************************************************/
|
|
|
|
int
|
|
PP_Run_Function(const char *modname, const char *funcname, /* load from module */
|
|
const char *resfmt, void *cresult, /* convert to c/c++ */
|
|
const char *argfmt, ... /* arg, arg... */ ) /* convert to python */
|
|
{
|
|
/* call a function or class in a module */
|
|
PyObject *func, *args, *presult;
|
|
va_list argslist;
|
|
va_start(argslist, argfmt); /* "modname.funcname(args)" */
|
|
|
|
func = PP_Load_Attribute(modname, funcname); /* may reload; incref'd */
|
|
if (func == NULL) /* func or class or C type */
|
|
return -1;
|
|
args = Py_VaBuildValue(argfmt, argslist); /* convert args to python */
|
|
if (args == NULL) { /* args incref'd */
|
|
Py_DECREF(func);
|
|
return -1;
|
|
}
|
|
if (PP_DEBUG && strcmp(modname, "pdb") != 0) /* debug this call? */
|
|
presult = PP_Debug_Function(func, args); /* run in pdb; incref'd */
|
|
else
|
|
presult = PyEval_CallObject(func, args); /* run function; incref'd */
|
|
|
|
Py_DECREF(func);
|
|
Py_DECREF(args); /* result may be None */
|
|
return PP_Convert_Result(presult, resfmt, cresult); /* convert result to C*/
|
|
}
|
|
|
|
|
|
PyObject *
|
|
PP_Debug_Function(PyObject *func, PyObject *args)
|
|
{
|
|
int oops, res;
|
|
PyObject *presult;
|
|
|
|
/* expand tuple at front */
|
|
// it seems that some versions of python want just 2 arguments; in that
|
|
// case, remove trailing 1
|
|
#if (PY_MAJOR_VERSION==2)&&(PY_MINOR_VERSION>=2)
|
|
oops = _PyTuple_Resize(&args, (1 + PyTuple_Size(args)));
|
|
#else
|
|
oops = _PyTuple_Resize(&args, (1 + PyTuple_Size(args)),1);
|
|
#endif
|
|
oops |= PyTuple_SetItem(args, 0, func);
|
|
if (oops)
|
|
return NULL; /* "args = (funcobj,) + (arg,..)" */
|
|
|
|
res = PP_Run_Function( /* "pdb.runcall(funcobj, arg,..)" */
|
|
"pdb", "runcall", /* recursive run_function */
|
|
"O", &presult,
|
|
"O", args); /* args already is a tuple */
|
|
return (res != 0) ? NULL : presult; /* errors in run_function? */
|
|
} /* presult not yet decref'd */
|
|
|
|
|
|
int
|
|
PP_Run_Known_Callable(PyObject *object, /* func|class|method */
|
|
const char *resfmt, void *cresult, /* skip module fetch */
|
|
const char *argfmt, ... /* arg,.. */) /* convert args, result */
|
|
{
|
|
/* call a known callable object */
|
|
PyObject *args, *presult;
|
|
va_list argslist;
|
|
va_start(argslist, argfmt); /* "return object(args)" */
|
|
|
|
Py_Initialize();
|
|
args = Py_VaBuildValue(argfmt, argslist); /* convert args to python */
|
|
if (args == NULL) /* args incref'd */
|
|
return -1;
|
|
if (PP_DEBUG) /* debug this call? */
|
|
presult = PP_Debug_Function(object, args); /* run in pdb; incref'd */
|
|
else
|
|
presult = PyEval_CallObject(object, args); /* run function; incref'd */
|
|
|
|
Py_DECREF(args); /* result may be None */
|
|
return PP_Convert_Result(presult, resfmt, cresult); /* convert result to C*/
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PYTHON EXCEPTION INFORMATION ACCESS
|
|
* fetch Python-related error info (type, value);
|
|
* after an API call returns an exception indicator, call
|
|
* PP_Fetch_Error_Text, then get text from the 3 char[]'s;
|
|
* note: calling PyErr_Fetch() clears/erases the current
|
|
* exception in the Python system, as does PyErr_Print(),
|
|
* so you should call one of these, one time, per exception:
|
|
* caveats: not thread-specific since saves data in globals,
|
|
* and only exports traceback object (the exception type and
|
|
* data are converted to text strings and disgarded); the
|
|
* PyErr_Print() built-in also does a bit more on syntax errors,
|
|
* and sends its text to sys.stderr: in principle, we could
|
|
* assign stderr to a StringIO object and call PyErr_Print, but
|
|
* the code here makes the 3 exception components more distinct;
|
|
*****************************************************************************/
|
|
|
|
#define MAX 2024
|
|
/*
|
|
FC_OS_LINUX: This is dangerous. How about PY_EXCEPT_MAX?
|
|
*/
|
|
|
|
/* exception text is here after PP_Fetch_Error_Text call */
|
|
char PP_last_error_type[MAX]; /* exception name text */
|
|
char PP_last_error_info[MAX]; /* exception data text */
|
|
char PP_last_error_trace[MAX]; /* exception traceback text */
|
|
PyObject *PP_last_traceback = NULL; /* saved exception traceback object */
|
|
|
|
|
|
void PP_Fetch_Error_Text()
|
|
{
|
|
// called without exception happened!
|
|
//assert(PyErr_Occurred());
|
|
|
|
char *tempstr;
|
|
PyObject *errobj, *errdata, *errtraceback, *pystring;
|
|
|
|
/* get latest python exception information */
|
|
/* this also clears the current exception */
|
|
|
|
PyErr_Fetch(&errobj, &errdata, &errtraceback); /* all 3 incref'd */
|
|
|
|
|
|
/* convert type and data to strings */
|
|
/* calls str() on both to stringify */
|
|
|
|
pystring = NULL;
|
|
if (errobj != NULL &&
|
|
(pystring = PyObject_Str(errobj)) != NULL && /* str(errobj) */
|
|
(PyString_Check(pystring)) ) /* str() increfs */
|
|
{
|
|
strncpy(PP_last_error_type, PyString_AsString(pystring), MAX); /*Py->C*/
|
|
PP_last_error_type[MAX-1] = '\0';
|
|
}
|
|
else
|
|
strcpy(PP_last_error_type, "<unknown exception type>");
|
|
Py_XDECREF(pystring);
|
|
|
|
|
|
pystring = NULL;
|
|
if (errdata != NULL &&
|
|
(pystring = PyObject_Str(errdata)) != NULL && /* str(): increfs */
|
|
(PyString_Check(pystring)) )
|
|
{
|
|
strncpy(PP_last_error_info, PyString_AsString(pystring), MAX); /*Py->C*/
|
|
PP_last_error_info[MAX-1] = '\0';
|
|
}
|
|
else
|
|
strcpy(PP_last_error_info, "<unknown exception data>");
|
|
Py_XDECREF(pystring);
|
|
|
|
|
|
/* convert traceback to string */
|
|
/* print text to a StringIO.StringIO() internal file object, then */
|
|
/* fetch by calling object's .getvalue() method (see lib manual); */
|
|
|
|
pystring = NULL;
|
|
if (errtraceback != NULL &&
|
|
(PP_Run_Function("StringIO", "StringIO", "O", &pystring, "()") == 0) &&
|
|
(PyTraceBack_Print(errtraceback, pystring) == 0) &&
|
|
(PP_Run_Method(pystring, "getvalue", "s", &tempstr, "()") == 0) )
|
|
{
|
|
strncpy(PP_last_error_trace, tempstr, MAX);
|
|
PP_last_error_trace[MAX-1] = '\0';
|
|
free(tempstr); /* it's a strdup */
|
|
}
|
|
else
|
|
strcpy(PP_last_error_trace, "<unknown exception traceback>");
|
|
Py_XDECREF(pystring);
|
|
|
|
|
|
Py_XDECREF(errobj);
|
|
Py_XDECREF(errdata); /* this function owns all 3 objects */
|
|
Py_XDECREF(PP_last_traceback); /* they've been NULL'd out in Python */
|
|
PP_last_traceback = errtraceback; /* save/export raw traceback object */
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* GET/SET MODULE-LEVEL (GLOBAL) PYTHON VARIABLES BY NAME
|
|
* handles module (re)loading, input/output conversions;
|
|
* useful for passing data to/from codestrings (no args or return
|
|
* val)--load module, set inputs, run codestring, get outputs;
|
|
* subtle thing: Python "s" output conversion code sets a C char* to
|
|
* the text in the middle of a Python string object, which may be
|
|
* returned to the heap if decref'd--this api copies the text to a new
|
|
* char array (with strdup) that the caller must free() when possible,
|
|
* rather than assuming the caller can and will copy the result out;
|
|
* could postpone the decref till next api call, but that's too subtle;
|
|
*****************************************************************************/
|
|
|
|
|
|
int
|
|
PP_Convert_Result(PyObject *presult, const char *resFormat, void *resTarget)
|
|
{
|
|
if (presult == NULL) /* error when run: fail */
|
|
return -1;
|
|
else
|
|
if (resTarget == NULL) { /* passed target=NULL: ignore result */
|
|
Py_DECREF(presult); /* procedures and stmts return None */
|
|
return 0;
|
|
}
|
|
else
|
|
if (! PyArg_Parse(presult, resFormat, resTarget)) { /* convert Python->C */
|
|
Py_DECREF(presult); /* need not be tuple */
|
|
return -1; /* error in convert */
|
|
}
|
|
else {
|
|
if (strcmp(resFormat, "O") != 0) { /* free object unless exported */
|
|
if (strcmp(resFormat, "s") == 0) { /* copy string: caller owns it */
|
|
char **target = (char**) resTarget;
|
|
#if defined (__GNUC__)
|
|
*target = strdup(*target);
|
|
#else
|
|
*target = _strdup(*target);
|
|
#endif
|
|
}
|
|
Py_DECREF(presult);
|
|
}
|
|
return 0; /* returns 0=success, -1=failure */
|
|
} /* if 0: C result in *resTarget */
|
|
} /* caller must decref if fmt="O" */
|
|
/* caller must free() if fmt="s" */
|
|
|
|
int
|
|
PP_Get_Global(const char *modname, const char *varname, const char *resfmt, void *cresult)
|
|
{
|
|
PyObject *var; /* "x = modname.varname" */
|
|
var = PP_Load_Attribute(modname, varname); /* var is incref'd */
|
|
return PP_Convert_Result(var, resfmt, cresult); /* convert var to C form */
|
|
}
|
|
|
|
|
|
int
|
|
PP_Set_Global(const char *modname, const char *varname, const char *valfmt, ... /* cval(s) */)
|
|
{
|
|
int result;
|
|
PyObject *module, *val; /* "modname.varname = val" */
|
|
va_list cvals;
|
|
va_start(cvals, valfmt); /* C args after valfmt */
|
|
|
|
module = PP_Load_Module(modname); /* get/load module */
|
|
if (module == NULL)
|
|
return -1;
|
|
val = Py_VaBuildValue(valfmt, cvals); /* convert input to Python */
|
|
va_end(cvals);
|
|
if (val == NULL)
|
|
return -1;
|
|
result = PyObject_SetAttrString(module, varname, val);
|
|
Py_DECREF(val); /* set global module var */
|
|
return result; /* decref val: var owns it */
|
|
} /* 0=success, varname set */
|
|
|
|
|
|
/*****************************************************************************
|
|
* MODULE INTERFACE
|
|
* make/import/reload a python module by name
|
|
* Note that Make_Dummy_Module could be implemented to keep a table
|
|
* of generated dictionaries to be used as namespaces, rather than
|
|
* using low level tools to create and mark real modules; this
|
|
* approach would require extra logic to manage and use the table;
|
|
* see basic example of using dictionaries for string namespaces;
|
|
*****************************************************************************/
|
|
|
|
|
|
int PP_RELOAD = 0; /* reload modules dynamically? */
|
|
int PP_DEBUG = 0; /* debug embedded code with pdb? */
|
|
|
|
|
|
const char *PP_Init(const char *modname) {
|
|
Py_Initialize(); /* init python if needed */
|
|
//#ifdef FC_OS_LINUX /* cannot convert `const char *' to `char *' in assignment */
|
|
if (modname!=NULL) return modname;
|
|
{ /* we assume here that the caller frees allocated memory */
|
|
// #0000716: strange assignment and a memory leak
|
|
return "__main__";
|
|
//char* __main__=(char *)malloc(sizeof("__main__"));
|
|
//return __main__="__main__";
|
|
}
|
|
//#else
|
|
// return modname == NULL ? "__main__" : modname; /* default to '__main__' */
|
|
//#endif
|
|
}
|
|
|
|
|
|
int
|
|
PP_Make_Dummy_Module(const char *modname) /* namespace for strings, if no file */
|
|
{ /* instead of sharing __main__ for all */
|
|
PyObject *module, *dict; /* note: __main__ is created in py_init */
|
|
Py_Initialize();
|
|
module = PyImport_AddModule(modname); /* fetch or make, no load */
|
|
if (module == NULL) /* module not incref'd */
|
|
return -1;
|
|
else { /* module.__dict__ */
|
|
dict = PyModule_GetDict(module); /* ['__dummy__'] = None */
|
|
PyDict_SetItemString(dict, "__dummy__", Py_None);
|
|
PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
PyObject * /* returns module object named modname */
|
|
PP_Load_Module(const char *modname) /* modname can be "package.module" form */
|
|
{ /* reload just resets C extension mods */
|
|
/*
|
|
* 4 cases:
|
|
* - module "__main__" has no file, and not prebuilt: fetch or make
|
|
* - dummy modules have no files: don't try to reload them
|
|
* - reload=on and already loaded (on sys.modules): "reload()" before use
|
|
* - not loaded yet, or loaded but reload=off: "import" to fetch or load
|
|
*/
|
|
|
|
PyObject *module, *sysmods;
|
|
modname = PP_Init(modname); /* default to __main__ */
|
|
|
|
if (strcmp(modname, "__main__") == 0) /* main: no file */
|
|
return PyImport_AddModule(modname); /* not increfd */
|
|
|
|
sysmods = PyImport_GetModuleDict(); /* get sys.modules dict */
|
|
module = PyDict_GetItemString(sysmods, modname); /* mod in sys.modules? */
|
|
|
|
if (module != NULL && /* dummy: no file */
|
|
PyModule_Check(module) &&
|
|
PyDict_GetItemString(PyModule_GetDict(module), "__dummy__")) {
|
|
return module; /* not increfd */
|
|
}
|
|
else
|
|
if (PP_RELOAD && module != NULL && PyModule_Check(module)) {
|
|
module = PyImport_ReloadModule(module); /* reload file,run code */
|
|
Py_XDECREF(module); /* still on sys.modules */
|
|
return module; /* not increfd */
|
|
}
|
|
else {
|
|
module = PyImport_ImportModule(modname); /* fetch or load module */
|
|
Py_XDECREF(module); /* still on sys.modules */
|
|
return module; /* not increfd */
|
|
}
|
|
}
|
|
|
|
|
|
PyObject *
|
|
PP_Load_Attribute(const char *modname, const char *attrname)
|
|
{
|
|
PyObject *module; /* fetch "module.attr" */
|
|
modname = PP_Init(modname); /* use before PyEval_CallObject */
|
|
module = PP_Load_Module(modname); /* not incref'd, may reload */
|
|
if (module == NULL)
|
|
return NULL;
|
|
return PyObject_GetAttrString(module, attrname); /* func, class, var,.. */
|
|
} /* caller must xdecref */
|
|
|
|
|
|
/* extra ops */
|
|
int
|
|
PP_Run_Command_Line(const char *prompt)
|
|
{
|
|
int res; /* interact with python, in "__main__" */
|
|
Py_Initialize(); /* in the program's "stdio" window */
|
|
if (prompt != NULL)
|
|
#if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX)
|
|
printf("[%s <Use Ctrl-D (i.e. EOF) to exit.>]\n", prompt);
|
|
#elif defined (FC_OS_WIN32)
|
|
printf("[%s <Use Ctrl-Z plus Return to exit.>]\n", prompt);
|
|
#endif
|
|
res = PyRun_InteractiveLoop(stdin, "<stdin>");
|
|
return res;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* RUN EMBEDDED CODE-STRINGS
|
|
* handles debugging, module (re)loading, namespaces, output conversions;
|
|
* pbd.runeval returns a value: "eval(expr + '\n', globals, locals)";
|
|
* pdb.run is just a statement: "exec cmd + '\n' in globals, locals"
|
|
* New tools: precompiling strings to bytecode, running bytecode;
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
int
|
|
PP_Run_Codestr(PPStringModes mode, const char *code, /* expr or stmt string */
|
|
const char *modname, /* loads module if needed */
|
|
const char *resfmt, void *cresult) /* converts expr result to C */
|
|
{
|
|
/* run a string of Python code */
|
|
int parse_mode; /* "eval(code, d, d)", or */
|
|
PyObject *module, *dict, *presult; /* "exec code in d, d" */
|
|
|
|
module = PP_Load_Module(modname); /* get module, init python */
|
|
if (module == NULL) /* not incref'd */
|
|
return -1;
|
|
dict = PyModule_GetDict(module); /* get dict namespace */
|
|
if (dict == NULL) /* not incref'd */
|
|
return -1;
|
|
|
|
parse_mode = (mode == PP_EXPRESSION ? Py_eval_input : Py_file_input);
|
|
if (PP_DEBUG)
|
|
presult = PP_Debug_Codestr(mode, code, dict); /* run in pdb */
|
|
else
|
|
presult = PyRun_String(code, parse_mode, dict, dict); /* eval direct */
|
|
/* increfs res */
|
|
if (mode == PP_STATEMENT) {
|
|
int result = (presult == NULL? -1 : 0); /* stmt: 'None' */
|
|
Py_XDECREF(presult); /* ignore result */
|
|
return result;
|
|
}
|
|
return PP_Convert_Result(presult, resfmt, cresult); /* expr val to C */
|
|
}
|
|
|
|
|
|
PyObject *
|
|
PP_Compile_Codestr(PPStringModes mode, /* precompile string to bytecode */
|
|
const char *codestr) /* pass result to PP_Run_Bytecode */
|
|
{
|
|
int start;
|
|
Py_Initialize();
|
|
switch (mode) {
|
|
case PP_STATEMENT:
|
|
start = Py_file_input; break;
|
|
case PP_EXPRESSION:
|
|
start = Py_eval_input; break;
|
|
default:
|
|
start = Py_single_input; /* prints expr results */
|
|
}
|
|
return Py_CompileString(codestr, "<PP_Compile_Codestr>", start);
|
|
}
|
|
|
|
|
|
int
|
|
PP_Run_Bytecode(PyObject *codeobj, /* run compiled bytecode object */
|
|
const char *modname, /* in named module's namespace */
|
|
const char *resfmt, void *restarget)
|
|
{
|
|
PyObject *presult, *module, *dict;
|
|
|
|
if (! PyCode_Check(codeobj)) /* make sure it's bytecode */
|
|
return -1;
|
|
module = PP_Load_Module(modname); /* get module, init python */
|
|
if (module == NULL) /* not incref'd */
|
|
return -1;
|
|
dict = PyModule_GetDict(module); /* get dict namespace */
|
|
if (dict == NULL) /* not incref'd */
|
|
return -1;
|
|
if (PP_DEBUG)
|
|
presult = PP_Debug_Bytecode(codeobj, dict); /* run in pdb */
|
|
else
|
|
presult = PyEval_EvalCode((PyCodeObject *)codeobj, dict, dict);
|
|
return PP_Convert_Result(presult, resfmt, restarget); /* expr val to C */
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* subtle things:
|
|
* 1) pdb.run and pdb.runeval both accept either a string or a
|
|
* compiled code object, just because they call the built in exec and
|
|
* eval(), which allow either form; further, eval() works on code
|
|
* objects compiled as either expressions or statements, but returns
|
|
* None as the result of statements, so we don't need to distinguish
|
|
* between expressions and statements here again for bytecode (we
|
|
* did when compiling); the equivalents in Python code:
|
|
* >>> a = 1
|
|
* >>> s = compile('x = 1', '', 'exec')
|
|
* >>> e = compile('a + 1', '', 'eval')
|
|
* >>> print eval(e)
|
|
* 2
|
|
* >>> print eval(s)
|
|
* None
|
|
* on the other hand, we can't blindly use pdb.runeval when dealing
|
|
* with uncompiled strings, because eval() fails on statement strings;
|
|
*
|
|
* 2) in 1.5, if you debug a string or bytecode object in a module's
|
|
* namespace where you've already debugged once, you may see a bogus
|
|
* return value on entry which is left over from a prior debug; this
|
|
* is because pdb leaves a '__return__' attribute in the module's
|
|
* dictionary, which may be a pdb bug, but we work around it here by
|
|
* manually deleting __return__ if present before starting pdb again;
|
|
* only happens for strings--function namespaces aren't persistent;
|
|
**************************************************************************/
|
|
|
|
|
|
static void fixPdbRetval(PyObject *moddict)
|
|
{ if (PyDict_DelItemString(moddict, "__return__")) PyErr_Clear(); }
|
|
|
|
|
|
PyObject *
|
|
PP_Debug_Codestr(PPStringModes mode, const char *codestring, PyObject *moddict)
|
|
{
|
|
int res;
|
|
PyObject *presult;
|
|
const char *pdbname = (mode == PP_EXPRESSION ? "runeval" : "run");
|
|
fixPdbRetval(moddict);
|
|
/* pass code to a pbd.py function */
|
|
res = PP_Run_Function( /* "pdb.run(stmt, gdict, ldict)" */
|
|
"pdb", pdbname, /* "pdb.runeval(expr, gdict, ldict)" */
|
|
"O", &presult,
|
|
"(sOO)", codestring, moddict, moddict);
|
|
return (res != 0) ? NULL : presult; /* return null or increfd object */
|
|
}
|
|
|
|
|
|
PyObject *
|
|
PP_Debug_Bytecode(PyObject *codeobject, PyObject *moddict)
|
|
{
|
|
int res;
|
|
PyObject *presult;
|
|
fixPdbRetval(moddict);
|
|
res = PP_Run_Function( /* "pdb.runeval(codeobj, gdict, ldict)" */
|
|
"pdb", "runeval", /* accepts string|code, code=stmt|expr */
|
|
"O", &presult,
|
|
"(OOO)", codeobject, moddict, moddict);
|
|
return (res != 0) ? NULL : presult; /* null if error in run_function */
|
|
}
|
|
|
|
|
|
|
|
|