[Pythonmac-SIG] pythonificiation of scriptable applications

Bob Ippolito bob at redivi.com
Tue Jul 15 19:06:40 EDT 2003


<< warning, this post is long.. most of it just just example code >>

I've been working on a non-backwards-compatible replacement for the  
ancient (what is it, 8 years old?  from the old codebase I can see  
python didn't even have %r or isinstance) gensuitemodule/aepack/aetools  
modules.  My primary goal was to make the stuff easy enough for me to  
use at the interpreter, without having to remember how backwards AE is.  
  Here are some of the advantages to the new system:
  - it's extremely easy to use
  - creation of these modules is done in pure python, without a source  
code template step (but .py files can be automatically generated by  
inspecting the module!)
  - compilation is done as a generator that yields status messages  
(basically right now essentially spits out documentation for the  
package while it's compiling)
  - it has a pretty scary class/suite dependency resolution algorithm,  
so that classes in the aete are actually subclasses of their parent,  
even the implicit ones (by way of the same code, as in subclassing capp  
implicitly)
  - it's self documenting, _every_ class has proper doc strings (or  
repr(), depending.. due to some scary descriptor tricks) that are  
actually generated from the aete!
  - it has a scary type registry that does just about everything you'd  
want it to with regard to packing/unpacking without a huge if:else: loop

Here are some of the disadvantages:
1 - it tries really hard to return objects that you want all the time  
(via __get__, __getitem__, etc.), this means it's chattier wrt to  
sending apple events.  this is hard to get around because python parses  
forward, not backwards like applescript :)
2 - this is typically a non-issue, but because you can't build complex  
descriptors (i.e. setting more than one property of a class) you can  
hit bottlenecks.  for example, iTunes, with my gigantic music library,  
takes a sweet second or more to update a single property of a single  
track (even from its own interface).  If you're updating a range of  
tracks from the interface, or more than one property in one fell swoop,  
it can be a faster process.
3 - it's very much incompatible with the current modules, and I don't  
have a clever new name for my version yet (currently it's just a  
package named aepy with very similar modules as gensuite and friends).
4 - there is no support for async methods

I'm not particularly interested in solving 1, 2, or 3 because I don't  
see them as real problems if 4 were to be implemented.  Solving 4 would  
be difficult, but not impossible.. Primarily because I have zero  
experience with Carbon event loops and I actually don't have a whole  
lot of experience with apple events in general, I'm just playing it by  
AEDebug, Script Editor, the existing code, pestering donovan, and  
guessing.

Here's some example interactive sessions so you can get an idea of what  
I'm talking about here:

