Saturday, November 21, 2009

Objective-C calls Python names (3)

I'm working my way through some code that was gifted to me here. It explains how to call Python from Objective-C. This first two posts are here and here. The overall scheme is that we do:

Python => ObjC => Python => ObjC.

The code in the Python module does several things:

• loads the bundle containing the AbstractClass class, written in Objective-C (given its location from a path passed in on the command line)

• first calls objc.lookUpClass("AbstractClass")---so we obtain the definition of the abstract superclass, compiled and saved in the bundle

• provides the Python implementation for ConcreteClass, which inherits from AbstractClass.

• also looks up a second Objective-C class that will run things, and calls a function defined in it. This gets everything rolling in Objective-C. We come back to Python from there.

This is a somewhat unusual Python module.

One thing is that the "bundle" we load is never explicitly used. It is just available to the Objective-C "runtime" so that we can do objc.lookUpClass(). [Oops: beyond checking to see whether the bundle was loaded, that is!] More about lookUpClass another time.

There is also the fact that ConcreteClass has no __init__ function. Instead, there is a class method (in part):


    @classmethod
def namedInstance_(self, aName):
print "creating %s" % aName
newInstance = ConcreteClass.new()


corresponding to this stub in the superclass:

+ namedInstance: (NSString *) aName

(which, as you remember, returned nil). So, I think what we are doing here is using PyObjC to go back and instantiate our Python class on the Objective-C side of bridge using new, which is, I suppose, where it has to be.

At the end of the module we do:


objectiveCCode = objc.lookUpClass("ObjectiveCCallingPython")
print objectiveCCode.callSomePython()


This brings us to the third class: ObjectiveCCallingPython. It uses an Objective-C call I've never heard of:

NSClassFromString()

I'll look more at these two string to class methods in another post. But the outline of what is going on is this:

• From the command line, we call the Python interpreter and give it the name of a Python module as the script to execute, as well as the path to a bundle containing two Objective-C classes.

• The Python code loads the bundle and (in some way not clear to me yet) uses that information to get two necessary class definitions. There is some magic in objc.lookUpClass().

• We then call back to Objective-C with: objectiveCCode.callSomePython()

• This Objective-C code calls a "factory" method to instantiate the Python-defined class using the PyObjC defined new method and do stuff with it from Objective-C.

Got that?

I am beginning to understand bbum's exasperation with me. On the very first page of the Userguide for PyObjC it says: "Using PyObjC is pretty simple, as it strives to make the bridge completely transparent. On the lowest level you can import the 'objc' module and use that to locate classes (objc.lookUpClass)."

There is also a fairly old tutorial.

And googling with objc.lookUpClass turned up someone who was more successful at following the hints than I was.