Python C-API: How to use the returned python dictionary in Objective-C?


Python C-API: How to use the returned python dictionary in Objective-C?



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


app.py


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.


myResult


PyFloat_AsDouble


PyUnicode_AsUTF8String


PyBytes_AsString


PyBLAHBLAH_AsDict


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.



Can someone help me figure out how I can convert the PyObject to a NSDictionary?


PyObject



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 myObjective 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:


json.dumps


JSONObjectWithData



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);





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.
– user2357112
Jul 2 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.
– Pranoy C
Jul 2 at 20:05


PyUnicode_AsUTF8String


PyBytes_AsString


stringWithUTF8String





Not a direct answer, but have you looked at PyObjC ? pythonhosted.org/pyobjc
– Koen
Jul 2 at 20:07





1 Answer
1



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


PyDict_Next



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.


PyObject*



(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.


PyDict_Next


PyDict_GetItemString


char *


PyDict_GetItem






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

api-platform.com Unable to generate an IRI for the item of type

How to set up datasource with Spring for HikariCP?

Display dokan vendor name on Woocommerce single product pages