]> granicus.if.org Git - python/commitdiff
Erik van Blokland's CaptureAE.
authorJack Jansen <jack.jansen@cwi.nl>
Sat, 22 Apr 2000 22:56:53 +0000 (22:56 +0000)
committerJack Jansen <jack.jansen@cwi.nl>
Sat, 22 Apr 2000 22:56:53 +0000 (22:56 +0000)
Mac/Contrib/AECaptureParser/AECaptureParser.py [new file with mode: 0644]
Mac/Contrib/AECaptureParser/readme.txt [new file with mode: 0644]

diff --git a/Mac/Contrib/AECaptureParser/AECaptureParser.py b/Mac/Contrib/AECaptureParser/AECaptureParser.py
new file mode 100644 (file)
index 0000000..25a0237
--- /dev/null
@@ -0,0 +1,362 @@
+'''
+AECaptureParser makes a brave attempt to convert the text output
+of the very handy Lasso Capture AE control panel
+into close-enough executable python code.
+
+In a roundabout way AECaptureParser offers the way to write lines of AppleScript
+and convert them to python code. Once Pythonised, the code can be made prettier,
+and it can run without Capture or Script Editor being open.
+
+You need Lasso Capture AE from Blueworld:
+ftp://ftp.blueworld.com/Lasso251/LassoCaptureAE.hqx
+
+Lasso Capture AE prints structured ascii representations in a small window.
+As these transcripts can be very complex, cut and paste to AECaptureParser, it parses and writes
+python code that will, when executed, cause the same events to happen.
+It's been tested with some household variety events, I'm sure there will be tons that
+don't work.
+
+All objects are converted to standard aetypes.ObjectSpecifier instances.
+
+How to use:
+       1. Start the Capture window
+       2. Cause the desired appleevent to happen
+               - by writing a line of applescript in Script Editor and running it (!)
+               - by recording some action in Script Editor and running it
+       3. Find the events in Capture:
+               - make sure you get the appropriate events, cull if necessary
+               - sometimes Capture barfs, just quit and start Capture again, run events again
+               - AECaptureParser can process multiple events - it will just make more code.
+       4. Copy and paste in this script and execute
+       5. It will print python code that, when executed recreates the events.
+
+Example:
+       For instance the following line of AppleScript in Script Editor
+                       tell application "Finder"
+                               return application processes
+                       end tell
+       will result in the following transcript:
+                       [event: target="Finder", class=core, id=getd]
+                       '----':obj {form:indx, want:type(pcap), seld:abso(«616C6C20»), from:'null'()}
+                       [/event]
+       Feed a string with this (and perhaps more) events to AECaptureParser
+       
+Some mysteries:
+       *       what is '&subj' - it is sent in an activate event:      &subj:'null'()
+               The activate event works when this is left out. A possibility?
+       *       needs to deal with embedded aliasses
+
+
+'''
+__version__ = '0.002'
+__author__ = 'evb'
+
+
+import string 
+
+opentag = '{'
+closetag = '}'
+
+
+
+import aetools
+import aetypes
+
+class eventtalker(aetools.TalkTo):
+       pass
+
+def processes():
+       '''Helper function to get the list of current processes and their creators
+       This code was mostly written by AECaptureParser! It ain't pretty, but that's not python's fault!'''
+       talker = eventtalker('MACS')
+       _arguments = {}
+       _attributes = {}
+       p = []
+       names = []
+       creators = []
+       results = []
+       # first get the list of process names
+       _arguments['----'] = aetypes.ObjectSpecifier(want=aetypes.Type('pcap'),
+                       form="indx", seld=aetypes.Unknown('abso', "all "), fr=None)
+       _reply, _arguments, _attributes = talker.send('core', 'getd', _arguments, _attributes)
+       if _arguments.has_key('errn'):
+               raise aetools.Error, aetools.decodeerror(_arguments)
+       if _arguments.has_key('----'):
+               p = _arguments['----']
+       for proc in p:
+               names.append(proc.seld)
+       # then get the list of process creators
+       _arguments = {}
+       _attributes = {}
+       AEobject_00 = aetypes.ObjectSpecifier(want=aetypes.Type('pcap'), form="indx", seld=aetypes.Unknown('abso', "all "), fr=None)
+       AEobject_01 = aetypes.ObjectSpecifier(want=aetypes.Type('prop'), form="prop", seld=aetypes.Type('fcrt'), fr=AEobject_00)
+       _arguments['----'] = AEobject_01
+       _reply, _arguments, _attributes = talker.send('core', 'getd', _arguments, _attributes)
+       if _arguments.has_key('errn'):
+               raise aetools.Error, aetools.decodeerror(_arguments)
+       if _arguments.has_key('----'):
+               p = _arguments['----']
+       for proc in p:
+               creators.append(proc.type)
+       # then put the lists together
+       for i in range(len(names)):
+               results.append((names[i], creators[i]))
+       return results
+
+               
+class AECaptureParser:
+       '''convert a captured appleevent-description into executable python code'''
+       def __init__(self, aetext):
+               self.aetext = aetext
+               self.events = []
+               self.arguments = {}
+               self.objectindex = 0
+               self.varindex = 0
+               self.currentevent =  {'variables':{}, 'arguments':{}, 'objects':{}}
+               self.parse()
+       
+       def parse(self):
+               self.lines = string.split(self.aetext, '\n')
+               for l in self.lines:
+                       if l[:7] == '[event:':
+                               self.eventheader(l)
+                       elif l[:7] == '[/event':
+                               if len(self.currentevent)<>0:
+                                       self.events.append(self.currentevent)
+                                       self.currentevent = {'variables':{}, 'arguments':{}, 'objects':{}}
+                                       self.objectindex = 0
+                       else:
+                               self.line(l)
+       
+       def line(self, value):
+               '''interpret literals, variables, lists etc.'''
+               # stuff in [  ], l ists
+               varstart = string.find(value, '[')
+               varstop = string.find(value, ']')
+               if varstart <> -1 and varstop <> -1 and varstop>varstart:
+                       variable = value[varstart:varstop+1]
+                       name = 'aevar_'+string.zfill(self.varindex, 2)
+                       self.currentevent['variables'][name] = variable
+                       value = value[:varstart]+name+value[varstop+1:]
+                       self.varindex = self.varindex + 1
+               # stuff in «  »
+               # these are 'ordinal' descriptors of 4 letter codes, so translate
+               varstart = string.find(value, '«')
+               varstop = string.find(value, '»')
+               if varstart <> -1 and varstop <> -1 and varstop>varstart:
+                       variable = value[varstart+1:varstop]
+                       t = ''
+                       for i in range(0, len(variable), 2):
+                               c = eval('0x'+variable[i : i+2])
+                               t = t + chr(c)
+                       
+                       name = 'aevar_'+string.zfill(self.varindex, 2)
+                       self.currentevent['variables'][name] = '"' + t + '"'
+                       value = value[:varstart]+name+value[varstop+1:]
+                       self.varindex = self.varindex + 1
+               pos = string.find(value, ':')
+               if pos==-1:return
+               ok = 1
+               while ok <> None:
+                       value, ok = self.parseobject(value)
+               self.currentevent['arguments'].update(self.splitparts(value, ':'))
+               
+               # remove the &subj argument?
+               if self.currentevent['arguments'].has_key('&subj'):
+                       del self.currentevent['arguments']['&subj']
+                       
+               # check for arguments len(a) < 4, and pad with spaces
+               for k in self.currentevent['arguments'].keys():
+                       if len(k)<4:
+                               newk = k + (4-len(k))*' '
+                               self.currentevent['arguments'][newk] = self.currentevent['arguments'][k]
+                               del self.currentevent['arguments'][k]
+
+       def parseobject(self, obj):
+               a, b = self.findtag(obj)
+               stuff = None
+               if a<>None and b<>None:
+                       stuff = obj[a:b]
+                       name = 'AEobject_'+string.zfill(self.objectindex, 2)
+                       self.currentevent['objects'][name] = self.splitparts(stuff, ':')
+                       obj = obj[:a-5] + name + obj[b+1:]
+                       self.objectindex = self.objectindex +1
+               return obj, stuff
+               
+       def nextopen(self, pos, text):
+               return string.find(text, opentag, pos)
+               
+       def nextclosed(self, pos, text):
+               return string.find(text, closetag, pos)
+       
+       def nexttag(self, pos, text):
+               start = self.nextopen(pos, text)
+               stop = self.nextclosed(pos, text)
+               if start == -1:
+                       if stop == -1:
+                               return -1, -1
+                       return 0, stop
+               if start < stop and start<>-1:
+                       return 1, start
+               else:
+                       return 0, stop
+                                       
+       def findtag(self, text):
+               p = -1
+               last = None,None
+               while 1:
+                       kind, p = self.nexttag(p+1, text)
+                       if last[0]==1 and kind==0:
+                               return last[1]+len(opentag), p
+                       if (kind, p) == (-1, -1):
+                               break
+                       last=kind, p
+               return None, None
+       
+       def splitparts(self, txt, splitter):
+               res = {}
+               parts = string.split(txt, ', ')
+               for p in parts:
+                       pos = string.find(p, splitter)
+                       key = string.strip(p[:pos])
+                       value = string.strip(p[pos+1:])
+                       res[key] = self.map(value)
+               return res
+               
+       def eventheader(self, hdr):
+               self.currentevent['event'] = self.splitparts(hdr[7:-1], '=')
+       
+       def printobject(self, d):
+               '''print one object as python code'''
+               t = []
+               obj = {}
+               obj.update(d)
+               t.append("aetypes.ObjectSpecifier(")
+               if obj.has_key('want'):
+                       t.append('want=' + self.map(obj['want']))
+                       del obj['want']
+                       t.append(', ')
+               if obj.has_key('form'):
+                       t.append('form=' + addquotes(self.map(obj['form'])))
+                       del obj['form']
+                       t.append(', ')
+               if obj.has_key('seld'):
+                       t.append('seld=' + self.map(obj['seld']))
+                       del obj['seld']
+                       t.append(', ')
+               if obj.has_key('from'):
+                       t.append('fr=' + self.map(obj['from']))
+                       del obj['from']
+               if len(obj.keys()) > 0:
+                       print '# ', `obj`                       
+               t.append(")")
+               return string.join(t, '')
+       
+       def map(self, t):
+               '''map some Capture syntax to python
+               matchstring : [(old, new), ... ]
+               '''
+               m = {
+                               'type(': [('type(', "aetypes.Type('"), (')', "')")],
+                               "'null'()": [("'null'()", "None")],
+                               'abso(': [('abso(', "aetypes.Unknown('abso', ")],
+                               '³':    [('³', '"')],
+                               '²':    [('²', '"')],
+                               '[':    [('[', '('), (', ', ',')],
+                               ']':    [(']', ')')],
+                               '«':    [('«', "«")],
+                               '»':    [('»', "»")],
+                               
+                       }
+               for k in m.keys():
+                       if string.find(t, k) <> -1:
+                               for old, new in m[k]:
+                                       p = string.split(t, old)
+                                       t = string.join(p, new)
+               return t
+               
+       def printevent(self, i):
+               '''print the entire captured sequence as python'''
+               evt = self.events[i]
+               code = []
+               code.append('\n# start event ' + `i` + ', talking to ' + evt['event']['target'])
+               # get the signature for the target application
+               code.append('talker = eventtalker("'+self.gettarget(evt['event']['target'])+'")')
+               code.append("_arguments = {}")
+               code.append("_attributes = {}")
+               # write the variables
+               for key, value in evt['variables'].items():
+                       value = evt['variables'][key]
+                       code.append(key + ' = ' + value)
+               # write the object in the right order
+               objkeys = evt['objects'].keys()
+               objkeys.sort()
+               for key in objkeys:
+                       value = evt['objects'][key]
+                       code.append(key + ' = ' + self.printobject(value))
+               # then write the arguments
+               for key, value in evt['arguments'].items():
+                       code.append("_arguments[" + addquotes(key) + "] = " + value )
+               code.append('_reply, _arguments, _attributes = talker.send("'+
+                               evt['event']['class']+'", "'+evt['event']['id']+'", _arguments, _attributes)')
+               code.append("if _arguments.has_key('errn'):")
+               code.append('\traise aetools.Error, aetools.decodeerror(_arguments)')   
+               code.append("if _arguments.has_key('----'):")
+               code.append("\tprint _arguments['----']")
+               code.append('# end event ' + `i`)
+               return string.join(code, '\n')
+       
+       def gettarget(self, target):
+               '''get the signature for the target application'''
+               target = target[1:-1]
+               if target == 'Finder':
+                       return "MACS"
+               apps = processes()
+               for name, creator in apps:
+                       if name == target:
+                               return creator
+               return '****'
+                       
+       def makecode(self):
+               code = []
+               code.append("\n\n")
+               code.append("# code generated by AECaptureParser v " + __version__)
+               code.append("# imports, definitions for all events")
+               code.append("import aetools")
+               code.append("import aetypes")
+               code.append("class eventtalker(aetools.TalkTo):")
+               code.append("\tpass")
+               code.append("# the events")
+               # print the events
+               for i in range(len(self.events)):
+                       code.append(self.printevent(i))
+               code.append("# end code")
+               return string.join(code, '\n')
+
+def addquotes(txt):
+       quotes = ['"', "'"]
+       if not txt[0] in quotes and not txt[-1] in quotes:
+               return '"'+txt+'"'
+       return txt 
+       
+       
+
+
+
+
+# ------------------------------------------
+#      the factory
+# ------------------------------------------
+
+# for instance, this event was captured from the Script Editor asking the Finder for a list of active processes.
+
+eventreceptacle = """
+
+[event: target="Finder", class=core, id=setd]
+'----':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]
+[/event]
+
+"""
+
+aet = AECaptureParser(eventreceptacle)
+print aet.makecode()
diff --git a/Mac/Contrib/AECaptureParser/readme.txt b/Mac/Contrib/AECaptureParser/readme.txt
new file mode 100644 (file)
index 0000000..1506111
--- /dev/null
@@ -0,0 +1,5 @@
+AECaptureParser is a tool by Erik van Blokland, erik@letterror.com, which
+listens for AppleEvents and turns them into the Python code that will generate
+those events when executed.
+
+Lots more information is in the docstring in the code.