[Pythonmac-SIG] [appscript] requesting feedback

has hengist.podd at virgin.net
Mon Oct 17 15:34:22 CEST 2005


Hi all,

Following a bit of a hiatus, I'm now working on the next release of appscript. Below is a rather rough first draft of a new chapter for the appscript documentation that'll provide a basic whistle-stop tour of the concepts and technologies behind application scripting. I'd appreciate any comments or criticisms folks might have: does it make any sense, does it cover everything readers need to know, what needs improved/rephrased/explained better/completely rewritten, does it totally suck, etc? 

Many thanks,

has

----------------------------------------------------------------------
===== About Application Scripting =====

This chapter introduces the central concepts behind application scripting.

----- What are Apple events? -----

Apple events are a high-level message-based form of Inter-Process Communication, used to communicate between local or remote application processes (and in some cases within the same process). 

An Apple event contains typed data describing how the event should be handled ('attributes') such as its name (specified by two OSTypes [1]) and whether or not a reply is required, and data to be passed as arguments to the corresponding event handler ('parameters'). Datatypes include common scalar types (including boolean, integer, float, string, date and file reference), ordered lists, records (key-value lists where each key is an OSType) and object specifiers (used to construct first-class queries, commonly referred to as 'application references', that identify objects within an application).

[1] OSType: a 32-bit code, often represented as a 4-character string. Commonly used by Carbon APIs such as the Apple Event Manager. Mnemonic values are preferred, e.g. 'docu' = 'document'.


----- What is a scriptable application? -----

A scriptable (or 'AppleScriptable') application is an application that provides an Apple event interface intended for third-party (e.g. end-user) use. The application implements one or more event handlers that respond to corresponding events, and may also support the Apple Event Object Model. While this interface may be considered an API, the emphasis is on providing a high-level user interface that is peer to other users interfaces the application may have (GUI, CLI, web, etc.) and accessible to end-users as much as developers.


----- What is the Apple Event Object Model? -----

The Apple Event Object Model (AEOM) is an idealised, user-oriented representation of the application's internal data model, allowing clients to identify and manipulate that structures via Apple events. Incoming Apple events are unpacked, and any object specifiers are evaluated against the application's AEOM to identify the user-level object(s) upon which that AE handler should act. 

The AEOM presents user-level objects which may map directly to equivalent implementation objects (e.g. document, window mapping directly to NSDocument, NSWindow) or serve as abstract proxies to the actual implementation structures (e.g. character, word, paragraph mapping indirectly to a character buffer), depending on which arrangement is most appropriate/convenient for the user.

In AEOM, commands operate upon objects, so unlike DOM a single command may invoke multiple method calls upon implementation objects in order to perform its task. Also, where multiple objects are specified, the command should perform the same action for each of them [assuming a well-implemented AEOM; in practice most AEOM implementations usually suffer some limitations in this regard].


----- How does the AEOM work? -----

The AEOM is a tree structure made up of objects. These objects may have attributes (descriptive values such as class, name, id, size, etc.), e.g.:

app('Finder').version

and may 'contain' other objects, e.g.:

app('Finder').Finder_windows

However, unlike other object models such as DOM, objects within the AEOM are associated with one another [largely] by relationships rather than simple physical containment. (Think of AEOM as combining aspects of DOM and relational database mechanics.) [TO DECIDE: how best to explain attributes such as app('Finder').Finder_preferences?]

While these relationships may often follow the containment structure of the underlying data structures, e.g.

app('TextEdit').documents

this is not always the case, e.g.

app('Finder').files
app('Finder').desktop_object.files
app('Finder').disks['MacHD']folders['Users'].folders['John Brown'].folders['Desktop'].files

would all identify the same objects (files on the user's desktop), though [at best] only one of these references represents their physical containment structure.

Similarly, some references may identify different object at different times according to changes in the application's underlying state, e.g.:

app('iTunes').current_track

while others may identify objects that do not literally exist as individual entities within the application's underlying data structures, but are calculated on-the-fly as proxies to the relevant portions of the actual data structures, e.g.:

app('TextEdit').documents[1].text.characters
app('TextEdit').documents[1].text.words
app('TextEdit').documents[1].text.paragraphs


Relationships may be one-to-one, e.g.:

app('Finder').home
app('iTunes').current_track

or one-to-many, e.g.:

app('Finder').folders
app('TextEdit').documents

Finally, one-to-many relationships may be selective in the objects they identify, e.g.:

app('Finder').items # identifies all objects that are a subclass of class 'item' (disks, folders, document files, alias files, etc.)
app('Finder').files # identifies all objects that are a subclass of class 'file' (document files, alias files, etc.)
app('Finder').document_files # identifies all objects of class 'document file' only


----- What is appscript? -----

Appscript is a high-level Python-to-Apple Event Manager bridge, intended for use by both developers and end-users. The appscript architecture consists of three layers:

- Carbon.AE -- low-level, largely procedural Python wrapper around the AEM API

- aem -- mid-level wrapper around Carbon.AE, providing an object-oriented API for building relational AEOM queries and dispatching events

- appscript -- high-level wrapper around aem, providing automatic translation between human-readable application terminology and corresponding OSType codes, and representing relational AEOM queries in an OO-like syntax for ease of use.

AEM is largely intended for use by higher-level libraries and developers, though may also be used by end-users in cases where an application lacks terminology, or bugs within its terminology prevent its use by appscript. Appscript is intended for use by both developers and end-users, though developers may prefer aem for certain tasks as appscript doesn't expose every aspect of the aem API (send flags) and imposes additional overheads and dependencies on client code.

e.g. To set the size of the first character of every non-empty paragraph in every document of TextEdit to 24 pt:

- using appscript:

app('TextEdit').documents.text.paragraphs.filter(its != '\n').characters[1].size.set(24)

- using aem:

Application('/Applications/TextEdit.app').event('coregetd', {
	'----': app.elements('docu').property('ctxt').elements('cpar').byfilter(its.ne('\n'))
		.elements('cha ').byindex(1).property('ptsz'),
	'data': 24
	}).send()

(Carbon.AE equivalent not shown due to sheer length and ugliness.)


More information about the Pythonmac-SIG mailing list