Actually, when we say "AppleScript" in this document we actually mean
"the Open Scripting Architecture", there is nothing
AppleScript-specific in the Python implementation.
The application we are going to script is Disk Copy, Apple's standard utility for making copies of floppies, creating files that are mountable as disk images, etc.
A suite in an OSA application can extend the functionality of a standard suite, and this is implemented in Python by importing everything from the module that implements the standard suite and overriding anything that has been extended. The standard suites live in the StdSuite package.
This all sounds complicated, and you can do strange and wondrous things with it once you fully understand it, but the good news is that simple scripting is actually pretty simple.
gensuitemodule.py
, and lives in Mac:scripts
.
When we start it, it asks us for an input file and we point it to the
Disk Copy executable.
Next it wants a folder where it will store the package it is going to generate.
Note that this is the package folder, not the parent folder, so we
navigate to Python:Mac:Demo:applescript
, create a folder
Disk_Copy
and select that.
Next it wants the folder from which it should import the standard suites. Here
you always select Python:Mac:Lib:lib-scriptpackages
. (There is
one exception to this rule: when you are generating StdSuites
itself
you select cancel
, for obvious reasons).
It starts parsing the AETE resource, and for each AppleEvent suite it finds it prompts us for the filename of the resulting python module. Remember to change folders for the first module, you don't want to clutter up the Eudora folder with your python interfaces. If you want to skip a suite you press cancel and the process continues with the next suite.
Gensuitemodule may ask you questions like "Where is enum 'xyz ' declared?".
This is either due to a misunderstanding on my part or (rather too common)
bugs in the AETE resources. Pressing cancel
is usually the
right option, it will cause the specific enum not to be treated as an enum
but as a "normal" type. As things like fsspecs and TEXT strings clearly are
not enumerators this is correct. If someone understands what is really going on
here please let me know.
Time for a sidebar. If you want to re-create the StdSuite modules you should look in one of two places. On older systems you will find the AEUT resources inLet's glance at the Disk_Copy package just created. You may want to open Script Editor alongside, and have a look at how it interprets the dictionary. The main package module is inSystem Folder:Extensions:Scripting Additions:Dialects:English Dialect
. On newer systems you will find them inSystem Folder:Extensions:Applescript
.
__init__.py
and the only interesting bit is the Disk_Copy
class, which
includes the event handling classes from the individual suites. It also
inherits aetools.TalkTo
, which is a base class that handles all
details on how to start the program and talk to it, and a class variable
_signature
which is the default application this class will talk
to (you can override this in various when you instantiate your class, see
aetools.py
for details).
The Special_Events
module is a nice example of a suite module.
The Special_Events_Events
class is the bulk of the code
generated. For each verb it contains a method. Each method knows what
arguments the verb expects, and it makes handy use of keyword
arguments to present a palatable
interface to the python programmer. You will see that each method
calls some routines from aetools
, an auxiliary module
living in Lib:toolbox
which contains some other nifty
AppleEvent tools as well. Have a look at it sometime, there is (of
course) no documentation yet.
The other thing you notice is that each method calls
self.send
, this comes from the aetools.TalkTo
baseclass.
After the big class we get a number of little class declarations. These
declarations are for the (appleevent) classes and properties in the suite.
They allow you to create object IDs, which can then be passed to the verbs.
For instance, to get the name of the sender of the first message in mailbox
inbox you would use mailbox("inbox").message(1).sender
. It is
also possible to specify this as sender(message(1, mailbox("inbox")))
,
which is sometimes needed because these classes don't always inherit correctly
from baseclasses, so you may have to use a class or property from another suite.
There are also some older object specifiers for standard objects in aetools. You use these in the formNext we get the enumeration dictionaries, which allow you to pass english names as arguments to verbs, so you don't have to bother with the 4-letter type code. So, you can sayaetools.Word(10, aetools.Document(1))
where the corresponding AppleScript terminology would beword 10 of the first document
. Examine the two modules mentioned above along with the comments at the end of your suite module if you need to create more than the standard object specifiers.
diskcopy.create(..., filesystem="Mac OS Standard")
as it is called in Script Editor, in stead of the cryptic lowlevel
diskcopy.create(..., filesystem="Fhfs")
Finally, we get the "table of contents" of the module, listing all classes and such by code, which is used by gensuitemodule.
talker = Disk_Copy.Disk_Copy(start=1)
You will usually specify the start=1: it will run the application if it is
not already running. You may want to omit it if you want to talk to the application
only if it is already running, or if the application is something like the Finder. Looking at the sourcefile makedisk.py we see that it starts with some imports. The main program itself is a wonder of simplicity. We create the object that talks to Disk Copy, creates a disk and mounts it.
The exception handling does need a few comments, though. Since
AppleScript is basically a connectionless RPC protocol nothing happens
when we create to talker object. Hence, if the destination application
is not running we will not notice until we send our first
command. There is another thing to note about errors returned by
AppleScript calls: MacOS.Error
is raised for
all of the errors that are known to be OSErr
-type errors,
server generated errors raise aetools.Error
.
gensuitemodule
on the
OSAX (commonly found in System Folder:Extensions:Scripting Additions
or something similar). There is one minor gotcha: the application
signature to use is 'MACS'
.
There are two minor points to watch out for when using gensuitemodule
on OSAXen: they appear all to define the class System_Object_Suite
,
and a lot of them have the command set in multiple dialects. You have to
watch out for name conflicts, so, and make sure you select a reasonable dialect
(some of the non-english dialects cause gensuitemodule to generate incorrect
Python code).
findertools
and nsremote
, or (possibly better, as it
is more involved) fullbuild
from the Mac:scripts folder.