blob: 183d66599a2fbd0a7dee19134807fc33c1a1ead8 [file] [log] [blame]
Jack Jansena6308131996-03-18 13:38:52 +00001<HTML><HEAD><TITLE>Using Open Scripting Extension from Python</TITLE></HEAD>
2<BODY>
3<H1>Using Open Scripting Extension from Python</H1>
4<HR>
5
Jack Jansenc15ab032000-08-20 21:57:38 +00006OSA support in Python is still not 100% complete, but
Jack Jansena6308131996-03-18 13:38:52 +00007there is already enough in place to allow you to do some nifty things
8to other programs from your python program. <P>
9
10<CITE>
11Actually, when we say "AppleScript" in this document we actually mean
12"the Open Scripting Architecture", there is nothing
13AppleScript-specific in the Python implementation. <p>
14</CITE>
15
16In this example, we will look at a scriptable application, extract its
17"AppleScript Dictionary" and generate a Python interface module from
18that and use that module to control the application. Because we want
19to concentrate on the OSA details we don't bother with a real
20user-interface for our application. <p>
21
Jack Jansenc15ab032000-08-20 21:57:38 +000022The application we are going to script is Disk Copy, Apple's standard
23utility for making copies of floppies, creating files that are mountable
24as disk images, etc. <p>
25
26<H2>Python OSA architecture</H2>
27
28Open Scripting suites and inheritance can be modelled rather nicely with
29with Python packages, so for each application we want to script we generate
30a package. Each suite defined in the application becomes a module in the
31package, and the package main module imports everything from all the
32submodules and glues all the classes (Python terminology, OSA terminology is
33events, AppleScript terminology is verbs) together. <p>
34
35A suite in an OSA application can extend the functionality of a standard
36suite, and this is implemented in Python by importing everything from the
37module that implements the standard suite and overriding anything that has
38been extended. The standard suites live in the StdSuite package. <p>
39
40This all sounds complicated, and you can do strange and wondrous things
41with it once you fully understand it, but the good news is that simple
42scripting is actually pretty simple. <p>
Jack Jansena6308131996-03-18 13:38:52 +000043
44<H2>Creating the Python interface module</H2>
45
46There is a tool in the standard distribution that looks through a file
47for an 'AETE' or 'AEUT' resource, the internal representation of the
48AppleScript dictionary. This tool is called
Jack Jansenf10786b1997-08-19 14:00:56 +000049<CODE>gensuitemodule.py</CODE>, and lives in <CODE>Mac:scripts</CODE>.
50When we start it, it asks us for an input file and we point it to the
Jack Jansenc15ab032000-08-20 21:57:38 +000051Disk Copy executable. <p>
52
53Next it wants a folder where it will store the package it is going to generate.
54Note that this is the package folder, not the parent folder, so we
55navigate to <code>Python:Mac:Demo:applescript</code>, create a folder
56<code>Disk_Copy</code> and select that. <p>
57
58Next it wants the folder from which it should import the standard suites. Here
59you always select <code>Python:Mac:Lib:lib-scriptpackages</code>. (There is
60one exception to this rule: when you are generating <code>StdSuites</code> itself
61you select <code>cancel</code>, for obvious reasons). <p>
62
63It starts parsing the AETE resource, and for
Jack Jansenf10786b1997-08-19 14:00:56 +000064each AppleEvent suite it finds it prompts us for the filename of the
65resulting python module. Remember to change folders for the first
66module, you don't want to clutter up the Eudora folder with your python
67interfaces. If you want to skip a suite you press cancel and the process
Jack Jansenc15ab032000-08-20 21:57:38 +000068continues with the next suite. <p>
Jack Jansena6308131996-03-18 13:38:52 +000069
Jack Jansenf10786b1997-08-19 14:00:56 +000070Gensuitemodule may ask you questions like "Where is enum 'xyz ' declared?".
Jack Jansenc15ab032000-08-20 21:57:38 +000071This is either due to a misunderstanding on my part or (rather too common)
72bugs in the AETE resources. Pressing <code>cancel</code> is usually the
73right option, it will cause the specific enum not to be treated as an enum
74but as a "normal" type. As things like fsspecs and TEXT strings clearly are
75not enumerators this is correct. If someone understands what is really going on
76here please let me know. <p>
Jack Jansenf10786b1997-08-19 14:00:56 +000077
Jack Jansen08365421996-04-19 15:56:08 +000078<BLOCKQUOTE>
Jack Jansenc15ab032000-08-20 21:57:38 +000079Time for a sidebar. If you want to re-create the StdSuite modules
80you should look in one of two places. On older systems you will find the
81AEUT resources in <CODE>System Folder:Extensions:Scripting
82Additions:Dialects:English Dialect</CODE>. On newer systems you will
83find them in <code>System Folder:Extensions:Applescript</code>. <p>
Jack Jansen08365421996-04-19 15:56:08 +000084</BLOCKQUOTE>
Jack Jansena6308131996-03-18 13:38:52 +000085
86Let's glance at the <A
Jack Jansenc15ab032000-08-20 21:57:38 +000087HREF="applescript/Disk_Copy">Disk_Copy</A> package just created. You
Jack Jansena6308131996-03-18 13:38:52 +000088may want to open Script Editor alongside, and have a look at how it
Jack Jansenc15ab032000-08-20 21:57:38 +000089interprets the dictionary. The main package module is in <code>__init__.py</code>
90and the only interesting bit is the <code>Disk_Copy</code> class, which
91includes the event handling classes from the individual suites. It also
92inherits <code>aetools.TalkTo</code>, which is a base class that handles all
93details on how to start the program and talk to it, and a class variable
94<code>_signature</code> which is the default application this class will talk
95to (you can override this in various when you instantiate your class, see
96<code>aetools.py</code> for details).
Jack Jansen64f9f712001-03-27 21:34:31 +000097<p>
98<blockquote>
99Let us do another sidebar. Since MacPython 2.0 this new structure, with packages
100per application and submodules per suite, is used. Older MacPythons had a
101single level of modules, with uncertain semantics. With the new structure
102it is possible for programs to override standard suites, as programs often do.
103It is a good idea to convert your own old programs to the new scheme, but if you
104really want the old standard suites are still available in
105<code>:Mac:Lib:lib-scripting</code>.
106</blockquote>
Jack Jansena6308131996-03-18 13:38:52 +0000107
Jack Jansenc15ab032000-08-20 21:57:38 +0000108The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
109module is a nice example of a suite module.
110The <CODE>Special_Events_Events</CODE> class is the bulk of the code
Jack Jansena6308131996-03-18 13:38:52 +0000111generated. For each verb it contains a method. Each method knows what
Jack Jansenc15ab032000-08-20 21:57:38 +0000112arguments the verb expects, and it makes handy use of keyword
113arguments to present a palatable
Jack Jansena6308131996-03-18 13:38:52 +0000114interface to the python programmer. You will see that each method
115calls some routines from <CODE>aetools</CODE>, an auxiliary module
Jack Jansen08365421996-04-19 15:56:08 +0000116living in <CODE>Lib:toolbox</CODE> which contains some other nifty
Jack Jansena6308131996-03-18 13:38:52 +0000117AppleEvent tools as well. Have a look at it sometime, there is (of
118course) no documentation yet. <p>
119
120The other thing you notice is that each method calls
Jack Jansenc15ab032000-08-20 21:57:38 +0000121<CODE>self.send</CODE>, this comes from the <code>aetools.TalkTo</code> baseclass. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000122
Jack Jansenf10786b1997-08-19 14:00:56 +0000123After the big class we get a number of little class declarations. These
124declarations are for the (appleevent) classes and properties in the suite.
125They allow you to create object IDs, which can then be passed to the verbs.
126For instance, to get the name of the sender of the first message in mailbox
127inbox you would use <code>mailbox("inbox").message(1).sender</code>. It is
128also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
Jack Jansenc15ab032000-08-20 21:57:38 +0000129which is sometimes needed because these classes don't always inherit correctly
Jack Jansenf10786b1997-08-19 14:00:56 +0000130from baseclasses, so you may have to use a class or property from another suite. <p>
131
132<blockquote>
133There are also some older object specifiers for standard objects in aetools.
134You use these in the form <CODE>aetools.Word(10,
Jack Jansena6308131996-03-18 13:38:52 +0000135aetools.Document(1))</CODE> where the corresponding AppleScript
136terminology would be <CODE>word 10 of the first
137document</CODE>. Examine the two modules mentioned above along with
138the comments at the end of your suite module if you need to create
Jack Jansenf10786b1997-08-19 14:00:56 +0000139more than the standard object specifiers.
140</blockquote>
141
142Next we get the enumeration dictionaries, which allow you to pass
143english names as arguments to verbs, so you don't have to bother with the 4-letter
144type code. So, you can say
145<CODE><PRE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000146 diskcopy.create(..., filesystem="Mac OS Standard")
Jack Jansenf10786b1997-08-19 14:00:56 +0000147</PRE></CODE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000148as it is called in Script Editor, in stead of the cryptic lowlevel
Jack Jansenf10786b1997-08-19 14:00:56 +0000149<CODE><PRE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000150 diskcopy.create(..., filesystem="Fhfs")
Jack Jansenf10786b1997-08-19 14:00:56 +0000151</PRE></CODE><p>
152
153Finally, we get the "table of contents" of the module, listing all classes and such
154by code, which is used by gensuitemodule. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000155
156<H2>Using a Python suite module</H2>
157
Jack Jansenc15ab032000-08-20 21:57:38 +0000158Now that we have created the suite module we can use it in a Python script.
Jack Jansena6308131996-03-18 13:38:52 +0000159
Jack Jansenc15ab032000-08-20 21:57:38 +0000160In older MacPython distributions this used to be a rather
161complicated affair, but with the package scheme and with the application signature
162known by the package it is very simple: you import the package and instantiate
163the class, as
Jack Jansena6308131996-03-18 13:38:52 +0000164<CODE><PRE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000165 talker = Disk_Copy.Disk_Copy(start=1)
Jack Jansena6308131996-03-18 13:38:52 +0000166</PRE></CODE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000167You will usually specify the start=1: it will run the application if it is
168not already running. You may want to omit it if you want to talk to the application
169only if it is already running, or if the application is something like the Finder. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000170
171Looking at the sourcefile <A
Jack Jansenc15ab032000-08-20 21:57:38 +0000172HREF="applescript/makedisk.py">makedisk.py</A> we see that it starts
173with some imports.
Jack Jansenbdf03a01996-09-20 15:22:47 +0000174
Jack Jansena6308131996-03-18 13:38:52 +0000175The main program itself is a wonder of simplicity. We create the
Jack Jansenc15ab032000-08-20 21:57:38 +0000176object that talks to Disk Copy, creates a disk and mounts it. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000177
178The exception handling does need a few comments, though. Since
179AppleScript is basically a connectionless RPC protocol nothing happens
180when we create to talker object. Hence, if the destination application
181is not running we will not notice until we send our first
182command. There is another thing to note about errors returned by
Jack Jansen0fb1d821996-11-20 15:13:24 +0000183AppleScript calls: <CODE>MacOS.Error</CODE> is raised for
184all of the errors that are known to be <CODE>OSErr</CODE>-type errors,
185server generated errors raise <CODE>aetools.Error</CODE>. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000186
Jack Jansena742d111996-12-23 17:28:53 +0000187<H2>Scripting Additions</H2>
188
189If you want to use any of the scripting additions (or OSAXen, in
190everyday speech) from a Python program you can use the same method
191as for applications, i.e. run <CODE>gensuitemodule</CODE> on the
192OSAX (commonly found in <CODE>System Folder:Extensions:Scripting Additions</CODE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000193or something similar). There is one minor gotcha: the application
Jack Jansena742d111996-12-23 17:28:53 +0000194signature to use is <CODE>'MACS'</CODE>. <P>
195
196There are two minor points to watch out for when using gensuitemodule
197on OSAXen: they appear all to define the class <CODE>System_Object_Suite</CODE>,
198and a lot of them have the command set in multiple dialects. You have to
199watch out for name conflicts, so, and make sure you select a reasonable dialect
200(some of the non-english dialects cause gensuitemodule to generate incorrect
201Python code). <P>
202
Jack Jansenc15ab032000-08-20 21:57:38 +0000203<H2>Further Reading</H2>
204
205If you want to look at more involved examples of applescripting look at the standard
206modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
207is more involved) <code>fullbuild</code> from the Mac:scripts folder.