Neat PyObjC Trick

I demoed the latest build of PyObjC at the recent CocoaHeads meeting in Cupertino.

The demo concluded with a “show all [most, really] the tricks at once” sequence.

  • How py2app, a part of PyObjC, will automatically create an installer package from a standard Python package
  • Injecting a live Python interpreter into a running application
  • Introspecting the application
  • Interacting with Core Data, including dealing with ** style parameters
  • Modifying a managed object and having KVO automatically fire on assignment

First, grab the top-of-tree source from the pyobjc repository:

svn co http://svn.red-bean.com/pyobjc/trunk/pyobjc/

Alternatively, you can mount that URL in the finder (click cancel on all the bloody attempts to write .DS_Store files) and cp -r the source to somewhere from the Terminal.

Then:

cd pyobjc
python setup.py bdist_mpkg --open

That will build PyObjC and open an Installer package that contains the runtime, documentation, examples, and Xcode templates.

Next, grab the OutlineEdit examples from /Developer/Examples/CoreData/ and run it.

Now, from the PyObjC source directory:

cd Examples/Inject/InjectInterpreter
ps aux | grep OutlineEdit
... note the PID ...
python test.py PID

The InjectInterpreter will be built and, shortly, you will see a Window pop up in OutlineEdit containing a Python interpreter.

Click on OutlineEdit and add some new Notes in the Untitled document window. Quantity and structure don’t matter.

Now, click on the interpreter window and do:

from AppKit import *
from objc import *

At this point, we now have the PyObjC bridge imported into the Python interpreter that is running within the Main Event Loop of OutlineEdit. With that in place, we can now interact with the Cocoa infrastructure to interrogate and manipulate the application. Since this is a Core Data application, we can — of course — interact with all the Core Data goodness within the document.

First, we need to get a hold of all that Core Data goodness:

d = NSDocumentController.sharedDocumentController().documents()[0]
mom = d.managedObjectModel()
moc = d.managedObjectContext()
noteEntity = mom.entitiesByName()['Note']
fr = NSFetchRequest.new()
fr.setEntity_(noteEntity)

Now, grab one of the Note objedcts:

notes, error = moc.executeFetchRequest_error_(fr)
note = notes[0]

And manipulate:

note.contents = u'Hello, world!'

Note that as soon as you hit return, the UI updates. This happens because PyObjC is smart enough to trigger the KVO/KVC notifications upon assignment to an attribute of an NSObject subclass.

And, of course, if you screw up the state of the document too badly:

moc.undo()


6 Responses to “Neat PyObjC Trick”

  1. keith ray says:

    This is really cool… All we need now is an integration of this with PyFIT ( Fitnesse ) so we can write automated acceptance tests for our Cocoa applications. http://agile.unisonis.com/PyFitTutorial.html

    If anyone has already done this, please publish a tutorial for the rest of us!

  2. jcburns says:

    [EDITOR: JC was injecting pyobjc into PyOutlineEdit — the Python/PyObjC port of OutlineEdit — it should work, but doesn’t. The example was specifically aimed at injecting into a non-PyObjC app. JC has since verified that the instructions work fine w/the regular ObjC OutlineEdit]

    arrgh..

    everytime I try this I get

    Thread 0 Crashed:
    0   libobjc.A.dylib                	0x909ad100 objc_msgSend + 32
    1   ...ied.InjectInterpreterPlugin 	0x017d2de0 dyld_stub__dyld_get_image_header_containing_address + 24971912
    2   ...ied.InjectInterpreterPlugin 	0x017d3074 dyld_stub__dyld_get_image_header_containing_address + 24972572
    3   dyld                           	0x8fe16584 ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 820
    4   dyld                           	0x8fe0b6f0 ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&) + 220
    5   dyld                           	0x8fe0f378 ImageLoader::link(ImageLoader::LinkContext const&, ImageLoader::BindingLaziness, ImageLoader::InitializerRunning, unsigned) + 848
    6   dyld                           	0x8fe044d4 dyld::link(ImageLoader*, ImageLoader::BindingLaziness, ImageLoader::InitializerRunning) + 396
    7   dyld                           	0x8fe08c48 NSLinkModule + 232
    8   libSystem.B.dylib              	0x9002db98 NSLinkModule + 104
    9   > 	0x06629574 0 + 107124084
    

    (and so on) CRRRASH!

  3. leeg says:

    Did anything ever come of ObjectiveEverything from tiptop? I know that PyObjC, CamelBones, RubyCocoa et al are now at least at the same point if not superior to it, but it did provide a nice little environment…

  4. John C. Randolph says:

    Leeg,

    Pedja hasn’t been doing anything with Objective-Everything for many years now. The last time I saw him was at WWDC at least four years ago, and he told me then that he really can’t afford the time away from his consulting business to work on the TipTop products.

    -jcr

  5. Malte Tancred says:

    I tried the above but running “python test.py PID” did not work out as expected.
    I realised I built the PyObjC package (and installed it) using the DarwinPorts
    distribution of python 2.4. This is the end of the Xcode Run Log output:


    2005-11-20 17:52:45.903 OutlineEdit[27580] An uncaught exception was raised during execution of the main script:
    ImportError: No module named _File

    I ran find on Apple’s Python framework as well as DarwinPorts’
    and sure enough, there’s a
    ./lib/python2.3/lib-dynload/_File.so
    in Apple’s framework, but not in DarwinPorts.
    Any clue if this can be fixed somehow (I mean in DarwinPorts)?
    Perhaps I shouldn’t try to run PyObjC apps on OS X against DP?

  6. Bob Ippolito says:

    Yeah, don’t use DP’s python for developing Mac OS X specific stuff. Nobody on the PyObjC, py2app, etc. teams use anything but a bog standard framework build of Python, and nobody from the DP or Fink teams care enough about this stuff to test and contribute patches.

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>