( Note that all these examples are using unmodified but pregenerated  
packages generated by the introspector, so they import quickly.   
However, "on the fly" wrapping of an aete takes anywhere between ~0.5  
and 2.0 seconds on my 1ghz -- most of that time is spent waiting for  
the application to respond with the aete and generating egregious  
debugging messages.  I can optimize a lot of stuff after I get a better  
idea of what assumptions I can and can't make about apple events. )

[crack:~/src/aepy] bob% pythonw
Python 2.3b2 (#3, Jul  3 2003, 22:26:01)
[GCC 3.1 20020420 (prerelease)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
 >>> from Mail import *
 >>> m = Mail()
 >>> len(m.windows)
3
 >>> for w in m.windows:
...   print w.name
...
So, who's using apple events from python?
mac — bob at new.redivi.com
Rules
 >>> repr(m.__class__.windows)
'window by name, index, relative position, range, test, unique id'
 >>> w = m.windows.first
 >>> w.properties # This ought to be in english, but I'm afraid I'll  
have to special-case this descriptor hardcore since implicitly I only  
know it's an AERecord
{'prsz': True, 'pcls': Type(cwin), 'pvis': True, 'pidx': 1, 'pmod':  
False, 'pmnd': False, 'pbnd': QDRectangle(0, 84, 991, 1166), 'docu':  
Type(msng), 'hclb': True, 'isfl': False, 'ID  ': 3427, 'ismn': True,  
'pnam': u"So, who's using apple events from python?", 'iszm': True,  
'pzum': False, 'ptit': False}
 >>> w.class_
Type(cwin)
 >>> w.name, w.index, w.ID, w.name, w.modal
(u"So, who's using apple events from python?", 3, 3427, u"So, who's  
using apple events from python?", False)
 >>> m.accounts["bob at new.redivi.com"].mailboxes["lists/python/mac"]
mailbox(u'lists/python/mac', account(u'bob at new.redivi.com'))
 >>>  
m.accounts["bob at new.redivi.com"].mailboxes["lists/python/mac"].messages
mailbox(u'lists/python/mac',  
account(u'bob at new.redivi.com')).message(???)
 >>>  
len(m.accounts["bob at new.redivi.com"].mailboxes["lists/python/ 
mac"].messages)
191
 >>>  
m.accounts["bob at new.redivi.com"].mailboxes["lists/python/ 
mac"].messages.any.subject
u'Re: [Pythonmac-SIG] Apple Events / iTunes'
 >>>  
m.accounts["bob at new.redivi.com"].mailboxes["lists/python/ 
mac"].messages.any.subject
u'Re: [Pythonmac-SIG] which python pil?'
 >>> m.message_viewers.first
message_viewer(30799328)
 >>> v = m.message_viewers.first
 >>> v.is_sorted_ascending
True
 >>> v.selected_mailboxes
[mailbox(u'lists/python/mac', account(u'bob at new.redivi.com'))]
 >>> v.visible_columns    # I'm sure these constants mean something, but  
not as far as I can tell wrt the aete
[1701014899, 1701013106, 1701016437, 1701012594, 1701011828, 1701013100]
 >>> v.sort_column
'date_received_column'


[crack:~/src/aepy] bob% pythonw
Python 2.3b2 (#3, Jul  3 2003, 22:26:01)
[GCC 3.1 20020420 (prerelease)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
 >>> from iTunes import *
 >>> i = iTunes()
 >>> i.source.first.playlist.first
library_playlist(7940, source(32))
 >>> len(i.source.first.playlist.first.tracks)
7907
 >>> print ' - '.join((i.current_track.name, i.current_track.artist,  
i.current_track.album))
kumari - Muslimgauze - Ingaza
 >>> p = i.source.first.playlist.first
 >>> p.track["kumari"]
file_track(15212, library_playlist(7940, source(32)))
 >>> p.track["kumari"].album
u'Ingaza'
 >>> p.duration
2441630
 >>> min, sec = divmod(p.duration, 60)
 >>> hr, min = divmod(min, 60)
 >>> day, hr = divmod(hr, 24)
 >>> print "%02d:%02d:%02d:%02d total time" % (day, hr, min, sec)
28:06:13:50 total time
 >>> p.track.first.location
<Carbon.File.Alias object at 0xc1140>
 >>> f = _
 >>> import Carbon.File
 >>> Carbon.File.pathname(f.FSResolveAlias(None)[0]).encode('utf8')
"/Volumes/Pipe/Music/ongaku/[electronica]/[The Orb]/[96 Auntie  
Aubrey's...]/[Disc Two]/01 You Gotta Say Yes To.mp3"

[crack:~/src/aepy] bob% pythonw
Python 2.3b2 (#3, Jul  3 2003, 22:26:01)
[GCC 3.1 20020420 (prerelease)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
 >>> from Finder import *
 >>> f = Finder()
 >>> f.startup_disk
disk(Type(sdsk))
 >>> f.desktop
desktop_object(Type(desk))
 >>> d = _
 >>> d.class_
Type(desk)
 >>> # note it's REALLY a cdsk, but the code is smart enough to work  
around this bug in finder! :)
...
 >>> unicode(d) # __unicode__ and __str__ try and find a pnam for things
u'Crack:Users:bob:Desktop:'
 >>> d.url
u'file://localhost/Users/bob/Desktop/'
 >>> import Carbon.File
 >>> d._as(Carbon.File.Alias) # look ma, intelligent type coercion!
<Carbon.File.Alias object at 0xc1140>
 >>> f = _
 >>> Carbon.File.pathname(f.FSResolveAlias(None)[0])
'/Users/bob/Desktop'

As you can see, this stuff is really cool and I look forward to  
releasing it.  It should be ready for general consumption in a few  
days.  Please forward any suggestions/ideas/etc to me ASAP before I get  
bored of Apple Events ;)  A good name for the new module would be  
greatly appreciated as well, because surely someone has legacy code  
that they still need and I don't want to stomp on any toes.

I also hate Carbon.File with a passion, as soon as I get back I'm going  
to pester him about putting bidirectional unix path conversions to and  
from Alias, FSSpec, and FSRef.  I understand that Alias is useful  
because it can describe files in a persistent way, but god damn are  
they annoying little beasts if you're a unix guy.

-bob



More information about the Pythonmac-SIG mailing list