Improved some error messages for command line processing.
[python/dscho.git] / Mac / Demo / applescript.html
blobf527b5225dea4d1836e96939bb8433a34d12a652
1 <HTML><HEAD><TITLE>Using Open Scripting Extension from Python</TITLE></HEAD>
2 <BODY>
3 <H1>Using Open Scripting Extension from Python</H1>
4 <HR>
6 OSA support in Python is still far from complete, and what
7 support there is is likely to change in the forseeable future. Still,
8 there is already enough in place to allow you to do some nifty things
9 to other programs from your python program. <P>
11 <CITE>
12 Actually, when we say "AppleScript" in this document we actually mean
13 "the Open Scripting Architecture", there is nothing
14 AppleScript-specific in the Python implementation. <p>
15 </CITE>
17 In this example, we will look at a scriptable application, extract its
18 "AppleScript Dictionary" and generate a Python interface module from
19 that and use that module to control the application. Because we want
20 to concentrate on the OSA details we don't bother with a real
21 user-interface for our application. <p>
23 The application we are going to script is Eudora Light, a free mail
24 program from <A HREF="http://www.qualcomm.com">QualComm</A>. This is a
25 very versatile mail-reader, and QualComm has an accompanying
26 commercial version once your needs outgrow Eudora Light. Our program
27 will tell Eudora to send queued mail, retrieve mail or quit. <p>
29 <H2>Creating the Python interface module</H2>
31 There is a tool in the standard distribution that looks through a file
32 for an 'AETE' or 'AEUT' resource, the internal representation of the
33 AppleScript dictionary. This tool is called
34 <CODE>gensuitemodule.py</CODE>, and lives in <CODE>Mac:scripts</CODE>.
35 When we start it, it asks us for an input file and we point it to the
36 Eudora Light executable. It starts parsing the AETE resource, and for
37 each AppleEvent suite it finds it prompts us for the filename of the
38 resulting python module. Remember to change folders for the first
39 module, you don't want to clutter up the Eudora folder with your python
40 interfaces. If you want to skip a suite you press cancel and the process
41 continues with the next suite. In the case of Eudora, you do
42 <EM>not</EM> want to generate the Required and Standard suites, because
43 they are identical to the standard ones which are pregenerated (and
44 empty in the eudora binary). AppleScript understands that an empty suite
45 means "incorporate the whole standard suite by this name",
46 gensuitemodule does not currently understand this. Creating the empty
47 <CODE>Required_Suite.py</CODE> would hide the correct module of that
48 name from our application. <p>
50 Gensuitemodule may ask you questions like "Where is enum 'xyz ' declared?".
51 For the first time, cancel out of this dialog after taking down the
52 enum (or class or prop) name. After you've created all the suites look
53 for these codes, in the suites generated here and in the standard suites.
54 If you've found them all run gensuitemodule again and point it to the right
55 file for each declaration. Gensuitemodule will generate the imports to make the
56 reference work. <p>
58 <BLOCKQUOTE>
59 Time for a sidebar. If you want to re-create
60 <CODE>Required_Suite.py</CODE> or one of the other standard modules
61 you should look in <CODE>System Folder:Extensions:Scripting
62 Additions:Dialects:English Dialect</CODE>, that is where the core
63 AppleEvent dictionaries live. Also, if you are looking for the
64 <CODE>Finder_Suite</CODE> interface: don't look in the finder (it has
65 an old System 7.0 scripting suite), look at the extension <CODE>Finder
66 Scripting Extension</CODE>. <p>
67 </BLOCKQUOTE>
69 Let's glance at the <A
70 HREF="scripting/Eudora_Suite.py">Eudora_Suite.py</A> just created. You
71 may want to open Script Editor alongside, and have a look at how it
72 interprets the dictionary. EudoraSuite.py starts with some
73 boilerplate, then a big class definition with methods for each
74 AppleScript Verb, then some small class definitions and then some dictionary
75 initializations. <p>
77 The <CODE>Eudora_Suite</CODE> class is the bulk of the code
78 generated. For each verb it contains a method. Each method knows what
79 arguments the verb expects, and it makes handy use of the keyword
80 argument scheme introduced in Python 1.3 to present a palatable
81 interface to the python programmer. You will see that each method
82 calls some routines from <CODE>aetools</CODE>, an auxiliary module
83 living in <CODE>Lib:toolbox</CODE> which contains some other nifty
84 AppleEvent tools as well. Have a look at it sometime, there is (of
85 course) no documentation yet. <p>
87 The other thing you notice is that each method calls
88 <CODE>self.send</CODE>, but no such method is defined. You will have
89 to provide it by subclassing or multiple inheritance, as we shall see
90 later. <p>
92 After the big class we get a number of little class declarations. These
93 declarations are for the (appleevent) classes and properties in the suite.
94 They allow you to create object IDs, which can then be passed to the verbs.
95 For instance, to get the name of the sender of the first message in mailbox
96 inbox you would use <code>mailbox("inbox").message(1).sender</code>. It is
97 also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
98 which is sometimes needed because these classes don't inherit correctly
99 from baseclasses, so you may have to use a class or property from another suite. <p>
101 <blockquote>
102 There are also some older object specifiers for standard objects in aetools.
103 You use these in the form <CODE>aetools.Word(10,
104 aetools.Document(1))</CODE> where the corresponding AppleScript
105 terminology would be <CODE>word 10 of the first
106 document</CODE>. Examine the two modules mentioned above along with
107 the comments at the end of your suite module if you need to create
108 more than the standard object specifiers.
109 </blockquote>
111 Next we get the enumeration dictionaries, which allow you to pass
112 english names as arguments to verbs, so you don't have to bother with the 4-letter
113 type code. So, you can say
114 <CODE><PRE>
115 eudora.notice(occurrence="mail_arrives")
116 </PRE></CODE>
117 instead of the rather more cryptic
118 <CODE><PRE>
119 eudora.notice(occurrence="wArv")
120 </PRE></CODE><p>
122 Finally, we get the "table of contents" of the module, listing all classes and such
123 by code, which is used by gensuitemodule. <p>
125 <H2>Using a Python suite module</H2>
127 Now that we have created the suite module we can use it in an
128 application. We do this by creating a class that inherits
129 <CODE>Eudora_Suite</CODE> and the <CODE>TalkTo</CODE> class from
130 <CODE>aetools</CODE>. The <CODE>TalkTo</CODE> class is basically a
131 container for the <CODE>send</CODE> method used by the methods from
132 the suite classes. <p>
134 Actually, our class will also inherit <CODE>Required_Suite</CODE>,
135 because we also need functionality from that suite: the quit
136 command. Gensuitemodule could have created this completely derived
137 class for us, since it has access to all information needed to build
138 the class but unfortunately it does not do so at the moment. All in
139 all, the heart of our program looks like this:
140 <CODE><PRE>
141 import Eudora_Suite, Required_Suite, aetools
143 class Eudora(Eudora_Suite.Eudora_Suite, Required_Suite.Required_Suite, \
144 aetools.TalkTo):
145 pass
146 </PRE></CODE>
148 Yes, our class body is <CODE>pass</CODE>, all functionality is already
149 provided by the base classes, the only thing we have to do is glue it
150 together in the right way. <p>
152 Looking at the sourcefile <A
153 HREF="scripting/testeudora.py">testeudora.py</A> we see that it starts
154 with some imports. Then we get the class definition
155 for our main object and a constant giving the signature of Eudora. <p>
157 This, again, needs a little explanation. There are various ways to
158 describe to AppleScript which program we want to talk to, but the
159 easiest one to use (from Python, at least) is creator
160 signature. Application name would be much nicer, but Python currently
161 does not have a module that interfaces to the Finder database (which
162 would allow us to map names to signatures). The other alternative,
163 <CODE>ChooseApplication</CODE> from the program-to-program toolbox, is
164 also not available from Python at the moment. <p>
166 If you specify the application by creator you can specify an optional
167 <CODE>start</CODE> parameter, which will cause the application to be
168 started if it is not running. <P>
170 The main program itself is a wonder of simplicity. We create the
171 object that talks to Eudora (passing the signature as argument), ask
172 the user what she wants and call the appropriate method of the talker
173 object. The use of keyword arguments with the same names as used by
174 AppleScript make passing the parameters a breeze. <p>
176 The exception handling does need a few comments, though. Since
177 AppleScript is basically a connectionless RPC protocol nothing happens
178 when we create to talker object. Hence, if the destination application
179 is not running we will not notice until we send our first
180 command. There is another thing to note about errors returned by
181 AppleScript calls: <CODE>MacOS.Error</CODE> is raised for
182 all of the errors that are known to be <CODE>OSErr</CODE>-type errors,
183 server generated errors raise <CODE>aetools.Error</CODE>. <p>
185 <H2>Scripting Additions</H2>
187 If you want to use any of the scripting additions (or OSAXen, in
188 everyday speech) from a Python program you can use the same method
189 as for applications, i.e. run <CODE>gensuitemodule</CODE> on the
190 OSAX (commonly found in <CODE>System Folder:Extensions:Scripting Additions</CODE>
191 or something similar), define a class which inherits the generated
192 class and <CODE>aetools.TalkTo</CODE> and instantiate it. The application
193 signature to use is <CODE>'MACS'</CODE>. <P>
195 There are two minor points to watch out for when using gensuitemodule
196 on OSAXen: they appear all to define the class <CODE>System_Object_Suite</CODE>,
197 and a lot of them have the command set in multiple dialects. You have to
198 watch out for name conflicts, so, and make sure you select a reasonable dialect
199 (some of the non-english dialects cause gensuitemodule to generate incorrect
200 Python code). <P>
202 That concludes our simple example. Again, let me emphasize that
203 scripting support in Python is not very complete at the moment, and
204 the details of how to use AppleEvents will definitely change in the
205 near future. This will not only fix all the ideosyncracies noted in
206 this document but also break existing programs, since the current
207 suite organization will have to change to fix some of the problems.
208 Still, if you want to experiment with AppleEvents right now: go ahead!