Thursday, July 19, 2007

a sneak preview of PyObjC 2.0

The development of PyObjC has slowed down to a standstill, or so it would seem if you look the website or subversion repository. That's far from the truth however, I've been hard at work on a major new version of PyObjC and the effort is paying of.

The new version isn't in the public repository yet however because I'm targeting Leopard (Objective-C) and haven't separated the Leopard specific code and examples from the bits that are safe for Leopard. In case anyone is starting to get very worried: PyObjC still supports Tiger through a compatibility layer.

The new version sport some major new versions, hence the major update to the version number:
  • Full support for CoreFoundation-based frameworks such as Core Graphics and other parts of the Quartz graphics layer. This includes access to all APIs in Core Graphics instead of the rather more limited bindings that Apple ships.

    I have several examples for this, including Python ports for all examples in the (excellent) book "Programming with Quartz".

  • Class methods are finally true first-class citizens. It is now possible to access all class methods directory through the class without going through the pyobjc_classMethods attribute. This fixes a longstanding bug in PyObjC and gives a more natural access to class methods whose name shadows an instance method (such as NSObject.description).

  • Creating typed instance variables is now more convenient, as is creating instance variables in general. Compare the old way:

    class MyClass (NSObject):
    var1 = objc.ivar("var1")
    active = objc.ivar("var1", type=objc._C_BOOL)

    and the new way:

    class MyClass (NSObject):
    var1 = objc.ivar()
    active = objc.ivar.bool()

    It is also no longer necessary to mention the name of IBOutlets twice.

  • PyObjC is split into a core package that contains the actual bridge and a set of add-on packages that provide the wrappers for frameworks and make it possible to type import Foundation instead of loading the framework by hand.

    This makes development easier, and more importantly makes sure that the infrastructure for creating framework wrappers is sufficiently public thereby making it easier for others to provide bindings to their own frameworks.

  • Output arguments are treated slightly different: you must now pass a place holder value (either None or objc.NULL) instead of leaving the argument out. This makes it even easier to translate between Objective-C documentation or code samples and Python and also makes is possible to tell the method that you're not interested in the output value (by passing in objc.NULL).

    The old behaviour is still supported but will give a deprecation warning.

  • The module PyObjCTools.NibClassBuilder is also on the way out. Using it is still supported and doesn't generate deprecation warnings, but the the module is formally deprecated.

    There are several reasons for this, one of them is that the mechanism that this module uses to extract class information from NIB files doesn't always work correctly in localized applications (localization tools won't always copy the required information because that information isn't used by Objective-C code).

These are the major new features that are interesting for users of PyObjC, there's some cleanup work and a major implementation change to make maintaining framework wrappers a lot easier.

We used to have a largish set of obscure calls to objc.setSignatureForSelector and manually written method wrappers to make sure that PyObjC did the right thing for methods whose interface is non-trivial, such as methods that have pass-by-reference arguments or pointers to C arrays of values.

Maintaining these is annoyingly hard and I'm glad that this is for the most part no longer necessary. The core of PyObjC can now process the metadata format defined by the BridgeSupport project.

This makes maintenance of framework wrappers a lot more easier because we've no longer have to maintain cryptic signature strings but can instead rely on a tool to extract most information from the header files (with an exception mechanism to steer that tool when it doesn't quite do the right thing). This metadata file also takes a way the need for most manually written method wrappers.

The best part of this is that maintenance of these metadata files can be shared between the users and developers of a number of language bridges and with some luck framework authors will start shipping metadata files with their frameworks.