blob: 2a27a8a7dbe4ed36bf6237dce18ad4f83496d1d2 [file] [log] [blame]
Jack Jansenc7a7d2d2002-01-25 15:06:19 +00001<!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>
Jack Jansena6308131996-03-18 13:38:52 +00006
Jack Jansenc7a7d2d2002-01-25 15:06:19 +00007<p>OSA support in Python is still not 100% complete, but
Jack Jansena6308131996-03-18 13:38:52 +00008there is already enough in place to allow you to do some nifty things
Jack Jansenc7a7d2d2002-01-25 15:06:19 +00009with other programs from your python program. </p>
Jack Jansena6308131996-03-18 13:38:52 +000010
Jack Jansena6308131996-03-18 13:38:52 +000011
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000012<p>
Jack Jansena6308131996-03-18 13:38:52 +000013In this example, we will look at a scriptable application, extract its
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000014&#8220;AppleScript Dictionary,&#8221; generate a Python interface package from
15the dictionary, and use that package to control the application.
Jack Jansenc15ab032000-08-20 21:57:38 +000016The application we are going to script is Disk Copy, Apple's standard
17utility for making copies of floppies, creating files that are mountable
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000018as disk images, etc.
19Because we want
20to concentrate on the OSA details, we won&#8217;t bother with a real
21user-interface for our application. </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000022
Jack Jansenc15ab032000-08-20 21:57:38 +000023
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000024<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
27AppleScript-specific in the Python implementation. Most of this document
28focuses on the classic Mac OS; <a href="#osx">Mac OS X</a> users have some
29additional tools.</em>
30</p>
31
32<h2>Python OSA architecture</h2>
33
34<p>Open Scripting suites and inheritance can be modelled rather nicely
35with Python packages, so we generate
36a package for each application we want to script. Each suite defined in
37the application becomes a module in the
Jack Jansenc15ab032000-08-20 21:57:38 +000038package, and the package main module imports everything from all the
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000039submodules and glues together all the classes (in Python terminology&#8212;
40events in OSA terminology or verbs in AppleScript terminology). </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000041
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000042<p>
Jack Jansenc15ab032000-08-20 21:57:38 +000043A suite in an OSA application can extend the functionality of a standard
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000044suite. This is implemented in Python by importing everything from the
45module that implements the standard suites and overriding anything that has
46been extended. The standard suites live in the StdSuite package. </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000047
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000048<p>
49This all sounds complicated, but the good news is that basic
50scripting is actually pretty simple. You can do strange and wondrous things
51with OSA scripting once you fully understand it. </p>
Jack Jansena6308131996-03-18 13:38:52 +000052
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000053<h2>Creating the Python interface package</h2>
Jack Jansena6308131996-03-18 13:38:52 +000054
Jack Jansenc15ab032000-08-20 21:57:38 +000055
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000056<p>There is a tool in the standard distribution that can automatically
57generate the interface packages. This tool is called
58<code>gensuitemodule.py</code>, and lives in <code>Mac:scripts</code>.
59It looks through a file
60for an &#8216;AETE&#8217; or &#8216;AEUT&#8217; resource,
61the internal representation of the
62AppleScript dictionary, and parses the resource to generate the suite
63modules.
64When we start <code>gensuitemodule</code>, it asks us for an input file;
65for our example,
66we point it to the Disk Copy executable. </p>
67
68<p>
69Next, <code>gensuitemodule</code> wants a folder where it will store the
70package it is going to generate.
Jack Jansenc15ab032000-08-20 21:57:38 +000071Note that this is the package folder, not the parent folder, so we
72navigate to <code>Python:Mac:Demo:applescript</code>, create a folder
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000073<code>Disk_Copy</code>, and select that. </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000074
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000075<p>
76We next specify the folder from which <code>gensuitemodule</code>
77should import the standard suites. Here,
78we always select <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>. (There is
Jack Jansenc15ab032000-08-20 21:57:38 +000079one exception to this rule: when you are generating <code>StdSuites</code> itself
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000080<!-- you select <code>cancel</code>.) -->
81you select <code>_builtinSuites</code>.)
82</p>
Jack Jansenc15ab032000-08-20 21:57:38 +000083
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000084<p>
Jack Jansenc15ab032000-08-20 21:57:38 +000085It starts parsing the AETE resource, and for
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000086each AppleEvent suite it finds, <code>gensuitemodule.py</code>
87prompts us for the filename of the
Jack Jansenf10786b1997-08-19 14:00:56 +000088resulting python module. Remember to change folders for the first
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000089module&#8212;you don't want to clutter up, say, the
90Eudora folder
91<!--Why Eudora folder? Was there an earlier example featuring Eudora?
92Added "say" to make it seem less specific.-->
93with your python
94interfaces. If you want to skip a suite, press <code>cancel</code> and the process
95continues with the next suite. </p>
Jack Jansena6308131996-03-18 13:38:52 +000096
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000097<h3>Summary</h3>
98
99<ol>
100
101 <li>Run <code>gensuitemodule</code>.</li>
102
103 <li>Select the application (or OSAX) for which you would like a Python interface.</li>
104
105 <li>Select the package folder where the interface modules should be
106 stored.</li>
107
108 <li>Specify the folder <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>
109<!-- to import the standard suites (or <code>cancel</code> if you are -->
110 to import the standard suites (or <code>_builtinSuites</code> if you are
111 generating <code>StdSuites</code> itself). </li>
112
113 <li>Save the generated suites (use <code>cancel</code> to skip a suite).</li>
114
115
116</ol>
117
118
119<h3>Notes</h3>
120
121
122<ul>
123
124 <li>The interface package may need some editing by hand. For example,
125 <code>gensuitemodule</code> does not handle all Python reserved words, so
126 if
127 one of the AppleScript verbs is a Python reserved word, a <code>SyntaxError</code>
128 may be raised when the package is imported.
129 Simply rename the class into something acceptable, if this happens;
130 take a look at how the
131 <code>print</code> verb is handled (automatically by <code>gensuitemodule</code>)
132 in the standard suites. </li>
133
134
135 <li>If you want to re-create the StdSuite modules,
136you should look in one of two places. With versions of AppleScript older than 1.4.0
137(which first shipped with OS 9.0), you will find the
138AEUT resources in <code>System Folder:Extensions:Scripting
139Additions:Dialects:English Dialect</code>. For newer versions, you will
140find them in <code>System Folder:Extensions:Applescript</code>.
141</li>
142
143 <li>Since MacPython 2.0, this new structure, with packages
144per application and submodules per suite, is used. Older MacPythons had a
145single level of modules, with uncertain semantics. With the new structure,
146it is possible for programs to override standard suites, as programs often do.
147It is a good idea to convert your own old programs to the new scheme.
148<!-- , but if you -->
149<!-- really want them, the old standard suites are still available in -->
150<!-- <code>:Mac:Lib:lib-scripting</code>. -->
151<!-- Seems to be gone in MacPython 2.2 -->
152
153</li>
154
155<li><code>Gensuitemodule.py</code> may ask you questions
156like &#8220;Where is enum 'xyz ' declared?&#8221;.
157This is either due to a misunderstanding on my part or (rather too commonly)
Jack Jansenc15ab032000-08-20 21:57:38 +0000158bugs in the AETE resources. Pressing <code>cancel</code> is usually the
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000159right choice: it will cause the specific enum not to be treated as an enum
160but as a &#8220;normal&#8221; type. As things like fsspecs and TEXT strings clearly are
161not enumerators, this is correct. If someone understands what is really going on
162here, please let me know.</li>
Jack Jansenf10786b1997-08-19 14:00:56 +0000163
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000164</ul>
Jack Jansena6308131996-03-18 13:38:52 +0000165
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000166
167
168<h2>The Python interface package contents</h2>
169
170<p>
171Let&#8217;s glance at the
172<a href="applescript/Disk_Copy">Disk_Copy</a> package just created. You
173may want to open Script Editor alongside to see how it
174interprets the dictionary.
175</p>
176
177
178<p>
179The main package module is in <code>__init__.py</code>.
180The only interesting bit is the <code>Disk_Copy</code> class, which
Jack Jansenc15ab032000-08-20 21:57:38 +0000181includes the event handling classes from the individual suites. It also
182inherits <code>aetools.TalkTo</code>, which is a base class that handles all
183details on how to start the program and talk to it, and a class variable
184<code>_signature</code> which is the default application this class will talk
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000185to (you can override this in various ways when you instantiate your class, see
Jack Jansenc15ab032000-08-20 21:57:38 +0000186<code>aetools.py</code> for details).
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000187</p>
Jack Jansena6308131996-03-18 13:38:52 +0000188
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000189<p>
Jack Jansenc15ab032000-08-20 21:57:38 +0000190The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
191module is a nice example of a suite module.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000192The <code>Special_Events_Events</code> class is the bulk of the code
193generated. For each verb, it contains a method. Each method knows what
194arguments the verb expects, and it makes use of keyword
Jack Jansenc15ab032000-08-20 21:57:38 +0000195arguments to present a palatable
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000196interface to the python programmer.
Jack Jansena6308131996-03-18 13:38:52 +0000197
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000198Notice that each method
199calls some routines from <code>aetools</code>, an auxiliary module
200living in <code>Mac:Lib</code>.
201The other thing to notice is that each method calls
202<code>self.send</code>. This comes from the <code>aetools.TalkTo</code>
203baseclass. </p>
Jack Jansena6308131996-03-18 13:38:52 +0000204
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000205
206<p>
207After the big class, there are a number of little class declarations. These
208declarations are for the (AppleEvent) classes and properties in the suite.
Jack Jansenf10786b1997-08-19 14:00:56 +0000209They allow you to create object IDs, which can then be passed to the verbs.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000210For instance,
211<!--Is this for Eudora again? I'll assume so...-->
212when scripting the popular email program Eudora,
213you would use <code>mailbox("inbox").message(1).sender</code>
214to get the name of the sender of the first message in mailbox
215inbox. It is
Jack Jansenf10786b1997-08-19 14:00:56 +0000216also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000217which is sometimes needed because these classes don&#8217;t always inherit correctly
218from baseclasses, so you may have to use a class or property from another
219suite. </p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000220
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000221<p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000222Next we get the enumeration dictionaries, which allow you to pass
223english names as arguments to verbs, so you don't have to bother with the 4-letter
224type code. So, you can say
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000225<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000226 diskcopy.create(..., filesystem="Mac OS Standard")
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000227</code>
228as it is called in Script Editor, instead of the cryptic lowlevel
229<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000230 diskcopy.create(..., filesystem="Fhfs")
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000231</code></p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000232
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000233<p>
234Finally, we get the &#8220;table of contents&#8221; of the module, listing all
235classes and such
236by code, which is used by <code>gensuitemodule</code>.
237<!--
238 | Not sure I understand. Is the code used by gensuitemodule, or is the TOC
239 | module used by gensuitemodule?
240-->
241</p>
Jack Jansena6308131996-03-18 13:38:52 +0000242
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000243<h3>Notes</h3>
Jack Jansena6308131996-03-18 13:38:52 +0000244
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000245<ul>
246
247 <li>The <code>aetools</code> module contains some other nifty
248AppleEvent tools as well. Have a look at it sometime, there is (of
249course) no documentation yet.
250</li>
251
252 <li>There are also some older object specifiers for standard objects in aetools.
253You use these in the form <code>aetools.Word(10,
254aetools.Document(1))</code>, where the corresponding AppleScript
255terminology would be <code>word 10 of the first
256document</code>. Examine
257<code>aetools</code> and <code>aetools.TalkTo</code>
258along with
259the comments at the end of your suite module if you need to create
260more than the standard object specifiers.
261</li>
262
263</ul>
Jack Jansena6308131996-03-18 13:38:52 +0000264
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000265
266
267
268<h2>Using a Python suite module</h2>
269
270<p>
271Now that we have created the suite module, we can use it in a Python script.
Jack Jansenc15ab032000-08-20 21:57:38 +0000272In older MacPython distributions this used to be a rather
273complicated affair, but with the package scheme and with the application signature
274known by the package it is very simple: you import the package and instantiate
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000275the class, e.g.
276<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000277 talker = Disk_Copy.Disk_Copy(start=1)
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000278</code>
279You will usually specify the <code>start=1</code>: it will run the application if it is
280not already running.
281You may want to omit it if you want to talk to the application
282only if it is already running, or if the application is something like the Finder.
283Another way to ensure that the application is running is to call <code>talker.start()</code>.
284</p>
Jack Jansena6308131996-03-18 13:38:52 +0000285
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000286<p>
287Looking at the sourcefile <a
288href="applescript/makedisk.py">makedisk.py</a>, we see that it starts
289with some imports. Naturally, one of these is the Python interface to Disk
290Copy.</p>
Jack Jansenbdf03a01996-09-20 15:22:47 +0000291
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000292<p>
293The main program itself is a wonder of simplicity: we create the
294object (<code>talker</code>) that talks to Disk Copy,
295create a disk, and mount it. The bulk of
296the work is done by <code>talker</code> and the Python interface package we
297just created.</p>
Jack Jansena6308131996-03-18 13:38:52 +0000298
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000299<p>
300The exception handling does warrant a few comments, though. Since
301AppleScript is basically a connectionless RPC protocol,
302nothing happens
303when we create the <code>talker</code> object. Hence, if the destination application
304is not running, we will not notice until we send our first
305command (avoid this as described above). There is another thing to note about errors returned by
306AppleScript calls: <code>MacOS.Error</code> is raised for
307all of the errors that are known to be <code>OSErr</code>-type errors,
308while
309server generated errors raise <code>aetools.Error</code>. </p>
Jack Jansena6308131996-03-18 13:38:52 +0000310
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000311<h2>Scripting Additions</h2>
Jack Jansena742d111996-12-23 17:28:53 +0000312
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000313<p>
Jack Jansena742d111996-12-23 17:28:53 +0000314If you want to use any of the scripting additions (or OSAXen, in
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000315everyday speech) from a Python program, you can use the same method
316as for applications, i.e. run <code>gensuitemodule</code> on the
317OSAX (commonly found in <code>System Folder:Scripting Additions</code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000318or something similar). There is one minor gotcha: the application
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000319signature to use is <code>MACS</code>. You will need to edit the main class
320in the <code>__init__.py</code> file of the created package and change the value
321of <code>_signature</code> to <code>MACS</code>.
322</p>
Jack Jansena742d111996-12-23 17:28:53 +0000323
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000324<p>
325There are two minor points to watch out for when using <code>gensuitemodule</code>
326on OSAXen: they appear all to define the class <code>System_Object_Suite</code>,
Jack Jansena742d111996-12-23 17:28:53 +0000327and a lot of them have the command set in multiple dialects. You have to
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000328watch out for name conflicts and make sure you select a reasonable dialect
329(some of the non-English dialects cause <code>gensuitemodule</code> to generate incorrect
330Python code). </p>
Jack Jansena742d111996-12-23 17:28:53 +0000331
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000332Despite these difficulties, OSAXen offer a lot of possibilities. Take a
333look at some of the OSAXen in the Scripting Additions folder, or
334<A HREF="http://www.osaxen.com/index.php">download</A> some from the net.
Jack Jansenc15ab032000-08-20 21:57:38 +0000335
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000336<h2>Further Reading</h2>
337
338<p>
339If you want to look at more involved examples of applescripting, look at the standard
Jack Jansenc15ab032000-08-20 21:57:38 +0000340modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000341is more involved) <code>fullbuild</code> from the <code>Mac:scripts</code> folder.
342</p>
343
344<h2><a name="alternatives">Alternatives</a></h2>
345
346<h3><a name="osx">Mac OS X</a></h3>
347
348<p>
349Under Mac OS X, the above still works, but with some new difficulties.
350The application package structure can hide the &#8216;AETE&#8217; or &#8216;AEUT&#8217; resource
351from <code>gensuitemodule</code>, so that, for example, it cannot generate an OSA interface to
352iTunes.
353</p>
354
355<p>
356One alternative is available through the Unix command line version of python.
357Apple has provided the <code>osacompile</code> and <code>osascript</code> tools,
358which can be used to compile and execute scripts written in OSA languages. See the
359man pages for more details.
360</p>
361
362
363</body>
364</html>