[capi-sig] Easy way to Return Value from 1 line of Embedded Python?

Jerry Krinock support at sheepsystems.com
Sun May 30 07:38:02 CEST 2010


On 2010 May 28, at 01:54, M.-A. Lemburg wrote:

> You can compile the code into code object using Py_CompileString()
> and then pass this to PyEval_EvalCode() for execution. This
> will evaluate the code and return the resulting Python object.

Thank you, Mark-Andre.  I had trouble getting that to work.  Let's move on to your preferred answer.

> In your case, it's probably easier to just load the pickle
> module from C and call the loads() function directly rather
> than going through an extra layer of Python code.

I'm not sure if you meant to use PyFunction_New() or PyRun_String().  I got it to work using the latter.  See code below.

> The string object you get from the Python function will only be allocated while the interpreter is initialized. ... To correct this, you will have to get the pointer to the string data, copy it to a buffer you allocate in your app and only then finalize the interpreter.

Thanks.  I've corrected that now by creating and returning a Cocoa object.

So, here is the working code using PyRun_String().  The NSString is the Cocoa object used in Mac OS X or iPhone OS.  Otherwise, use your favorite string object.

#import <Cocoa/Cocoa.h>
#include <Python/Python.h>

NSString* PythonStringCodeUnpickle(char* pickle) {
    NSString* answer = nil ;
    char* myErrorDesc = NULL ;    
    
    Py_Initialize() ;
    
    // Create Python Namespace (dictionary of variables)
    PyObject* pythonStringArg = PyString_FromString(pickle);
    if (!pythonStringArg) {
        myErrorDesc = "Cannot convert string arg to Python\n" ;
        goto end ;
    }
    PyObject* pythonVarsDic = PyDict_New();
    PyDict_SetItemString(
                         pythonVarsDic,
                         "__builtins__",
                         PyEval_GetBuiltins());    
    PyDict_SetItemString(
                         pythonVarsDic,
                         "x",
                         pythonStringArg) ;
    
    // Create "hard" source code string to unpickle
    // (For some strange reason, they call it loads()
    // The "s" in "loads" stands for "string".)
    char* pythonSource =
    "from pickle import loads\n\n"
    "y = loads(x)\n" ;
    
    // Run the python source code
    PyRun_String(pythonSource,
                 Py_file_input,
                 pythonVarsDic,
                 pythonVarsDic) ;
    PyObject* unpickledPythonString = PyDict_GetItemString(pythonVarsDic, "y") ;
    if (!unpickledPythonString) {
        myErrorDesc = "Unpickling returned NULL\n" ;
        goto end ;
    }
    
    // Convert the unpickled Python string to a C string
    char* unpickledString = PyString_AsString(unpickledPythonString) ;
    Py_DECREF(unpickledPythonString);
    if (!unpickledString) {
        myErrorDesc = "Failed converting unpickled string to C string\n" ;
        goto end ;
    }
    
    // Convert the C string into a string object
    answer = [NSString stringWithUTF8String:unpickledString] ;
    
end:
    if (myErrorDesc) {
        printf("%s\n", myErrorDesc) ;
    }
    
    if (PyErr_Occurred()) {
        PyErr_Print();
    }
    
    Py_Finalize() ;
    
    return answer ;
}

int main(int argc, char *argv[]) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;
    
    // The following string is an actual Python pickle.
    // Of course, one would never hard code such a thing in a real program.
    // This is just a test  :)
    char* s = "V/Users/jk/Dropbox2/Dropbox\np1\n.\n" ;

    printf("Unpickling using Python code from string:\n") ;
    printf("   Unpickled result: %s\n\n",
           [PythonStringCodeUnpickle(s) UTF8String]) ;

    // The correct answer for Unpickled result is:
    //    "/Users/jk/Dropbox2/Dropbox"
    
    [pool release] ;
    return 0 ;
}





More information about the capi-sig mailing list