Whitespace normalization.
[python/dscho.git] / Mac / Demo / applescript.html
blob014e2ad8969216a12d8da3f7482b9b906ea412f6
1 <!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
2 <html><head><title>Using the Open Scripting Architecture from Python</title></head>
3 <body>
4 <h1>Using the Open Scripting Architecture from Python</h1>
5 <hr>
7 <p>OSA support in Python is still not 100% complete, but
8 there is already enough in place to allow you to do some nifty things
9 with other programs from your python program. </p>
12 <p>
13 In this example, we will look at a scriptable application, extract its
14 &#8220;AppleScript Dictionary,&#8221; generate a Python interface package from
15 the dictionary, and use that package to control the application.
16 The application we are going to script is Disk Copy, Apple's standard
17 utility for making copies of floppies, creating files that are mountable
18 as disk images, etc.
19 Because we want
20 to concentrate on the OSA details, we won&#8217;t bother with a real
21 user-interface for our application. </p>
24 <p>
25 <em>When we say &#8220;AppleScript&#8221; in this document we actually mean
26 &#8220;the Open Scripting Architecture.&#8221; There is nothing
27 AppleScript-specific in the Python implementation. Most of this document
28 focuses on the classic Mac OS; <a href="#osx">Mac OS X</a> users have some
29 additional tools.</em>
30 </p>
32 <h2>Python OSA architecture</h2>
34 <p>Open Scripting suites and inheritance can be modelled rather nicely
35 with Python packages, so we generate
36 a package for each application we want to script. Each suite defined in
37 the application becomes a module in the
38 package, and the package main module imports everything from all the
39 submodules and glues together all the classes (in Python terminology&#8212;
40 events in OSA terminology or verbs in AppleScript terminology). </p>
42 <p>
43 A suite in an OSA application can extend the functionality of a standard
44 suite. This is implemented in Python by importing everything from the
45 module that implements the standard suites and overriding anything that has
46 been extended. The standard suites live in the StdSuite package. </p>
48 <p>
49 This all sounds complicated, but the good news is that basic
50 scripting is actually pretty simple. You can do strange and wondrous things
51 with OSA scripting once you fully understand it. </p>
53 <h2>Creating the Python interface package</h2>
56 <p>There is a tool in the standard distribution that can automatically
57 generate the interface packages. This tool is called
58 <code>gensuitemodule.py</code>, and lives in <code>Mac:scripts</code>.
59 It looks through a file
60 for an &#8216;AETE&#8217; or &#8216;AEUT&#8217; resource,
61 the internal representation of the
62 AppleScript dictionary, and parses the resource to generate the suite
63 modules.
64 When we start <code>gensuitemodule</code>, it asks us for an input file;
65 for our example,
66 we point it to the Disk Copy executable. </p>
68 <p>
69 Next, <code>gensuitemodule</code> wants a folder where it will store the
70 package it is going to generate.
71 Note that this is the package folder, not the parent folder, so we
72 navigate to <code>Python:Mac:Demo:applescript</code>, create a folder
73 <code>Disk_Copy</code>, and select that. </p>
75 <p>
76 We next specify the folder from which <code>gensuitemodule</code>
77 should import the standard suites. Here,
78 we always select <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>. (There is
79 one exception to this rule: when you are generating <code>StdSuites</code> itself
80 you select <code>_builtinSuites</code>.)
81 </p>
83 <p>
84 It starts parsing the AETE resource, and for
85 each AppleEvent suite it finds, <code>gensuitemodule.py</code>
86 prompts us for the filename of the
87 resulting python module. Remember to change folders for the first
88 module&#8212;you don't want to clutter up, say, the
89 Disk Copy folder
90 with your python
91 interfaces. If you want to skip a suite, press <code>cancel</code> and the process
92 continues with the next suite. </p>
94 <h3>Summary</h3>
96 <ol>
98 <li>Run <code>gensuitemodule</code>.</li>
100 <li>Select the application (or OSAX) for which you would like a Python interface.</li>
102 <li>Select the package folder where the interface modules should be
103 stored.</li>
105 <li>Specify the folder <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>
106 to import the standard suites (or <code>_builtinSuites</code> if you are
107 generating <code>StdSuites</code> itself). </li>
109 <li>Save the generated suites (use <code>cancel</code> to skip a suite).</li>
112 </ol>
115 <h3>Notes</h3>
118 <ul>
120 <li>The interface package may occasionally need some editing by hand. For example,
121 <code>gensuitemodule</code> does not handle all Python reserved words, so
123 one of the AppleScript verbs is a Python reserved word, a <code>SyntaxError</code>
124 may be raised when the package is imported.
125 Simply rename the class into something acceptable, if this happens;
126 take a look at how the
127 <code>print</code> verb is handled (automatically by <code>gensuitemodule</code>)
128 in the standard suites. But: f you need to edit your package this should be considered a
129 bug in gensuitemodule, so please report it so it can be fixed in future releases.
130 </li>
133 <li>If you want to re-create the StdSuite modules,
134 you should look in one of two places. With versions of AppleScript older than 1.4.0
135 (which first shipped with OS 9.0), you will find the
136 AEUT resources in <code>System Folder:Extensions:Scripting
137 Additions:Dialects:English Dialect</code>. For newer versions, you will
138 find them in <code>System Folder:Extensions:Applescript</code>.
139 </li>
141 <li>Since MacPython 2.0, this new structure, with packages
142 per application and submodules per suite, is used. Older MacPythons had a
143 single level of modules, with uncertain semantics. With the new structure,
144 it is possible for programs to override standard suites, as programs often do.
146 </li>
148 <li><code>Gensuitemodule.py</code> may ask you questions
149 like &#8220;Where is enum 'xyz ' declared?&#8221;.
150 This is either due to a misunderstanding on my part or (rather too commonly)
151 bugs in the AETE resources. Pressing <code>cancel</code> is usually the
152 right choice: it will cause the specific enum not to be treated as an enum
153 but as a &#8220;normal&#8221; type. As things like fsspecs and TEXT strings clearly are
154 not enumerators, this is correct. If someone understands what is really going on
155 here, please let me know.</li>
157 </ul>
161 <h2>The Python interface package contents</h2>
164 Let&#8217;s glance at the
165 <a href="applescript/Disk_Copy">Disk_Copy</a> package just created. You
166 may want to open Script Editor alongside to see how it
167 interprets the dictionary.
168 </p>
172 The main package module is in <code>__init__.py</code>.
173 The only interesting bit is the <code>Disk_Copy</code> class, which
174 includes the event handling classes from the individual suites. It also
175 inherits <code>aetools.TalkTo</code>, which is a base class that handles all
176 details on how to start the program and talk to it, and a class variable
177 <code>_signature</code> which is the default application this class will talk
178 to (you can override this in various ways when you instantiate your class, see
179 <code>aetools.py</code> for details).
180 </p>
183 The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
184 module is a nice example of a suite module.
185 The <code>Special_Events_Events</code> class is the bulk of the code
186 generated. For each verb, it contains a method. Each method knows what
187 arguments the verb expects, and it makes use of keyword
188 arguments to present a palatable
189 interface to the python programmer.
191 Notice that each method
192 calls some routines from <code>aetools</code>, an auxiliary module
193 living in <code>Mac:Lib</code>.
194 The other thing to notice is that each method calls
195 <code>self.send</code>. This comes from the <code>aetools.TalkTo</code>
196 baseclass. </p>
200 After the big class, there are a number of little class declarations. These
201 declarations are for the (AppleEvent) classes and properties in the suite.
202 They allow you to create object IDs, which can then be passed to the verbs.
203 For instance,
204 when scripting the popular email program Eudora,
205 you would use <code>mailbox("inbox").message(1).sender</code>
206 to get the name of the sender of the first message in mailbox
207 inbox. It is
208 also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
209 which is sometimes needed because these classes don&#8217;t always inherit correctly
210 from baseclasses, so you may have to use a class or property from another
211 suite. </p>
214 Next we get the enumeration dictionaries, which allow you to pass
215 english names as arguments to verbs, so you don't have to bother with the 4-letter
216 type code. So, you can say
217 <code>
218 diskcopy.create(..., filesystem="Mac OS Standard")
219 </code>
220 as it is called in Script Editor, instead of the cryptic lowlevel
221 <code>
222 diskcopy.create(..., filesystem="Fhfs")
223 </code></p>
226 Finally, we get the &#8220;table of contents&#8221; of the module, listing all
227 classes and such
228 by code, which is used by <code>gensuitemodule</code> itself: if you use this
229 suite as a base package in a later run this is how it knows what is defined in this
230 suite, and what the Python names are.
231 </p>
233 <h3>Notes</h3>
235 <ul>
237 <li>The <code>aetools</code> module contains some other nifty
238 AppleEvent tools as well. Have a look at it sometime, there is (of
239 course) no documentation yet.
240 </li>
242 <li>There are also some older object specifiers for standard objects in aetools.
243 You use these in the form <code>aetools.Word(10,
244 aetools.Document(1))</code>, where the corresponding AppleScript
245 terminology would be <code>word 10 of the first
246 document</code>. Examine
247 <code>aetools</code> and <code>aetools.TalkTo</code>
248 along with
249 the comments at the end of your suite module if you need to create
250 more than the standard object specifiers.
251 </li>
253 </ul>
258 <h2>Using a Python suite module</h2>
261 Now that we have created the suite module, we can use it in a Python script.
262 In older MacPython distributions this used to be a rather
263 complicated affair, but with the package scheme and with the application signature
264 known by the package it is very simple: you import the package and instantiate
265 the class, e.g.
266 <code>
267 talker = Disk_Copy.Disk_Copy(start=1)
268 </code>
269 You will usually specify the <code>start=1</code>: it will run the application if it is
270 not already running.
271 You may want to omit it if you want to talk to the application
272 only if it is already running, or if the application is something like the Finder.
273 Another way to ensure that the application is running is to call <code>talker._start()</code>.
274 </p>
277 Looking at the sourcefile <a
278 href="applescript/makedisk.py">makedisk.py</a>, we see that it starts
279 with some imports. Naturally, one of these is the Python interface to Disk
280 Copy.</p>
283 The main program itself is a wonder of simplicity: we create the
284 object (<code>talker</code>) that talks to Disk Copy,
285 create a disk, and mount it. The bulk of
286 the work is done by <code>talker</code> and the Python interface package we
287 just created.</p>
290 The exception handling does warrant a few comments, though. Since
291 AppleScript is basically a connectionless RPC protocol,
292 nothing happens
293 when we create the <code>talker</code> object. Hence, if the destination application
294 is not running, we will not notice until we send our first
295 command (avoid this as described above). There is another thing to note about errors returned by
296 AppleScript calls: <code>MacOS.Error</code> is raised for
297 all of the errors that are known to be <code>OSErr</code>-type errors,
298 while
299 server generated errors raise <code>aetools.Error</code>. </p>
301 <h2>Scripting Additions</h2>
304 If you want to use any of the scripting additions (or OSAXen, in
305 everyday speech) from a Python program, you can use the same method
306 as for applications, i.e. run <code>gensuitemodule</code> on the
307 OSAX (commonly found in <code>System Folder:Scripting Additions</code>
308 or something similar). There is one minor gotcha: the application
309 signature to use is <code>MACS</code>. You will need to edit the main class
310 in the <code>__init__.py</code> file of the created package and change the value
311 of <code>_signature</code> to <code>MACS</code>, or use a subclass to the
312 same effect.
313 </p>
316 There are two minor points to watch out for when using <code>gensuitemodule</code>
317 on OSAXen: they appear all to define the class <code>System_Object_Suite</code>,
318 and a lot of them have the command set in multiple dialects. You have to
319 watch out for name conflicts and make sure you select a reasonable dialect
320 (some of the non-English dialects cause <code>gensuitemodule</code> to generate incorrect
321 Python code). </p>
323 Despite these difficulties, OSAXen offer a lot of possibilities. Take a
324 look at some of the OSAXen in the Scripting Additions folder, or
325 <A HREF="http://www.osaxen.com/index.php">download</A> some from the net.
327 <h2>Further Reading</h2>
330 If you want to look at more involved examples of applescripting, look at the standard
331 modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
332 is more involved) <code>fullbuild</code> from the <code>Mac:scripts</code> folder.
333 </p>
335 <h2><a name="alternatives">Alternatives</a></h2>
337 <h3><a name="osx">Mac OS X</a></h3>
340 Under Mac OS X, the above still works, but with some new difficulties.
341 The application package structure can hide the &#8216;AETE&#8217; or
342 &#8216;AEUT&#8217; resource from <code>gensuitemodule</code>, so that,
343 for example, it cannot generate an OSA interface to iTunes. Script
344 Editor gets at the dictionary of such programs using a &#8216;Get
345 AETE&#8217; AppleEvent, if someone wants to donate code to use the same
346 method for gensuitemodule: by all means!
347 </p>
350 One alternative is available through the Unix command line version of python.
351 Apple has provided the <code>osacompile</code> and <code>osascript</code> tools,
352 which can be used to compile and execute scripts written in OSA languages. See the
353 man pages for more details.
354 </p>
357 </body>
358 </html>