How to convert an NSDictionary to a Python dict?
-
11-09-2019 - |
Question
I have a plugin written entirely in Python using PyObjC whose core classes I need to convert to Objective-C. One of them basically just loads up a Python module and executes a specific function, passing it keyword arguments. In PyObjC, this was extremely.
However, I'm having difficulty figuring out how to do the same thing using the Python C API. In particular, I'm unsure how best to convert an NSDictionary (which might hold integers, strings, booleans, or all of the above) into a format that I can then pass on to Python as keyword arguments.
Anyone have pointers on how to accomplish something like this? Thanks in advance!
Edit: just to clarify, I'm converting my existing class which was formerly Python into Objective-C, and am having trouble figuring out how to move from an NSDictionary in Objective-C to a Python dictionary I can pass on when I invoke the remaining Python scripts. The Objective-C class is basically just a Python loader, but I'm unfamiliar with the Python C API and am having trouble figuring out where to look for examples or functions that will help me.
Solution
Oh, looks like I misunderstood your question. Well, going the other direction isn't terribly different. This should be (as least a start of) the function you're looking for (I haven't tested it thoroughly though, so beware of the bugs):
// Returns a new reference
PyObject *ObjcToPyObject(id object)
{
if (object == nil) {
// This technically doesn't need to be an extra case,
// but you may want to differentiate it for error checking
return NULL;
} else if ([object isKindOfClass:[NSString class]]) {
return PyString_FromString([object UTF8String]);
} else if ([object isKindOfClass:[NSNumber class]]) {
// You could probably do some extra checking here if you need to
// with the -objCType method.
return PyLong_FromLong([object longValue]);
} else if ([object isKindOfClass:[NSArray class]]) {
// You may want to differentiate between NSArray (analagous to tuples)
// and NSMutableArray (analagous to lists) here.
Py_ssize_t i, len = [object count];
PyObject *list = PyList_New(len);
for (i = 0; i < len; ++i) {
PyObject *item = ObjcToPyObject([object objectAtIndex:i]);
NSCAssert(item != NULL, @"Can't add NULL item to Python List");
// Note that PyList_SetItem() "steals" the reference to the passed item.
// (i.e., you do not need to release it)
PyList_SetItem(list, i, item);
}
return list;
} else if ([object isKindOfClass:[NSDictionary class]]) {
PyObject *dict = PyDict_New();
for (id key in object) {
PyObject *pyKey = ObjcToPyObject(key);
NSCAssert(pyKey != NULL, @"Can't add NULL key to Python Dictionary");
PyObject *pyItem = ObjcToPyObject([object objectForKey:key]);
NSCAssert(pyItem != NULL, @"Can't add NULL item to Python Dictionary");
PyDict_SetItem(dict, pyKey, pyItem);
Py_DECREF(pyKey);
Py_DECREF(pyItem);
}
return dict;
} else {
NSLog(@"ObjcToPyObject() could not convert Obj-C object to PyObject.");
return NULL;
}
}
You may also want to take a look at the Python/C API Reference manual if you haven't already.