blob: 5e4fac7e33a3a6a90e0d7ba727c81c5c71dca802 [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
Thomas Wouters0e3f5912006-08-11 14:57:12 +00007<p><b>NOTE:</b> this document describes the OSA support that is shipped with
8the core python distribution. Most users are better of with the more
9userfriendly <a href="http://freespace.virgin.net/hamish.sanderson/appscript.html">appscript library</a>.
10
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000011<p>OSA support in Python is still not 100% complete, but
Jack Jansena6308131996-03-18 13:38:52 +000012there is already enough in place to allow you to do some nifty things
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000013with other programs from your python program. </p>
Jack Jansena6308131996-03-18 13:38:52 +000014
Jack Jansena6308131996-03-18 13:38:52 +000015
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000016<p>
Jack Jansena6308131996-03-18 13:38:52 +000017In this example, we will look at a scriptable application, extract its
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000018&#8220;AppleScript Dictionary,&#8221; generate a Python interface package from
19the dictionary, and use that package to control the application.
Jack Jansenc15ab032000-08-20 21:57:38 +000020The application we are going to script is Disk Copy, Apple's standard
21utility for making copies of floppies, creating files that are mountable
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000022as disk images, etc.
23Because we want
24to concentrate on the OSA details, we won&#8217;t bother with a real
25user-interface for our application. </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000026
Jack Jansenc15ab032000-08-20 21:57:38 +000027
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000028<p>
29<em>When we say &#8220;AppleScript&#8221; in this document we actually mean
30&#8220;the Open Scripting Architecture.&#8221; There is nothing
31AppleScript-specific in the Python implementation. Most of this document
32focuses on the classic Mac OS; <a href="#osx">Mac OS X</a> users have some
33additional tools.</em>
34</p>
35
36<h2>Python OSA architecture</h2>
37
38<p>Open Scripting suites and inheritance can be modelled rather nicely
39with Python packages, so we generate
40a package for each application we want to script. Each suite defined in
41the application becomes a module in the
Jack Jansenc15ab032000-08-20 21:57:38 +000042package, and the package main module imports everything from all the
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000043submodules and glues together all the classes (in Python terminology&#8212;
44events in OSA terminology or verbs in AppleScript terminology). </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000045
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000046<p>
Jack Jansenc15ab032000-08-20 21:57:38 +000047A suite in an OSA application can extend the functionality of a standard
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000048suite. This is implemented in Python by importing everything from the
49module that implements the standard suites and overriding anything that has
50been extended. The standard suites live in the StdSuite package. </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000051
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000052<p>
53This all sounds complicated, but the good news is that basic
54scripting is actually pretty simple. You can do strange and wondrous things
55with OSA scripting once you fully understand it. </p>
Jack Jansena6308131996-03-18 13:38:52 +000056
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000057<h2>Creating the Python interface package</h2>
Jack Jansena6308131996-03-18 13:38:52 +000058
Jack Jansenc15ab032000-08-20 21:57:38 +000059
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000060<p>There is a tool in the standard distribution that can automatically
61generate the interface packages. This tool is called
62<code>gensuitemodule.py</code>, and lives in <code>Mac:scripts</code>.
63It looks through a file
64for an &#8216;AETE&#8217; or &#8216;AEUT&#8217; resource,
65the internal representation of the
66AppleScript dictionary, and parses the resource to generate the suite
67modules.
68When we start <code>gensuitemodule</code>, it asks us for an input file;
69for our example,
70we point it to the Disk Copy executable. </p>
71
72<p>
73Next, <code>gensuitemodule</code> wants a folder where it will store the
74package it is going to generate.
Jack Jansenc15ab032000-08-20 21:57:38 +000075Note that this is the package folder, not the parent folder, so we
76navigate to <code>Python:Mac:Demo:applescript</code>, create a folder
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000077<code>Disk_Copy</code>, and select that. </p>
Jack Jansenc15ab032000-08-20 21:57:38 +000078
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000079<p>
80We next specify the folder from which <code>gensuitemodule</code>
81should import the standard suites. Here,
82we always select <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>. (There is
Jack Jansenc15ab032000-08-20 21:57:38 +000083one exception to this rule: when you are generating <code>StdSuites</code> itself
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000084you select <code>_builtinSuites</code>.)
85</p>
Jack Jansenc15ab032000-08-20 21:57:38 +000086
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000087<p>
Jack Jansenc15ab032000-08-20 21:57:38 +000088It starts parsing the AETE resource, and for
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000089each AppleEvent suite it finds, <code>gensuitemodule.py</code>
90prompts us for the filename of the
Jack Jansenf10786b1997-08-19 14:00:56 +000091resulting python module. Remember to change folders for the first
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000092module&#8212;you don't want to clutter up, say, the
Jack Jansen9051ad12002-01-25 15:28:39 +000093Disk Copy folder
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000094with your python
95interfaces. If you want to skip a suite, press <code>cancel</code> and the process
96continues with the next suite. </p>
Jack Jansena6308131996-03-18 13:38:52 +000097
Jack Jansenc7a7d2d2002-01-25 15:06:19 +000098<h3>Summary</h3>
99
100<ol>
101
102 <li>Run <code>gensuitemodule</code>.</li>
103
104 <li>Select the application (or OSAX) for which you would like a Python interface.</li>
105
106 <li>Select the package folder where the interface modules should be
107 stored.</li>
108
109 <li>Specify the folder <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000110 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
Jack Jansen9051ad12002-01-25 15:28:39 +0000124 <li>The interface package may occasionally need some editing by hand. For example,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000125 <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>)
Jack Jansen9051ad12002-01-25 15:28:39 +0000132 in the standard suites. But: f you need to edit your package this should be considered a
133 bug in gensuitemodule, so please report it so it can be fixed in future releases.
134 </li>
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000135
136
137 <li>If you want to re-create the StdSuite modules,
138you should look in one of two places. With versions of AppleScript older than 1.4.0
139(which first shipped with OS 9.0), you will find the
140AEUT resources in <code>System Folder:Extensions:Scripting
141Additions:Dialects:English Dialect</code>. For newer versions, you will
142find them in <code>System Folder:Extensions:Applescript</code>.
143</li>
144
145 <li>Since MacPython 2.0, this new structure, with packages
146per application and submodules per suite, is used. Older MacPythons had a
147single level of modules, with uncertain semantics. With the new structure,
148it is possible for programs to override standard suites, as programs often do.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000149
150</li>
151
152<li><code>Gensuitemodule.py</code> may ask you questions
153like &#8220;Where is enum 'xyz ' declared?&#8221;.
154This is either due to a misunderstanding on my part or (rather too commonly)
Jack Jansenc15ab032000-08-20 21:57:38 +0000155bugs in the AETE resources. Pressing <code>cancel</code> is usually the
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000156right choice: it will cause the specific enum not to be treated as an enum
157but as a &#8220;normal&#8221; type. As things like fsspecs and TEXT strings clearly are
158not enumerators, this is correct. If someone understands what is really going on
159here, please let me know.</li>
Jack Jansenf10786b1997-08-19 14:00:56 +0000160
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000161</ul>
Jack Jansena6308131996-03-18 13:38:52 +0000162
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000163
164
165<h2>The Python interface package contents</h2>
166
167<p>
168Let&#8217;s glance at the
169<a href="applescript/Disk_Copy">Disk_Copy</a> package just created. You
170may want to open Script Editor alongside to see how it
171interprets the dictionary.
172</p>
173
174
175<p>
176The main package module is in <code>__init__.py</code>.
177The only interesting bit is the <code>Disk_Copy</code> class, which
Jack Jansenc15ab032000-08-20 21:57:38 +0000178includes the event handling classes from the individual suites. It also
179inherits <code>aetools.TalkTo</code>, which is a base class that handles all
180details on how to start the program and talk to it, and a class variable
181<code>_signature</code> which is the default application this class will talk
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000182to (you can override this in various ways when you instantiate your class, see
Jack Jansenc15ab032000-08-20 21:57:38 +0000183<code>aetools.py</code> for details).
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000184</p>
Jack Jansena6308131996-03-18 13:38:52 +0000185
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000186<p>
Jack Jansenc15ab032000-08-20 21:57:38 +0000187The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
188module is a nice example of a suite module.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000189The <code>Special_Events_Events</code> class is the bulk of the code
190generated. For each verb, it contains a method. Each method knows what
191arguments the verb expects, and it makes use of keyword
Jack Jansenc15ab032000-08-20 21:57:38 +0000192arguments to present a palatable
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000193interface to the python programmer.
Jack Jansena6308131996-03-18 13:38:52 +0000194
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000195Notice that each method
196calls some routines from <code>aetools</code>, an auxiliary module
197living in <code>Mac:Lib</code>.
198The other thing to notice is that each method calls
199<code>self.send</code>. This comes from the <code>aetools.TalkTo</code>
200baseclass. </p>
Jack Jansena6308131996-03-18 13:38:52 +0000201
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000202
203<p>
204After the big class, there are a number of little class declarations. These
205declarations are for the (AppleEvent) classes and properties in the suite.
Jack Jansenf10786b1997-08-19 14:00:56 +0000206They allow you to create object IDs, which can then be passed to the verbs.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000207For instance,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000208when scripting the popular email program Eudora,
209you would use <code>mailbox("inbox").message(1).sender</code>
210to get the name of the sender of the first message in mailbox
211inbox. It is
Jack Jansenf10786b1997-08-19 14:00:56 +0000212also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000213which is sometimes needed because these classes don&#8217;t always inherit correctly
214from baseclasses, so you may have to use a class or property from another
215suite. </p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000216
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000217<p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000218Next we get the enumeration dictionaries, which allow you to pass
219english names as arguments to verbs, so you don't have to bother with the 4-letter
220type code. So, you can say
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000221<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000222 diskcopy.create(..., filesystem="Mac OS Standard")
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000223</code>
224as it is called in Script Editor, instead of the cryptic lowlevel
225<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000226 diskcopy.create(..., filesystem="Fhfs")
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000227</code></p>
Jack Jansenf10786b1997-08-19 14:00:56 +0000228
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000229<p>
230Finally, we get the &#8220;table of contents&#8221; of the module, listing all
231classes and such
Jack Jansen9051ad12002-01-25 15:28:39 +0000232by code, which is used by <code>gensuitemodule</code> itself: if you use this
233suite as a base package in a later run this is how it knows what is defined in this
234suite, and what the Python names are.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000235</p>
Jack Jansena6308131996-03-18 13:38:52 +0000236
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000237<h3>Notes</h3>
Jack Jansena6308131996-03-18 13:38:52 +0000238
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000239<ul>
240
241 <li>The <code>aetools</code> module contains some other nifty
242AppleEvent tools as well. Have a look at it sometime, there is (of
243course) no documentation yet.
244</li>
245
246 <li>There are also some older object specifiers for standard objects in aetools.
247You use these in the form <code>aetools.Word(10,
248aetools.Document(1))</code>, where the corresponding AppleScript
249terminology would be <code>word 10 of the first
250document</code>. Examine
251<code>aetools</code> and <code>aetools.TalkTo</code>
252along with
253the comments at the end of your suite module if you need to create
254more than the standard object specifiers.
255</li>
256
257</ul>
Jack Jansena6308131996-03-18 13:38:52 +0000258
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000259
260
261
262<h2>Using a Python suite module</h2>
263
264<p>
265Now that we have created the suite module, we can use it in a Python script.
Jack Jansenc15ab032000-08-20 21:57:38 +0000266In older MacPython distributions this used to be a rather
267complicated affair, but with the package scheme and with the application signature
268known by the package it is very simple: you import the package and instantiate
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000269the class, e.g.
270<code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000271 talker = Disk_Copy.Disk_Copy(start=1)
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000272</code>
273You will usually specify the <code>start=1</code>: it will run the application if it is
274not already running.
275You may want to omit it if you want to talk to the application
276only if it is already running, or if the application is something like the Finder.
Jack Jansen9051ad12002-01-25 15:28:39 +0000277Another way to ensure that the application is running is to call <code>talker._start()</code>.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000278</p>
Jack Jansena6308131996-03-18 13:38:52 +0000279
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000280<p>
281Looking at the sourcefile <a
282href="applescript/makedisk.py">makedisk.py</a>, we see that it starts
283with some imports. Naturally, one of these is the Python interface to Disk
284Copy.</p>
Jack Jansenbdf03a01996-09-20 15:22:47 +0000285
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000286<p>
287The main program itself is a wonder of simplicity: we create the
288object (<code>talker</code>) that talks to Disk Copy,
289create a disk, and mount it. The bulk of
290the work is done by <code>talker</code> and the Python interface package we
291just created.</p>
Jack Jansena6308131996-03-18 13:38:52 +0000292
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000293<p>
294The exception handling does warrant a few comments, though. Since
295AppleScript is basically a connectionless RPC protocol,
296nothing happens
297when we create the <code>talker</code> object. Hence, if the destination application
298is not running, we will not notice until we send our first
299command (avoid this as described above). There is another thing to note about errors returned by
300AppleScript calls: <code>MacOS.Error</code> is raised for
301all of the errors that are known to be <code>OSErr</code>-type errors,
302while
303server generated errors raise <code>aetools.Error</code>. </p>
Jack Jansena6308131996-03-18 13:38:52 +0000304
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000305<h2>Scripting Additions</h2>
Jack Jansena742d111996-12-23 17:28:53 +0000306
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000307<p>
Jack Jansena742d111996-12-23 17:28:53 +0000308If you want to use any of the scripting additions (or OSAXen, in
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000309everyday speech) from a Python program, you can use the same method
310as for applications, i.e. run <code>gensuitemodule</code> on the
311OSAX (commonly found in <code>System Folder:Scripting Additions</code>
Jack Jansenc15ab032000-08-20 21:57:38 +0000312or something similar). There is one minor gotcha: the application
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000313signature to use is <code>MACS</code>. You will need to edit the main class
314in the <code>__init__.py</code> file of the created package and change the value
Jack Jansen9051ad12002-01-25 15:28:39 +0000315of <code>_signature</code> to <code>MACS</code>, or use a subclass to the
316same effect.
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000317</p>
Jack Jansena742d111996-12-23 17:28:53 +0000318
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000319<p>
320There are two minor points to watch out for when using <code>gensuitemodule</code>
321on OSAXen: they appear all to define the class <code>System_Object_Suite</code>,
Jack Jansena742d111996-12-23 17:28:53 +0000322and a lot of them have the command set in multiple dialects. You have to
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000323watch out for name conflicts and make sure you select a reasonable dialect
324(some of the non-English dialects cause <code>gensuitemodule</code> to generate incorrect
325Python code). </p>
Jack Jansena742d111996-12-23 17:28:53 +0000326
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000327Despite these difficulties, OSAXen offer a lot of possibilities. Take a
328look at some of the OSAXen in the Scripting Additions folder, or
329<A HREF="http://www.osaxen.com/index.php">download</A> some from the net.
Jack Jansenc15ab032000-08-20 21:57:38 +0000330
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000331<h2>Further Reading</h2>
332
333<p>
334If you want to look at more involved examples of applescripting, look at the standard
Jack Jansenc15ab032000-08-20 21:57:38 +0000335modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000336is more involved) <code>fullbuild</code> from the <code>Mac:scripts</code> folder.
337</p>
338
339<h2><a name="alternatives">Alternatives</a></h2>
340
341<h3><a name="osx">Mac OS X</a></h3>
342
343<p>
344Under Mac OS X, the above still works, but with some new difficulties.
Jack Jansen9051ad12002-01-25 15:28:39 +0000345The application package structure can hide the &#8216;AETE&#8217; or
346&#8216;AEUT&#8217; resource from <code>gensuitemodule</code>, so that,
347for example, it cannot generate an OSA interface to iTunes. Script
348Editor gets at the dictionary of such programs using a &#8216;Get
349AETE&#8217; AppleEvent, if someone wants to donate code to use the same
350method for gensuitemodule: by all means!
Jack Jansenc7a7d2d2002-01-25 15:06:19 +0000351</p>
352
353<p>
354One alternative is available through the Unix command line version of python.
355Apple has provided the <code>osacompile</code> and <code>osascript</code> tools,
356which can be used to compile and execute scripts written in OSA languages. See the
357man pages for more details.
358</p>
359
360
361</body>
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000362</html>