symbian-qemu-0.9.1-12/python-2.6.1/Mac/Demo/applescript.html
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 <!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>
       
     6 
       
     7 <p><b>NOTE:</b> this document describes the OSA support that is shipped with
       
     8 the core python distribution. Most users are better of with the more 
       
     9 userfriendly <a href="http://freespace.virgin.net/hamish.sanderson/appscript.html">appscript library</a>.
       
    10 
       
    11 <p>OSA support in Python is still not 100% complete, but
       
    12 there is already enough in place to allow you to do some nifty things
       
    13 with other programs from your python program. </p> 
       
    14 
       
    15 
       
    16 <p>
       
    17 In this example, we will look at a scriptable application, extract its
       
    18 &#8220;AppleScript Dictionary,&#8221;  generate a Python interface package from
       
    19 the dictionary, and use that package to control the application. 
       
    20 The application we are going to script is Disk Copy, Apple's standard
       
    21 utility for making copies of floppies, creating files that are mountable
       
    22 as disk images, etc. 
       
    23 Because we want
       
    24 to concentrate on the OSA details, we won&#8217;t bother with a real
       
    25 user-interface for our application. </p>
       
    26 
       
    27 
       
    28 <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
       
    31 AppleScript-specific in the Python implementation. Most of this document 
       
    32 focuses on the classic Mac OS; <a href="#osx">Mac OS X</a> users have some 
       
    33 additional tools.</em>
       
    34 </p>
       
    35 
       
    36 <h2>Python OSA architecture</h2>
       
    37 
       
    38 <p>Open Scripting suites and inheritance can be modelled rather nicely 
       
    39 with Python packages, so we generate
       
    40 a package for each application we want to script. Each suite defined in 
       
    41 the application becomes a module in the
       
    42 package, and the package main module imports everything from all the
       
    43 submodules and glues together all the classes (in Python terminology&#8212; 
       
    44 events in OSA terminology or verbs in AppleScript terminology). </p>
       
    45 
       
    46 <p>
       
    47 A suite in an OSA application can extend the functionality of a standard
       
    48 suite. This is implemented in Python by importing everything from the
       
    49 module that implements the standard suites and overriding anything that has
       
    50 been extended. The standard suites live in the StdSuite package. </p>
       
    51 
       
    52 <p>
       
    53 This all sounds complicated, but the good news is that basic
       
    54 scripting is actually pretty simple. You can do strange and wondrous things
       
    55 with OSA scripting once you fully understand it. </p>
       
    56 
       
    57 <h2>Creating the Python interface package</h2>
       
    58 
       
    59 
       
    60 <p>There is a tool in the standard distribution that can automatically 
       
    61 generate the interface packages.  This tool is called
       
    62 <code>gensuitemodule.py</code>, and lives in <code>Mac:scripts</code>. 
       
    63 It looks through a file
       
    64 for an &#8216;AETE&#8217; or &#8216;AEUT&#8217; resource, 
       
    65 the internal representation of the
       
    66 AppleScript dictionary, and parses the resource to generate the suite 
       
    67 modules.
       
    68 When we start <code>gensuitemodule</code>, it asks us for an input file; 
       
    69 for our example,
       
    70 we point it to the Disk Copy executable. </p>
       
    71 
       
    72 <p>
       
    73 Next, <code>gensuitemodule</code> wants a folder where it will store the 
       
    74 package it is going to generate.
       
    75 Note that this is the package folder, not the parent folder, so we
       
    76 navigate to <code>Python:Mac:Demo:applescript</code>, create a folder
       
    77 <code>Disk_Copy</code>, and select that. </p>
       
    78 
       
    79 <p>
       
    80 We  next specify the folder from which <code>gensuitemodule</code>  
       
    81 should import the standard suites. Here,
       
    82 we always select <code>Python:Mac:Lib:lib-scriptpackages:StdSuites</code>. (There is
       
    83 one exception to this rule: when you are generating <code>StdSuites</code> itself
       
    84 you select <code>_builtinSuites</code>.)
       
    85 </p>
       
    86 
       
    87 <p>
       
    88 It starts parsing the AETE resource, and for
       
    89 each AppleEvent suite it finds, <code>gensuitemodule.py</code>
       
    90 prompts us for the filename of the
       
    91 resulting python module. Remember to change folders for the first
       
    92 module&#8212;you don't want to clutter up, say, the 
       
    93 Disk Copy folder
       
    94 with your python
       
    95 interfaces. If you want to skip a suite, press <code>cancel</code> and the process
       
    96 continues with the next suite. </p>
       
    97 
       
    98 <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>
       
   110 	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 	
       
   124 	<li>The interface package may occasionally need some editing by hand.  For example, 
       
   125 	<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>) 
       
   132 	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>
       
   135 	
       
   136 	
       
   137 	<li>If you want to re-create the StdSuite modules,
       
   138 you 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
       
   140 AEUT resources in <code>System Folder:Extensions:Scripting
       
   141 Additions:Dialects:English Dialect</code>. For newer versions, you will
       
   142 find them in <code>System Folder:Extensions:Applescript</code>.
       
   143 </li>
       
   144 
       
   145 	<li>Since MacPython 2.0, this new structure, with packages
       
   146 per application and submodules per suite, is used. Older MacPythons had a
       
   147 single level of modules, with uncertain semantics. With the new structure,
       
   148 it is possible for programs to override standard suites, as programs often do.
       
   149 
       
   150 </li>
       
   151 
       
   152 <li><code>Gensuitemodule.py</code> may ask you questions 
       
   153 like &#8220;Where is enum 'xyz ' declared?&#8221;.
       
   154 This is either due to a misunderstanding on my part or (rather too commonly)
       
   155 bugs in the AETE resources. Pressing <code>cancel</code> is usually the
       
   156 right choice: it will cause the specific enum not to be treated as an enum
       
   157 but as a &#8220;normal&#8221; type. As things like fsspecs and TEXT strings clearly are
       
   158 not enumerators, this is correct. If someone understands what is really going on
       
   159 here, please let me know.</li>
       
   160 
       
   161 </ul>
       
   162 
       
   163 
       
   164 
       
   165 <h2>The Python interface package contents</h2>
       
   166 
       
   167 <p>
       
   168 Let&#8217;s glance at the 
       
   169 <a href="applescript/Disk_Copy">Disk_Copy</a> package just created. You
       
   170 may want to open Script Editor alongside to see how it
       
   171 interprets the dictionary. 
       
   172 </p>
       
   173 
       
   174 
       
   175 <p>
       
   176 The main package module is in <code>__init__.py</code>.
       
   177 The only interesting bit is the <code>Disk_Copy</code> class, which
       
   178 includes the event handling classes from the individual suites. It also
       
   179 inherits <code>aetools.TalkTo</code>, which is a base class that handles all
       
   180 details 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
       
   182 to (you can override this in various ways when you instantiate your class, see
       
   183 <code>aetools.py</code> for details).
       
   184 </p>
       
   185 
       
   186 <p>
       
   187 The <a href="applescript/Disk_Copy/Special_Events.py">Special_Events</a>
       
   188 module is a nice example of a suite module.
       
   189 The <code>Special_Events_Events</code> class is the bulk of the code
       
   190 generated. For each verb, it contains a method. Each method knows what
       
   191 arguments the verb expects, and it makes  use of keyword
       
   192 arguments to present a palatable
       
   193 interface to the python programmer. 
       
   194 
       
   195 Notice that each method
       
   196 calls some routines from <code>aetools</code>, an auxiliary module
       
   197 living in <code>Mac:Lib</code>.
       
   198 The other thing to notice is that each method calls
       
   199 <code>self.send</code>.  This comes from the <code>aetools.TalkTo</code> 
       
   200 baseclass. </p>
       
   201 
       
   202 
       
   203 <p>
       
   204 After the big class, there are a number of little class declarations. These
       
   205 declarations are for the (AppleEvent) classes and properties in the suite.
       
   206 They allow you to create object IDs, which can then be passed to the verbs.
       
   207 For instance,
       
   208 when scripting the popular email program Eudora,
       
   209 you would use <code>mailbox("inbox").message(1).sender</code>
       
   210 to get the name of the sender of the first message in mailbox
       
   211 inbox. It is
       
   212 also possible to specify this as <code>sender(message(1, mailbox("inbox")))</code>,
       
   213 which is sometimes needed because these classes don&#8217;t always inherit correctly
       
   214 from baseclasses, so you may have to use a class or property from another 
       
   215 suite. </p>
       
   216 
       
   217 <p>
       
   218 Next we get the enumeration dictionaries, which allow you to pass
       
   219 english names as arguments to verbs, so you don't have to bother with the 4-letter
       
   220 type code. So, you can say
       
   221 <code>
       
   222 	diskcopy.create(..., filesystem="Mac OS Standard")
       
   223 </code>
       
   224 as it is called in Script Editor, instead of the cryptic lowlevel
       
   225 <code>
       
   226 	diskcopy.create(..., filesystem="Fhfs")
       
   227 </code></p>
       
   228 
       
   229 <p>
       
   230 Finally, we get the &#8220;table of contents&#8221; of the module, listing all 
       
   231 classes and such
       
   232 by code, which is used by <code>gensuitemodule</code> itself: if you use this
       
   233 suite as a base package in a later run this is how it knows what is defined in this
       
   234 suite, and what the Python names are.
       
   235 </p>
       
   236 
       
   237 <h3>Notes</h3>
       
   238 
       
   239 <ul>
       
   240 	
       
   241 	<li>The <code>aetools</code> module contains some other nifty
       
   242 AppleEvent tools as well. Have a look at it sometime, there is (of
       
   243 course) no documentation yet. 
       
   244 </li>
       
   245 	
       
   246 	<li>There are also some older object specifiers for standard objects in aetools.
       
   247 You use these in the form <code>aetools.Word(10,
       
   248 aetools.Document(1))</code>, where the corresponding AppleScript
       
   249 terminology would be <code>word 10 of the first
       
   250 document</code>. Examine 
       
   251 <code>aetools</code> and <code>aetools.TalkTo</code>
       
   252 along with
       
   253 the comments at the end of your suite module if you need to create
       
   254 more than the standard object specifiers.
       
   255 </li>
       
   256 	
       
   257 </ul>
       
   258 
       
   259 
       
   260 
       
   261 
       
   262 <h2>Using a Python suite module</h2>
       
   263 
       
   264 <p>
       
   265 Now that we have created the suite module, we can use it in a Python script.
       
   266 In older MacPython distributions this used to be a rather
       
   267 complicated affair, but with the package scheme and with the application signature
       
   268 known by the package it is very simple: you import the package and instantiate
       
   269 the class, e.g. 
       
   270 <code>
       
   271 	talker = Disk_Copy.Disk_Copy(start=1)
       
   272 </code>
       
   273 You will usually specify the <code>start=1</code>: it will run the application if it is
       
   274 not already running. 
       
   275 You may want to omit it if you want to talk to the application
       
   276 only if it is already running, or if the application is something like the Finder. 
       
   277 Another way to ensure that  the application is running is to call <code>talker._start()</code>.
       
   278 </p>
       
   279 
       
   280 <p>
       
   281 Looking at the sourcefile <a
       
   282 href="applescript/makedisk.py">makedisk.py</a>, we see that it starts
       
   283 with some imports.  Naturally, one of these is the Python interface to Disk 
       
   284 Copy.</p>
       
   285 
       
   286 <p>
       
   287 The main program itself is a wonder of simplicity: we create the
       
   288 object (<code>talker</code>) that talks to Disk Copy, 
       
   289 create a disk, and mount it. The bulk of 
       
   290 the work is done by <code>talker</code> and the Python interface package we 
       
   291 just created.</p>
       
   292 
       
   293 <p>
       
   294 The exception handling does warrant a few comments, though. Since
       
   295 AppleScript is basically a connectionless RPC protocol,
       
   296 nothing happens
       
   297 when we create the <code>talker</code> object. Hence, if the destination application
       
   298 is not running, we will not notice until we send our first
       
   299 command (avoid this as described above). There is another thing to note about errors returned by
       
   300 AppleScript calls: <code>MacOS.Error</code> is raised for
       
   301 all of the errors that are known to be <code>OSErr</code>-type errors, 
       
   302 while
       
   303 server generated errors raise <code>aetools.Error</code>. </p>
       
   304 
       
   305 <h2>Scripting Additions</h2>
       
   306 
       
   307 <p>
       
   308 If you want to use any of the scripting additions (or OSAXen, in
       
   309 everyday speech) from a Python program, you can use the same method
       
   310 as for applications, i.e. run <code>gensuitemodule</code> on the
       
   311 OSAX (commonly found in <code>System Folder:Scripting Additions</code>
       
   312 or something similar). There is one minor gotcha: the application
       
   313 signature to use is <code>MACS</code>. You will need to edit the main class
       
   314 in the <code>__init__.py</code> file of the created package and change the value 
       
   315 of <code>_signature</code> to <code>MACS</code>, or use a subclass to the
       
   316 same effect.
       
   317 </p>
       
   318 
       
   319 <p>
       
   320 There are two minor points to watch out for when using <code>gensuitemodule</code>
       
   321 on OSAXen: they appear all to define the class <code>System_Object_Suite</code>,
       
   322 and a lot of them have the command set in multiple dialects. You have to
       
   323 watch 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
       
   325 Python code). </p>
       
   326 
       
   327 Despite these difficulties, OSAXen offer a lot of possibilities.  Take a 
       
   328 look 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.
       
   330 
       
   331 <h2>Further Reading</h2>
       
   332 
       
   333 <p>
       
   334 If you want to look at more involved examples of applescripting, look at the standard
       
   335 modules <code>findertools</code> and <code>nsremote</code>, or (possibly better, as it
       
   336 is 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>
       
   344 Under Mac OS X, the above still works, but with some new difficulties.
       
   345 The application package structure can hide the &#8216;AETE&#8217; or
       
   346 &#8216;AEUT&#8217; resource from <code>gensuitemodule</code>, so that,
       
   347 for example, it cannot generate an OSA interface to iTunes. Script
       
   348 Editor gets at the dictionary of such programs using a &#8216;Get
       
   349 AETE&#8217; AppleEvent, if someone wants to donate code to use the same
       
   350 method for gensuitemodule: by all means!
       
   351 </p>
       
   352 
       
   353 <p>
       
   354 One alternative is available through the Unix command line version of python. 
       
   355 Apple has provided the <code>osacompile</code> and <code>osascript</code> tools, 
       
   356 which can be used to compile and execute scripts written in OSA languages. See the 
       
   357 man pages for more details.
       
   358 </p>
       
   359 
       
   360 
       
   361 </body>
       
   362 </html>