2 AECaptureParser makes a brave attempt to convert the text output
3 of the very handy Lasso Capture AE control panel
4 into close-enough executable python code.
6 In a roundabout way AECaptureParser offers the way to write lines of AppleScript
7 and convert them to python code. Once Pythonised, the code can be made prettier,
8 and it can run without Capture or Script Editor being open.
10 You need Lasso Capture AE from Blueworld:
11 ftp://ftp.blueworld.com/Lasso251/LassoCaptureAE.hqx
13 Lasso Capture AE prints structured ascii representations in a small window.
14 As these transcripts can be very complex, cut and paste to AECaptureParser, it parses and writes
15 python code that will, when executed, cause the same events to happen.
16 It's been tested with some household variety events, I'm sure there will be tons that
19 All objects are converted to standard aetypes.ObjectSpecifier instances.
22 1. Start the Capture window
23 2. Cause the desired appleevent to happen
24 - by writing a line of applescript in Script Editor and running it (!)
25 - by recording some action in Script Editor and running it
26 3. Find the events in Capture:
27 - make sure you get the appropriate events, cull if necessary
28 - sometimes Capture barfs, just quit and start Capture again, run events again
29 - AECaptureParser can process multiple events - it will just make more code.
30 4. Copy and paste in this script and execute
31 5. It will print python code that, when executed recreates the events.
34 For instance the following line of AppleScript in Script Editor
35 tell application "Finder"
36 return application processes
38 will result in the following transcript:
39 [event: target="Finder", class=core, id=getd]
40 '----':obj {form:indx, want:type(pcap), seld:abso(«616C6C20»), from:'null'()}
42 Feed a string with this (and perhaps more) events to AECaptureParser
45 * what is '&subj' - it is sent in an activate event: &subj:'null'()
46 The activate event works when this is left out. A possibility?
47 * needs to deal with embedded aliasses
65 class eventtalker(aetools
.TalkTo
):
69 '''Helper function to get the list of current processes and their creators
70 This code was mostly written by AECaptureParser! It ain't pretty, but that's not python's fault!'''
71 talker
= eventtalker('MACS')
78 # first get the list of process names
79 _arguments
['----'] = aetypes
.ObjectSpecifier(want
=aetypes
.Type('pcap'),
80 form
="indx", seld
=aetypes
.Unknown('abso', "all "), fr
=None)
81 _reply
, _arguments
, _attributes
= talker
.send('core', 'getd', _arguments
, _attributes
)
82 if _arguments
.has_key('errn'):
83 raise aetools
.Error
, aetools
.decodeerror(_arguments
)
84 if _arguments
.has_key('----'):
85 p
= _arguments
['----']
87 names
.append(proc
.seld
)
88 # then get the list of process creators
91 AEobject_00
= aetypes
.ObjectSpecifier(want
=aetypes
.Type('pcap'), form
="indx", seld
=aetypes
.Unknown('abso', "all "), fr
=None)
92 AEobject_01
= aetypes
.ObjectSpecifier(want
=aetypes
.Type('prop'), form
="prop", seld
=aetypes
.Type('fcrt'), fr
=AEobject_00
)
93 _arguments
['----'] = AEobject_01
94 _reply
, _arguments
, _attributes
= talker
.send('core', 'getd', _arguments
, _attributes
)
95 if _arguments
.has_key('errn'):
96 raise aetools
.Error
, aetools
.decodeerror(_arguments
)
97 if _arguments
.has_key('----'):
98 p
= _arguments
['----']
100 creators
.append(proc
.type)
101 # then put the lists together
102 for i
in range(len(names
)):
103 results
.append((names
[i
], creators
[i
]))
107 class AECaptureParser
:
108 '''convert a captured appleevent-description into executable python code'''
109 def __init__(self
, aetext
):
115 self
.currentevent
= {'variables':{}, 'arguments':{}, 'objects':{}}
119 self
.lines
= string
.split(self
.aetext
, '\n')
121 if l
[:7] == '[event:':
123 elif l
[:7] == '[/event':
124 if len(self
.currentevent
)<>0:
125 self
.events
.append(self
.currentevent
)
126 self
.currentevent
= {'variables':{}, 'arguments':{}, 'objects':{}}
131 def line(self
, value
):
132 '''interpret literals, variables, lists etc.'''
133 # stuff in [ ], l ists
134 varstart
= string
.find(value
, '[')
135 varstop
= string
.find(value
, ']')
136 if varstart
<> -1 and varstop
<> -1 and varstop
>varstart
:
137 variable
= value
[varstart
:varstop
+1]
138 name
= 'aevar_'+string
.zfill(self
.varindex
, 2)
139 self
.currentevent
['variables'][name
] = variable
140 value
= value
[:varstart
]+name
+value
[varstop
+1:]
141 self
.varindex
= self
.varindex
+ 1
143 # these are 'ordinal' descriptors of 4 letter codes, so translate
144 varstart
= string
.find(value
, '«')
145 varstop
= string
.find(value
, '»')
146 if varstart
<> -1 and varstop
<> -1 and varstop
>varstart
:
147 variable
= value
[varstart
+1:varstop
]
149 for i
in range(0, len(variable
), 2):
150 c
= eval('0x'+variable
[i
: i
+2])
153 name
= 'aevar_'+string
.zfill(self
.varindex
, 2)
154 self
.currentevent
['variables'][name
] = '"' + t
+ '"'
155 value
= value
[:varstart
]+name
+value
[varstop
+1:]
156 self
.varindex
= self
.varindex
+ 1
157 pos
= string
.find(value
, ':')
161 value
, ok
= self
.parseobject(value
)
162 self
.currentevent
['arguments'].update(self
.splitparts(value
, ':'))
164 # remove the &subj argument?
165 if self
.currentevent
['arguments'].has_key('&subj'):
166 del self
.currentevent
['arguments']['&subj']
168 # check for arguments len(a) < 4, and pad with spaces
169 for k
in self
.currentevent
['arguments'].keys():
171 newk
= k
+ (4-len(k
))*' '
172 self
.currentevent
['arguments'][newk
] = self
.currentevent
['arguments'][k
]
173 del self
.currentevent
['arguments'][k
]
175 def parseobject(self
, obj
):
176 a
, b
= self
.findtag(obj
)
178 if a
<>None and b
<>None:
180 name
= 'AEobject_'+string
.zfill(self
.objectindex
, 2)
181 self
.currentevent
['objects'][name
] = self
.splitparts(stuff
, ':')
182 obj
= obj
[:a
-5] + name
+ obj
[b
+1:]
183 self
.objectindex
= self
.objectindex
+1
186 def nextopen(self
, pos
, text
):
187 return string
.find(text
, opentag
, pos
)
189 def nextclosed(self
, pos
, text
):
190 return string
.find(text
, closetag
, pos
)
192 def nexttag(self
, pos
, text
):
193 start
= self
.nextopen(pos
, text
)
194 stop
= self
.nextclosed(pos
, text
)
199 if start
< stop
and start
<>-1:
204 def findtag(self
, text
):
208 kind
, p
= self
.nexttag(p
+1, text
)
209 if last
[0]==1 and kind
==0:
210 return last
[1]+len(opentag
), p
211 if (kind
, p
) == (-1, -1):
216 def splitparts(self
, txt
, splitter
):
218 parts
= string
.split(txt
, ', ')
220 pos
= string
.find(p
, splitter
)
221 key
= string
.strip(p
[:pos
])
222 value
= string
.strip(p
[pos
+1:])
223 res
[key
] = self
.map(value
)
226 def eventheader(self
, hdr
):
227 self
.currentevent
['event'] = self
.splitparts(hdr
[7:-1], '=')
229 def printobject(self
, d
):
230 '''print one object as python code'''
234 t
.append("aetypes.ObjectSpecifier(")
235 if obj
.has_key('want'):
236 t
.append('want=' + self
.map(obj
['want']))
239 if obj
.has_key('form'):
240 t
.append('form=' + addquotes(self
.map(obj
['form'])))
243 if obj
.has_key('seld'):
244 t
.append('seld=' + self
.map(obj
['seld']))
247 if obj
.has_key('from'):
248 t
.append('fr=' + self
.map(obj
['from']))
250 if len(obj
.keys()) > 0:
253 return string
.join(t
, '')
256 '''map some Capture syntax to python
257 matchstring : [(old, new), ... ]
260 'type(': [('type(', "aetypes.Type('"), (')', "')")],
261 "'null'()": [("'null'()", "None")],
262 'abso(': [('abso(', "aetypes.Unknown('abso', ")],
265 '[': [('[', '('), (', ', ',')],
272 if string
.find(t
, k
) <> -1:
273 for old
, new
in m
[k
]:
274 p
= string
.split(t
, old
)
275 t
= string
.join(p
, new
)
278 def printevent(self
, i
):
279 '''print the entire captured sequence as python'''
282 code
.append('\n# start event ' + `i`
+ ', talking to ' + evt
['event']['target'])
283 # get the signature for the target application
284 code
.append('talker = eventtalker("'+self
.gettarget(evt
['event']['target'])+'")')
285 code
.append("_arguments = {}")
286 code
.append("_attributes = {}")
287 # write the variables
288 for key
, value
in evt
['variables'].items():
289 value
= evt
['variables'][key
]
290 code
.append(key
+ ' = ' + value
)
291 # write the object in the right order
292 objkeys
= evt
['objects'].keys()
295 value
= evt
['objects'][key
]
296 code
.append(key
+ ' = ' + self
.printobject(value
))
297 # then write the arguments
298 for key
, value
in evt
['arguments'].items():
299 code
.append("_arguments[" + addquotes(key
) + "] = " + value
)
300 code
.append('_reply, _arguments, _attributes = talker.send("'+
301 evt
['event']['class']+'", "'+evt
['event']['id']+'", _arguments, _attributes)')
302 code
.append("if _arguments.has_key('errn'):")
303 code
.append('\traise aetools.Error, aetools.decodeerror(_arguments)')
304 code
.append("if _arguments.has_key('----'):")
305 code
.append("\tprint _arguments['----']")
306 code
.append('# end event ' + `i`
)
307 return string
.join(code
, '\n')
309 def gettarget(self
, target
):
310 '''get the signature for the target application'''
311 target
= target
[1:-1]
312 if target
== 'Finder':
315 for name
, creator
in apps
:
323 code
.append("# code generated by AECaptureParser v " + __version__
)
324 code
.append("# imports, definitions for all events")
325 code
.append("import aetools")
326 code
.append("import aetypes")
327 code
.append("class eventtalker(aetools.TalkTo):")
328 code
.append("\tpass")
329 code
.append("# the events")
331 for i
in range(len(self
.events
)):
332 code
.append(self
.printevent(i
))
333 code
.append("# end code")
334 return string
.join(code
, '\n')
338 if not txt
[0] in quotes
and not txt
[-1] in quotes
:
347 # ------------------------------------------
349 # ------------------------------------------
351 # for instance, this event was captured from the Script Editor asking the Finder for a list of active processes.
353 eventreceptacle
= """
355 [event: target="Finder", class=core, id=setd]
356 '----':obj {form:prop, want:type(prop), seld:type(posn), from:obj {form:name, want:type(cfol), seld:³MoPar:Data:DevDev:Python:Python 1.5.2c1:Extensions², from:'null'()}}, data:[100, 10]
361 aet
= AECaptureParser(eventreceptacle
)