[Pythonmac-SIG] Re: improving appscript architecture

has hengist.podd at virgin.net
Fri Jul 9 01:17:57 CEST 2004


Thanks to Russell and Gary for comments and links on implementing 
mix-ins in Python. Related to that subject, here's a quick question 
about using "non-standard voodoo":

Given that I'm angling for appscript's eventual inclusion in the 
standard MacPython library, the code needs to be grokkable and 
maintainable by others (lest I be hit by the Proverbial Bus someday). 
How would using something like a home-rolled mixin scheme affect 
that? e.g. Justifiable under certain circumstances? A complete 
non-starter?

--

Anyway, to expand on the issue a bit, since I was rather vague before:

What's bothering me isn't that the problem can't be solved, but that 
I'm struggling to find a way to solve it that's no more painful to 
other developers/maintainers than absolutely necessary. None of this 
impacts on end-users who use appscript for application scripting as 
they never have to instantiate classes directly. But it definitely 
affects anyone who has to maintain this code, and it'll very likely 
impact on application developers using appscript to implement 
application scripting support in their own Python-based applications.

A basic example of the knotty compositions I'm dealing with:

AbstractBase
   |
   |-- AbstractMid
   |     |
   |     |-- Public1
   |     |
   |     |-- Public2
   |

If I want to complement/extend/replace the behaviour supplied by 
AbstractMid, I can't do this directly. Instead, I have to subclass 
both Public1 and Public2, then either cut-n-paste the new code into 
both of these subclasses, or use MI to create a new AbstractMid2 that 
the new classes inherit from in addition to Public1 and Public2.

Now, add in various circular references, e.g. a method in Public1 may 
be responsible for creating and returning new instances of Public2, 
and vice-versa. I've already got a scheme whereby modules have to 
provide their Base superclass with a reference to themselves, so that 
ModuleA's version of Public1 returns instances of ModuleA.Public2 
while ModuleAPlus's version of Public1 returns instances of 
ModuleAPlus.Public2.

Oh, and ModuleA also imports ModuleAPlus so it can refer to its 
classes for type-checking purposes.

It all works, but it's already a right old spiderweb and I've yet to 
finish ModuleB and ModuleBPlus, which as their names suggest, build 
on ModuleA and ModuleAPlus respectively. Plus another layer which'll 
go on top of that to enable automatic sanity-checking and 
error-reporting. These layers are supposed to make the package much 
more flexible and easy to develop with - the A layer provides a 
'geek-level' API, the B level extends this with end user-friendly 
sugar, and the 'sanity' layer that users of both A and A+B should be 
free to add in if they want them.

...

In a prototype-based language that uses delegation [modifiable at 
runtime] instead of inheritance [determined at compile-time], 
composing new behaviours would be easy: I'd simply duplicate the 
original objects then modify the delegation chain to insert a new 
AbstractMidPlus object before AbstractMid. It's dead easy to do, and 
dead easy to follow. So I'm fair frustrated at how much harder it is 
to get the same results in a class-based language, but I've no choice 
but to work within these limitations as best I can.

...

It may be that the most practical solution is to replace the concrete 
class structure with a bunch of metaclass factories that pump out the 
appropriate classes by mixing together the raw base classes on the 
spot according to recipes provided by each 'plugin' module. I could 
pretty much do away with the whole inheritance tree and just have a 
pile of simple, flat abstract base classes that are slapped into 
concrete shape purely via multiple inheritance. This shouldn't make 
the method resolution order any harder to fathom than it would be 
with a deep single-inheritance tree, I don't think. Though metaclass 
programming can be more than a little indigestible to many 
developers, so it's not a clear winner either.

...

Mostly, I guess I'm trying to find out which approaches give others 
the least goosebumps to think about, and which will reduce them to 
quivering blobs at the mere thought. Find out which folk think would 
be most practical, given their own experiences, and maybe get a few 
pointers on how best to arrange the basic framework. Asking not so 
much for my benefit as for everyone else's. After all, you folks are 
probably the main developer-side market for the beast, so I'd better 
make damn sure you'll be happy with it. :)

Many thanks,

has
-- 
http://freespace.virgin.net/hamish.sanderson/


More information about the Pythonmac-SIG mailing list