blob: 014e2ad8969216a12d8da3f7482b9b906ea412f6 [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 +000080you select <code>_builtinSuites</code>.)
81</p>
Jack Jansenc15ab032000-08-20 21:57:38 +000082
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000083<p>
Jack Jansenc15ab032000-08-20 21:57:38 +000084It starts parsing the AETE resource, and for
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000085each AppleEvent suite it finds, <code>gensuitemodule.py</code>
86prompts us for the filename of the
Jack Jansenf10786b1997-08-19 14:00:56 +000087resulting python module. Remember to change folders for the first
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000088module&#8212;you don't want to clutter up, say, the
Jack Jansen9051ad12002-01-25 15:28:39 +000089Disk Copy folder
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000090with your python
91interfaces. If you want to skip a suite, press <code>cancel</code> and the process
92continues with the next suite. </p>
Jack Jansena6308131996-03-18 13:38:52 +000093
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000094<h3>Summary</h3>
95
96<ol>
97
98 <li>Run <code>gensuitemodule</code>.</li>
99
100 <li>Select the application (or OSAX) for which you would like a Python interface.</li>
101
102 <li>Select the package folder where the interface modules should be
103 stored.</li>
104
105 <li>Specify the folder <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000106 to import the standard suites (or <code>_builtinSuites</code> if you are
107 generating <code>StdSuites</code> itself). </li>
108
109 <li>Save the generated suites (use <code>cancel</code> to skip a suite).</li>
110
111
112</ol>
113
114
115<h3>Notes</h3>
116
117
118<ul>
119
Jack Jansen9051ad12002-01-25 15:28:39 +0000120 <li>The interface package may occasionally need some editing by hand. For example,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000121 <code>gensuitemodule</code> does not handle all Python reserved words, so
122 if
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>)
Jack Jansen9051ad12002-01-25 15:28:39 +0000128 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>
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000131
132
133 <li>If you want to re-create the StdSuite modules,
134you 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
136AEUT resources in <code>System Folder:Extensions:Scripting
137Additions:Dialects:English Dialect</code>. For newer versions, you will
138find them in <code>System Folder:Extensions:Applescript</code>.
139</li>
140
141 <li>Since MacPython 2.0, this new structure, with packages
142per application and submodules per suite, is used. Older MacPythons had a
143single level of modules, with uncertain semantics. With the new structure,
144it is possible for programs to override standard suites, as programs often do.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000145
146</li>
147
148<li><code>Gensuitemodule.py</code> may ask you questions
149like &#8220;Where is enum 'xyz ' declared?&#8221;.
150This is either due to a misunderstanding on my part or (rather too commonly)
Jack Jansenc15ab032000-08-20 21:57:38 +0000151bugs in the AETE resources. Pressing <code>cancel</code> is usually the
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000152right choice: it will cause the specific enum not to be treated as an enum
153but as a &#8220;normal&#8221; type. As things like fsspecs and TEXT strings clearly are
154not enumerators, this is correct. If someone understands what is really going on
155here, please let me know.</li>
Jack Jansenf10786b1997-08-19 14:00:56 +0000156
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000157</ul>
Jack Jansena6308131996-03-18 13:38:52 +0000158
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000159
160
161<h2>The Python interface package contents</h2>
162
163<p>
164Let&#8217;s glance at the
165<a href="applescript/Disk_Copy">Disk_Copy</a> package just created. You
166may want to open Script Editor alongside to see how it
167interprets the dictionary.
168</p>
169
170
171<p>
172The main package module is in <code>__init__.py</code>.
173The only interesting bit is the <code>Disk_Copy</code> class, which
Jack Jansenc15ab032000-08-20 21:57:38 +0000174includes the event handling classes from the individual suites. It also
175inherits <code>aetools.TalkTo</code>, which is a base class that handles all
176details 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
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000178to (you can override this in various ways when you instantiate your class, see
Jack Jansenc15ab032000-08-20 21:57:38 +0000179<code>aetools.py</code> for details).
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000180</p>
Jack Jansena6308131996-03-18 13:38:52 +0000181
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000182<p>
Jack Jansenc15ab032000-08-20 21:57:38 +0000183The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
184module is a nice example of a suite module.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000185The <code>Special_Events_Events</code> class is the bulk of the code
186generated. For each verb, it contains a method. Each method knows what
187arguments the verb expects, and it makes use of keyword
Jack Jansenc15ab032000-08-20 21:57:38 +0000188arguments to present a palatable
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000189interface to the python programmer.
Jack Jansena6308131996-03-18 13:38:52 +0000190
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000191Notice that each method
192calls some routines from <code>aetools</code>, an auxiliary module
193living in <code>Mac:Lib</code>.
194The other thing to notice is that each method calls
195<code>self.send</code>. This comes from the <code>aetools.TalkTo</code>
196baseclass. </p>
Jack Jansena6308131996-03-18 13:38:52 +0000197
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000198
199<p>
200After the big class, there are a number of little class declarations. These
201declarations are for the (AppleEvent) classes and properties in the suite.
Jack Jansenf10786b1997-08-19 14:00:56 +0000202They allow you to create object IDs, which can then be passed to the verbs.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000203For instance,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000204when scripting the popular email program Eudora,
205you would use <code>mailbox("inbox").message(1).sender</code>
206to get the name of the sender of the first message in mailbox
207inbox. It is
Jack Jansenf10786b1997-08-19 14:00:56 +0000208also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000209which is sometimes needed because these classes don&#8217;t always inherit correctly
210from baseclasses, so you may have to use a class or property from another
211suite. </p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000212
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000213<p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000214Next we get the enumeration dictionaries, which allow you to pass
215english names as arguments to verbs, so you don't have to bother with the 4-letter
216type code. So, you can say
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000217<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000218 diskcopy.create(..., filesystem="Mac OS Standard")
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000219</code>
220as it is called in Script Editor, instead of the cryptic lowlevel
221<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000222 diskcopy.create(..., filesystem="Fhfs")
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000223</code></p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000224
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000225<p>
226Finally, we get the &#8220;table of contents&#8221; of the module, listing all
227classes and such
Jack Jansen9051ad12002-01-25 15:28:39 +0000228by code, which is used by <code>gensuitemodule</code> itself: if you use this
229suite as a base package in a later run this is how it knows what is defined in this
230suite, and what the Python names are.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000231</p>
Jack Jansena6308131996-03-18 13:38:52 +0000232
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000233<h3>Notes</h3>
Jack Jansena6308131996-03-18 13:38:52 +0000234
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000235<ul>
236
237 <li>The <code>aetools</code> module contains some other nifty
238AppleEvent tools as well. Have a look at it sometime, there is (of
239course) no documentation yet.
240</li>
241
242 <li>There are also some older object specifiers for standard objects in aetools.
243You use these in the form <code>aetools.Word(10,
244aetools.Document(1))</code>, where the corresponding AppleScript
245terminology would be <code>word 10 of the first
246document</code>. Examine
247<code>aetools</code> and <code>aetools.TalkTo</code>
248along with
249the comments at the end of your suite module if you need to create
250more than the standard object specifiers.
251</li>
252
253</ul>
Jack Jansena6308131996-03-18 13:38:52 +0000254
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000255
256
257
258<h2>Using a Python suite module</h2>
259
260<p>
261Now that we have created the suite module, we can use it in a Python script.
Jack Jansenc15ab032000-08-20 21:57:38 +0000262In older MacPython distributions this used to be a rather
263complicated affair, but with the package scheme and with the application signature
264known by the package it is very simple: you import the package and instantiate
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000265the class, e.g.
266<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000267 talker = Disk_Copy.Disk_Copy(start=1)
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000268</code>
269You will usually specify the <code>start=1</code>: it will run the application if it is
270not already running.
271You may want to omit it if you want to talk to the application
272only if it is already running, or if the application is something like the Finder.
Jack Jansen9051ad12002-01-25 15:28:39 +0000273Another way to ensure that the application is running is to call <code>talker._start()</code>.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000274</p>
Jack Jansena6308131996-03-18 13:38:52 +0000275
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000276<p>
277Looking at the sourcefile <a
278href="applescript/makedisk.py">makedisk.py</a>, we see that it starts
279with some imports. Naturally, one of these is the Python interface to Disk
280Copy.</p>
Jack Jansenbdf03a01996-09-20 15:22:47 +0000281
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000282<p>
283The main program itself is a wonder of simplicity: we create the
284object (<code>talker</code>) that talks to Disk Copy,
285create a disk, and mount it. The bulk of
286the work is done by <code>talker</code> and the Python interface package we
287just created.</p>
Jack Jansena6308131996-03-18 13:38:52 +0000288
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000289<p>
290The exception handling does warrant a few comments, though. Since
291AppleScript is basically a connectionless RPC protocol,
292nothing happens
293when we create the <code>talker</code> object. Hence, if the destination application
294is not running, we will not notice until we send our first
295command (avoid this as described above). There is another thing to note about errors returned by
296AppleScript calls: <code>MacOS.Error</code> is raised for
297all of the errors that are known to be <code>OSErr</code>-type errors,
298while
299server generated errors raise <code>aetools.Error</code>. </p>
Jack Jansena6308131996-03-18 13:38:52 +0000300
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000301<h2>Scripting Additions</h2>
Jack Jansena742d111996-12-23 17:28:53 +0000302
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000303<p>
Jack Jansena742d111996-12-23 17:28:53 +0000304If you want to use any of the scripting additions (or OSAXen, in
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000305everyday speech) from a Python program, you can use the same method
306as for applications, i.e. run <code>gensuitemodule</code> on the
307OSAX (commonly found in <code>System Folder:Scripting Additions</code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000308or something similar). There is one minor gotcha: the application
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000309signature to use is <code>MACS</code>. You will need to edit the main class
310in the <code>__init__.py</code> file of the created package and change the value
Jack Jansen9051ad12002-01-25 15:28:39 +0000311of <code>_signature</code> to <code>MACS</code>, or use a subclass to the
312same effect.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000313</p>
Jack Jansena742d111996-12-23 17:28:53 +0000314
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000315<p>
316There are two minor points to watch out for when using <code>gensuitemodule</code>
317on OSAXen: they appear all to define the class <code>System_Object_Suite</code>,
Jack Jansena742d111996-12-23 17:28:53 +0000318and a lot of them have the command set in multiple dialects. You have to
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000319watch 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
321Python code). </p>
Jack Jansena742d111996-12-23 17:28:53 +0000322
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000323Despite these difficulties, OSAXen offer a lot of possibilities. Take a
324look 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.
Jack Jansenc15ab032000-08-20 21:57:38 +0000326
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000327<h2>Further Reading</h2>
328
329<p>
330If you want to look at more involved examples of applescripting, look at the standard
Jack Jansenc15ab032000-08-20 21:57:38 +0000331modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000332is more involved) <code>fullbuild</code> from the <code>Mac:scripts</code> folder.
333</p>
334
335<h2><a name="alternatives">Alternatives</a></h2>
336
337<h3><a name="osx">Mac OS X</a></h3>
338
339<p>
340Under Mac OS X, the above still works, but with some new difficulties.
Jack Jansen9051ad12002-01-25 15:28:39 +0000341The application package structure can hide the &#8216;AETE&#8217; or
342&#8216;AEUT&#8217; resource from <code>gensuitemodule</code>, so that,
343for example, it cannot generate an OSA interface to iTunes. Script
344Editor gets at the dictionary of such programs using a &#8216;Get
345AETE&#8217; AppleEvent, if someone wants to donate code to use the same
346method for gensuitemodule: by all means!
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000347</p>
348
349<p>
350One alternative is available through the Unix command line version of python.
351Apple has provided the <code>osacompile</code> and <code>osascript</code> tools,
352which can be used to compile and execute scripts written in OSA languages. See the
353man pages for more details.
354</p>
355
356
357</body>
358</html>