blob: 8a26a9bae2b27b093fc6a1d5f340a3fc7993a51c [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).
97 <p>
Jack Jansena6308131996-03-18 13:38:52 +000098
Jack Jansenc15ab032000-08-20 21:57:38 +000099The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
100module is a nice example of a suite module.
101The <CODE>Special_Events_Events</CODE> class is the bulk of the code
Jack Jansena6308131996-03-18 13:38:52 +0000102generated. For each verb it contains a method. Each method knows what
Jack Jansenc15ab032000-08-20 21:57:38 +0000103arguments the verb expects, and it makes handy use of keyword
104arguments to present a palatable
Jack Jansena6308131996-03-18 13:38:52 +0000105interface to the python programmer. You will see that each method
106calls some routines from <CODE>aetools</CODE>, an auxiliary module
Jack Jansen08365421996-04-19 15:56:08 +0000107living in <CODE>Lib:toolbox</CODE> which contains some other nifty
Jack Jansena6308131996-03-18 13:38:52 +0000108AppleEvent tools as well. Have a look at it sometime, there is (of
109course) no documentation yet. <p>
110
111The other thing you notice is that each method calls
Jack Jansenc15ab032000-08-20 21:57:38 +0000112<CODE>self.send</CODE>, this comes from the <code>aetools.TalkTo</code> baseclass. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000113
Jack Jansenf10786b1997-08-19 14:00:56 +0000114After the big class we get a number of little class declarations. These
115declarations are for the (appleevent) classes and properties in the suite.
116They allow you to create object IDs, which can then be passed to the verbs.
117For instance, to get the name of the sender of the first message in mailbox
118inbox you would use <code>mailbox("inbox").message(1).sender</code>. It is
119also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
Jack Jansenc15ab032000-08-20 21:57:38 +0000120which is sometimes needed because these classes don't always inherit correctly
Jack Jansenf10786b1997-08-19 14:00:56 +0000121from baseclasses, so you may have to use a class or property from another suite. <p>
122
123<blockquote>
124There are also some older object specifiers for standard objects in aetools.
125You use these in the form <CODE>aetools.Word(10,
Jack Jansena6308131996-03-18 13:38:52 +0000126aetools.Document(1))</CODE> where the corresponding AppleScript
127terminology would be <CODE>word 10 of the first
128document</CODE>. Examine the two modules mentioned above along with
129the comments at the end of your suite module if you need to create
Jack Jansenf10786b1997-08-19 14:00:56 +0000130more than the standard object specifiers.
131</blockquote>
132
133Next we get the enumeration dictionaries, which allow you to pass
134english names as arguments to verbs, so you don't have to bother with the 4-letter
135type code. So, you can say
136<CODE><PRE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000137 diskcopy.create(..., filesystem="Mac OS Standard")
Jack Jansenf10786b1997-08-19 14:00:56 +0000138</PRE></CODE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000139as it is called in Script Editor, in stead of the cryptic lowlevel
Jack Jansenf10786b1997-08-19 14:00:56 +0000140<CODE><PRE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000141 diskcopy.create(..., filesystem="Fhfs")
Jack Jansenf10786b1997-08-19 14:00:56 +0000142</PRE></CODE><p>
143
144Finally, we get the "table of contents" of the module, listing all classes and such
145by code, which is used by gensuitemodule. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000146
147<H2>Using a Python suite module</H2>
148
Jack Jansenc15ab032000-08-20 21:57:38 +0000149Now that we have created the suite module we can use it in a Python script.
Jack Jansena6308131996-03-18 13:38:52 +0000150
Jack Jansenc15ab032000-08-20 21:57:38 +0000151In older MacPython distributions this used to be a rather
152complicated affair, but with the package scheme and with the application signature
153known by the package it is very simple: you import the package and instantiate
154the class, as
Jack Jansena6308131996-03-18 13:38:52 +0000155<CODE><PRE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000156 talker = Disk_Copy.Disk_Copy(start=1)
Jack Jansena6308131996-03-18 13:38:52 +0000157</PRE></CODE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000158You will usually specify the start=1: it will run the application if it is
159not already running. You may want to omit it if you want to talk to the application
160only if it is already running, or if the application is something like the Finder. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000161
162Looking at the sourcefile <A
Jack Jansenc15ab032000-08-20 21:57:38 +0000163HREF="applescript/makedisk.py">makedisk.py</A> we see that it starts
164with some imports.
Jack Jansenbdf03a01996-09-20 15:22:47 +0000165
Jack Jansena6308131996-03-18 13:38:52 +0000166The main program itself is a wonder of simplicity. We create the
Jack Jansenc15ab032000-08-20 21:57:38 +0000167object that talks to Disk Copy, creates a disk and mounts it. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000168
169The exception handling does need a few comments, though. Since
170AppleScript is basically a connectionless RPC protocol nothing happens
171when we create to talker object. Hence, if the destination application
172is not running we will not notice until we send our first
173command. There is another thing to note about errors returned by
Jack Jansen0fb1d821996-11-20 15:13:24 +0000174AppleScript calls: <CODE>MacOS.Error</CODE> is raised for
175all of the errors that are known to be <CODE>OSErr</CODE>-type errors,
176server generated errors raise <CODE>aetools.Error</CODE>. <p>
Jack Jansena6308131996-03-18 13:38:52 +0000177
Jack Jansena742d111996-12-23 17:28:53 +0000178<H2>Scripting Additions</H2>
179
180If you want to use any of the scripting additions (or OSAXen, in
181everyday speech) from a Python program you can use the same method
182as for applications, i.e. run <CODE>gensuitemodule</CODE> on the
183OSAX (commonly found in <CODE>System Folder:Extensions:Scripting Additions</CODE>
Jack Jansenc15ab032000-08-20 21:57:38 +0000184or something similar). There is one minor gotcha: the application
Jack Jansena742d111996-12-23 17:28:53 +0000185signature to use is <CODE>'MACS'</CODE>. <P>
186
187There are two minor points to watch out for when using gensuitemodule
188on OSAXen: they appear all to define the class <CODE>System_Object_Suite</CODE>,
189and a lot of them have the command set in multiple dialects. You have to
190watch out for name conflicts, so, and make sure you select a reasonable dialect
191(some of the non-english dialects cause gensuitemodule to generate incorrect
192Python code). <P>
193
Jack Jansenc15ab032000-08-20 21:57:38 +0000194<H2>Further Reading</H2>
195
196If you want to look at more involved examples of applescripting look at the standard
197modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
198is more involved) <code>fullbuild</code> from the Mac:scripts folder.