[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