Cocomo: An experiment in metaprogramming in python

posted: Mon, 01 Jun 2009 11:16 | filed under: / / / | permalink | Tags: , , , , , | Comments: 1

Friday saw the second edition of the UTAS Computing Society Lightning Talks, if you haven't seen them already, I highly recommend that you check them out -- this semester's were at a very high standard indeed, and I wish I'd printed out more certificates for good talks :). My talk was a demonstration of using metaprogramming in Python, though that's not what it seemed to be about.

An introduction

I went to the Apple University Consortium's Cocoa Workshop at the University of New South Wales in February of this year, it was a heap of fun, and we learnt heaps whilst there. One of the key distinguising features of Cocoa is its use of verbose English method and attribute names, the idea being that each line of code should make a reasonable amount of sense when read aloud, hence:

NSString *str = [[NSString alloc] initWithString @"Hello World!"]

does indeed allocate memory to hold a string object, and initialises the newly-allocated memory with a string containing "Hello World!" (this code is highly redundant!). Supposedly such a naming scheme allows coders to write code that is easily maintainable by the original coder, and easily learnable by people who pick up the code for the first time.

On the other hand, my friends, collectively known as Maclab (named after the room at UTAS we inhabit) have developed a rather unique vocabularly, which in particular involves replacing as many words as possible with either 'thrust' or 'fork', so "Thrustingly thrust the forking forker" is not an uncommon utterance amongst my friends. If this is indeed their usual mode of conversation, then Cocoa's way of identifying methods and attributes is not necessarily going to be a particularly intiuitive one. So, clearly, we need a version of cocoa that meets their needs.

The setup

So, conveniently, Apple provide a comprehensive version of the Cocoa API, thanks to the PyObjC project. We can therefore use the Python bindings for Cocoa facilitate our new version of Cocoa. Since Cocoa has a very consistent naming scheme, we can simply perform string replacement to translate from our maclab language to the standard cocoa language, using a routine somewhat like this:

def translate(inp):
	''' Translates an input string from key language to value language '''
	for i in LANGUAGE:
		if i[0].islower():
			# Try both capital case and lowercase
			inp = inp.replace(i, LANGUAGE[i])
			inp = inp.replace(rtitle(i), rtitle(LANGUAGE[i]))
		else:
			inp = inp.replace(i, LANGUAGE[i])
	return inp

def rtitle(i):
	return i[0].upper() + i[1:]

Here, LANGUAGE is a dictionary, with keys in the language code will be written in and values being the target language (in this case, Cocoa). There's not all that much of a sophisticated nature going on in here. Now that we have a method by which we can translate our attribute accesses, we can get to the meat of the the code.

The implementation

To achieve the new API, we need to use a technique that I will call proxying. This involves the use of objects whose sole purpose is to intercept attribute accesses and calls to an underlying object. In this case, the point of intercepting the calls and accesses is to perform translation from our new objects to standard Cocoa objects. In Python we can do this by overriding the standard attribute access and call methods.

First up is __getattr__, the attribute accessor method -- for this, we are passed a string; the name of the attribute that we're looking for, which we translate, and then attempt to access upon the method on the underlying object (in this case, self.__u__). There is one slight hitch: in certain cases, we may not want to translate the attribute name. This is true, in particular, of the attribute that represents the underlying object. Hence we provide a REAL_ATTRS list, for which we use the default __getattr__ method for. This results in code that looks something like this:

	def __getattribute__(self,name):
		#''' Perform method/attribute proxying on ''' + repr(self.__u__)
		if name in REAL_ATTRS:
			return object.__getattribute__(self,name)
		else:
			new_objectname = "self.__u__.%s" % translate(name)
			new_object = eval(new_objectname)
			return CocomoProxy(new_object)

Notice that we use eval to perform the lookup? It turns out that __getattr__ doesn't work universally, whereas . notation does -- so we use that for less failover.

Being able to call methods on the objects is important, but slightly more difficult -- we want behaviour to be maintained, so we need to make sure that proper Cocoa objects are passed as arguments, rather than the Proxy objects that you may have originally dealt with. We can do this with Python's argument unpacking -- we build up a list of arguments, and unproxy them as necessary:

	def __call__(self,*a, **k):
		new_a = [i.__u__ if type(i) == CocomoProxy else i for i in a]
		new_k = dict( (translate(i), k[i].__u__ if type(k[i]) == CocomoProxy else k[i]) for i in k)
		return CocomoProxy(self.__u__(*new_a,**new_k))

We may also need to deal with iterators. This can be done using a standard generator function, thusly:

	def __iter__(self):
		for i in self.__u__:
			yield CocomoProxy(i)

Finally, there may be legitimate reasons for extracting Cocoa objects, these include printing strings, so we provide an accessor method called no_really:

	def no_really(self):
		return self.__u__

