0

My Python script app.py has this function for example:

def testmethod(x):
    return {"steps":["hello","world"]}

I am using Python C-API in Objective-C as follows:

Py_Initialize();
PyObject *pName = PyUnicode_DecodeFSDefault("app");
PyObject* pModule = PyImport_Import(pName);
Py_DECREF(pName);
        
PyObject* myFunction = PyObject_GetAttrString(pModule,(char*)"testmethod");
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(223.22));
PyObject* myResult = PyObject_CallObject(myFunction, args);
NSLog(@"Check for dict: %d",PyDict_Check(myResult)); // Prints true!!!!!!!!!!!! So definitely a dictionary

After this, I am not really sure how I can convert the myResult to retrieve my dictionary. For doubles and strings, we usually use the PyFloat_AsDouble and PyUnicode_AsUTF8String, PyBytes_AsString functions but I can't seem to find a PyBLAHBLAH_AsDict or equivalent method.

double result = PyFloat_AsDouble(myResult);
NSLog(@"Result: %f",result);*/

PyObject* pStrObj = PyUnicode_AsUTF8String(myResult);
char* zStr = PyBytes_AsString(pStrObj);
NSLog(@"String: %@",[NSString stringWithUTF8String:zStr]);

I don't have much experience with Objective-C so maybe I am not looking at the right place.

How can I convert the PyObject to a NSDictionary?

I am basically looking for a C way of accessing the dictionary first (a map for example) which I can then use Objective-C methods to convert to NSDictionary. Similar to how I used PyUnicode_AsUTF8String, PyBytes_AsString and stringWithUTF8String all combined together to get a NSString out of the Python String. Looking for something similar for dictionaries.

I have figured out a way to achieve this but I don't know if this is the best possible way of doing this. I modified my python script to use json.dumps to return a string representation of the dictionary. Then in my Objective-C code, I first get the string, then use JSONObjectWithData to convert the json string to a NSDictionary. This works for now but I am curious if there is a better way of doing this without having to return string instead of dictionary from Python. This seems more like a workaround solution to me. Here's what I have now:

app.py:

import json
def testmethod(x):
    return json.dumps({"steps":["hello","world"]})

Objective-C:

Py_Initialize();
PyObject *pName = PyUnicode_DecodeFSDefault("app");
PyObject* pModule = PyImport_Import(pName);
Py_DECREF(pName);
        
PyObject* myFunction = PyObject_GetAttrString(pModule,(char*)"testmethod");
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(223.22));
PyObject* myResult = PyObject_CallObject(myFunction, args);

PyObject* pStrObj = PyUnicode_AsUTF8String(myResult);
char* zStr = PyBytes_AsString(pStrObj);
NSString *jsonStr = [NSString stringWithUTF8String:zStr];
NSLog(@"String: %@",jsonStr);

NSError *error;
NSData *data = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

NSLog(@"Dictionary: %@",dictionary);
3
  • Objective-C is not C or C++. The Python C API is a C API, not an Objective-C API; it doesn't come with functions to convert Python objects into Objective-C data structures. Commented Jul 2, 2018 at 20:03
  • @user2357112 Yes I understand that. I am basically looking for a C way of accessing the dictionary first (a map for example) which I can then use objective C methods to convert to NSDictionary. Similar to how I used PyUnicode_AsUTF8String, PyBytes_AsString and stringWithUTF8String all combined together to get a NSString out of the Python String. Looking for something similar for dictionaries. Commented Jul 2, 2018 at 20:05
  • Not a direct answer, but have you looked at PyObjC ? pythonhosted.org/pyobjc Commented Jul 2, 2018 at 20:07

1 Answer 1

1

Use PyDict_Next to iterate over key / value pairs of the dictionary.

You should also provide the Python hash and equality functions if you are still going to be using PyObject*s. Otherwise, you should try and coerce them all into native types.

(This is in pseudo code since I don't really know Objective-C, but have used the C-Api and Python)

NSObject* convert_py_object(PyObject* obj) {
    if (PyDict_Check(obj)) return convert_py_dict(obj);
    if (PyLong_Check(obj)) return convert_py_int(obj);
    if (PyUnicode_Check(obj)) return convert_py_str(obj);
    if (PyBytes_Check(obj)) return convert_py_bytes(obj);
    if (PyList_Check(obj) || PyTuple_Check(obj) || PyIter_Check(obj)) return convert_py_list(obj);
    throw error;
}

NSDictionary* convert_py_dict(PyObject* dict) {
    if (!PyDict_Check(dict)) throw error;
    NSDictionary* native_dict;
    PyObject *key;
    PyObject *value;
    Py_ssize_t pos = 0;

    while (PyDict_Next(self->dict, &pos, &key, &value)) {
        native_dict[convert_py_object(key)] = convert_py_object(value);
    }
    return native_dict;
}

Another alternative would be to just use the Python API to access values of a dictionary. So iterate over all the values with PyDict_Next, PyDict_GetItemString to access string items with simple char *s, and PyDict_GetItem to access an arbitrary Python value.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.