Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6a2fdab

Browse files
committed
Erik van Blokland's CaptureAE.
1 parent 2ea47f9 commit 6a2fdab

2 files changed

Lines changed: 367 additions & 0 deletions

File tree

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
'''
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.
5+
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.
9+
10+
You need Lasso Capture AE from Blueworld:
11+
ftp://ftp.blueworld.com/Lasso251/LassoCaptureAE.hqx
12+
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
17+
don't work.
18+
19+
All objects are converted to standard aetypes.ObjectSpecifier instances.
20+
21+
How to use:
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.
32+
33+
Example:
34+
For instance the following line of AppleScript in Script Editor
35+
tell application "Finder"
36+
return application processes
37+
end tell
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'()}
41+
[/event]
42+
Feed a string with this (and perhaps more) events to AECaptureParser
43+
44+
Some mysteries:
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
48+
49+
50+
'''
51+
__version__ = '0.002'
52+
__author__ = 'evb'
53+
54+
55+
import string
56+
57+
opentag = '{'
58+
closetag = '}'
59+
60+
61+
62+
import aetools
63+
import aetypes
64+
65+
class eventtalker(aetools.TalkTo):
66+
pass
67+
68+
def processes():
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')
72+
_arguments = {}
73+
_attributes = {}
74+
p = []
75+
names = []
76+
creators = []
77+
results = []
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['----']
86+
for proc in p:
87+
names.append(proc.seld)
88+
# then get the list of process creators
89+
_arguments = {}
90+
_attributes = {}
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['----']
99+
for proc in p:
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]))
104+
return results
105+
106+
107+
class AECaptureParser:
108+
'''convert a captured appleevent-description into executable python code'''
109+
def __init__(self, aetext):
110+
self.aetext = aetext
111+
self.events = []
112+
self.arguments = {}
113+
self.objectindex = 0
114+
self.varindex = 0
115+
self.currentevent = {'variables':{}, 'arguments':{}, 'objects':{}}
116+
self.parse()
117+
118+
def parse(self):
119+
self.lines = string.split(self.aetext, '\n')
120+
for l in self.lines:
121+
if l[:7] == '[event:':
122+
self.eventheader(l)
123+
elif l[:7] == '[/event':
124+
if len(self.currentevent)<>0:
125+
self.events.append(self.currentevent)
126+
self.currentevent = {'variables':{}, 'arguments':{}, 'objects':{}}
127+
self.objectindex = 0
128+
else:
129+
self.line(l)
130+
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
142+
# stuff in « »
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]
148+
t = ''
149+
for i in range(0, len(variable), 2):
150+
c = eval('0x'+variable[i : i+2])
151+
t = t + chr(c)
152+
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, ':')
158+
if pos==-1:return
159+
ok = 1
160+
while ok <> None:
161+
value, ok = self.parseobject(value)
162+
self.currentevent['arguments'].update(self.splitparts(value, ':'))
163+
164+
# remove the &subj argument?
165+
if self.currentevent['arguments'].has_key('&subj'):
166+
del self.currentevent['arguments']['&subj']
167+
168+
# check for arguments len(a) < 4, and pad with spaces
169+
for k in self.currentevent['arguments'].keys():
170+
if len(k)<4:
171+
newk = k + (4-len(k))*' '
172+
self.currentevent['arguments'][newk] = self.currentevent['arguments'][k]
173+
del self.currentevent['arguments'][k]
174+
175+
def parseobject(self, obj):
176+
a, b = self.findtag(obj)
177+
stuff = None
178+
if a<>None and b<>None:
179+
stuff = obj[a:b]
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
184+
return obj, stuff
185+
186+
def nextopen(self, pos, text):
187+
return string.find(text, opentag, pos)
188+
189+
def nextclosed(self, pos, text):
190+
return string.find(text, closetag, pos)
191+
192+
def nexttag(self, pos, text):
193+
start = self.nextopen(pos, text)
194+
stop = self.nextclosed(pos, text)
195+
if start == -1:
196+
if stop == -1:
197+
return -1, -1
198+
return 0, stop
199+
if start < stop and start<>-1:
200+
return 1, start
201+
else:
202+
return 0, stop
203+
204+
def findtag(self, text):
205+
p = -1
206+
last = None,None
207+
while 1:
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):
212+
break
213+
last=kind, p
214+
return None, None
215+
216+
def splitparts(self, txt, splitter):
217+
res = {}
218+
parts = string.split(txt, ', ')
219+
for p in parts:
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)
224+
return res
225+
226+
def eventheader(self, hdr):
227+
self.currentevent['event'] = self.splitparts(hdr[7:-1], '=')
228+
229+
def printobject(self, d):
230+
'''print one object as python code'''
231+
t = []
232+
obj = {}
233+
obj.update(d)
234+
t.append("aetypes.ObjectSpecifier(")
235+
if obj.has_key('want'):
236+
t.append('want=' + self.map(obj['want']))
237+
del obj['want']
238+
t.append(', ')
239+
if obj.has_key('form'):
240+
t.append('form=' + addquotes(self.map(obj['form'])))
241+
del obj['form']
242+
t.append(', ')
243+
if obj.has_key('seld'):
244+
t.append('seld=' + self.map(obj['seld']))
245+
del obj['seld']
246+
t.append(', ')
247+
if obj.has_key('from'):
248+
t.append('fr=' + self.map(obj['from']))
249+
del obj['from']
250+
if len(obj.keys()) > 0:
251+
print '# ', `obj`
252+
t.append(")")
253+
return string.join(t, '')
254+
255+
def map(self, t):
256+
'''map some Capture syntax to python
257+
matchstring : [(old, new), ... ]
258+
'''
259+
m = {
260+
'type(': [('type(', "aetypes.Type('"), (')', "')")],
261+
"'null'()": [("'null'()", "None")],
262+
'abso(': [('abso(', "aetypes.Unknown('abso', ")],
263+
'³': [('³', '"')],
264+
'²': [('²', '"')],
265+
'[': [('[', '('), (', ', ',')],
266+
']': [(']', ')')],
267+
'«': [('«', "«")],
268+
'»': [('»', "»")],
269+
270+
}
271+
for k in m.keys():
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)
276+
return t
277+
278+
def printevent(self, i):
279+
'''print the entire captured sequence as python'''
280+
evt = self.events[i]
281+
code = []
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()
293+
objkeys.sort()
294+
for key in objkeys:
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')
308+
309+
def gettarget(self, target):
310+
'''get the signature for the target application'''
311+
target = target[1:-1]
312+
if target == 'Finder':
313+
return "MACS"
314+
apps = processes()
315+
for name, creator in apps:
316+
if name == target:
317+
return creator
318+
return '****'
319+
320+
def makecode(self):
321+
code = []
322+
code.append("\n\n")
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")
330+
# print 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')
335+
336+
def addquotes(txt):
337+
quotes = ['"', "'"]
338+
if not txt[0] in quotes and not txt[-1] in quotes:
339+
return '"'+txt+'"'
340+
return txt
341+
342+
343+
344+
345+
346+
347+
# ------------------------------------------
348+
# the factory
349+
# ------------------------------------------
350+
351+
# for instance, this event was captured from the Script Editor asking the Finder for a list of active processes.
352+
353+
eventreceptacle = """
354+
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]
357+
[/event]
358+
359+
"""
360+
361+
aet = AECaptureParser(eventreceptacle)
362+
print aet.makecode()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
AECaptureParser is a tool by Erik van Blokland, [email protected], which
2+
listens for AppleEvents and turns them into the Python code that will generate
3+
those events when executed.
4+
5+
Lots more information is in the docstring in the code.

0 commit comments

Comments
 (0)