And that's the entire implementation! The final thing we need to do is provide a pre-proxied version of the base module for Cocoa. Let's call it GypsyMagic.

The payoff

So now that we have a working bridge from Maclab English to Cocoa English, we can take this sample code that puts some stuff into an array, and then prints it:

import AppKit

hworld = AppKit.NSString.alloc().initWithString_("Hello, World!")
arr = AppKit.NSMutableArray.alloc().init()

arr.addObject_(hworld)
arr.addObject_("Boop!")


for i in arr:
	print i	

And write it in the far more palatable:

from cocomo import GypsyMagic

hworld = GypsyMagic.OGMouthWords.subsume().makeGogoWithMouthWords_("Hello, World!")
arr = GypsyMagic.OGForkableTrinketHolder.subsume().makeGogo()

arr.thrustinglyThrustForker_(hworld)
arr.thrustinglyThrustForker_("Boop!")

for i in arr:
	print i.no_really()

If you're interested in seeing how it all fits together, see Cocomo's website.

The Week in Review...

posted: Fri, 21 Nov 2008 18:41 | filed under: / / / | permalink | Tags: , , , , , , | Comments: 0

Time for me to enumerate a few things that have happened of late (in reverse order of occurrence, naturally), since it now seems like the time to do so.

Uni

I handed in my Computer Science term project today, which, I suppose means that my academic year is now complete. I'm fairly happy with how the semester's progressed, every unit that I studied (including the two that I chose on a whim) has been excellent, which is more than I can say for previous semesters. Analysis (Real analysis to be specific) was absolutely fantastic, and I'll be doing my best to enrol in the follow-up functional analysis unit (I've had it suggested to me by several people, and I'm convinced), and it's certainly made the maths major I'm now enrolled in seem like a very good idea.

As far as I can tell, exams went well, but I won't know for certain until results are released next week (I'm very confident with my two maths units, Graphics is a different story (though I don't recall doing as badly as the lecturer claims the class as a whole went)).

TUCS

In other (though slightly Uni-related) news, TUCS (The UTAS Computing Society) had its Annual General Meeting for 2009 last week, and as well as discovering the joy of barbecued* Woolworths' Quantity Burgers (they're excellent, really!), I was elected society president for 2009. The rest of the exec are also a truly awesome bunch of people, so the future certainly looks bright.

TUCS T-shirt

TUCS has run some excellent events in its inaugural year: our tech talks were, in general, wildly successful, amongst other things. Thanks to that, we've become what appears to be one of the most active societies on campus. I'll be doing my best to make sure that we can replicate, or even better that next year. (If you're a speaker, or know any good ones, and would like to give a talk, let me know!)

In related news, we also took delivery of some particularly awesome TUCS-Branded T-Shirts just after exams -- we're particularly happy with how that went and will probably do it again next year.

(*I will definitely be approving funding for a new barbecue for the society... the current one is truly dreadful)

LCA

Last week-ish, I had dinner with some members of the Linux.conf.au organising committee. Though much of what was discussed must be kept under wraps (it's thoroughly exciting, I promise!), I can tell you that the conference is shaping up to be most excellent, and if you haven't already booked your ticket, I suggest you do so as soon as possible!

That is all for me for now, more news as it comes (I hope!)

TUCS Tech Talk Photos

posted: Mon, 05 May 2008 13:43 | filed under: / / / | permalink | Tags: , , , , , | Comments: 0


TUCS Tech Talk #1, originally uploaded by Christopher Neugebauer.

As I mentioned previously, TUCS had its first tech talk on Friday (delivered by myself, on the topic of Introductory Python), this is the first opportunity to show off photos from it. I was rather impressed by the turnout (there are a few people off to the side that can't be seen in the frame).

Goodbye, IDS; Hello, TUCS!

posted: Thu, 13 Mar 2008 21:41 | filed under: / / / | permalink | Tags: , , | Comments: 0

Today was the AGM for the UTas Internet Developers' Society. Other than the usual blather that occurs in AGMs for these sorts of things, we've approved a change of name to TUCS (or rather the Tasmania University (union) Computing Society). For the moment this name change is purely cosmetic (as we really haven't done that much in the way of Internet Development for as long as I've been at the Uni. The new name (in my opinion) reflects the membership, and the aims of the society a lot better.

One item of Business that I raised was the upcoming Linux.conf.au 2009, which you probably already know by now is being held at the University of Tasmania in Hobart. It's been resolved that the Society establish better ties with the Free Software/Open Source Community (in Tasmania, in Particular with TasLUG) with the intention of better promoting Free/Open Source software amongst the student and staff body in the leadup to the conference; and I ran for the executive (successfully) on that basis.

Here's hoping it's a successful year for the society (which now has a cool name!)