""" gensuitemodule - Generate an AE suite module from an aete/aeut resource Based on aete.py """ import MacOS import os import string import sys import types import StringIO import macfs from Res import * def main(): fss, ok = macfs.PromptGetFile('Select file with aeut/aete resource:') if not ok: sys.exit(0) process(fss.as_pathname()) def process(fullname): """Process all resources in a single file""" cur = CurResFile() print fullname rf = OpenRFPerm(fullname, 0, 1) try: UseResFile(rf) resources = [] for i in range(Count1Resources('aete')): res = Get1IndResource('aete', 1+i) resources.append(res) for i in range(Count1Resources('aeut')): res = Get1IndResource('aeut', 1+i) resources.append(res) print "\nLISTING aete+aeut RESOURCES IN", `fullname` for res in resources: print "decoding", res.GetResInfo(), "..." data = res.data aete = decode(data) compileaete(aete, fullname) finally: if rf <> cur: CloseResFile(rf) UseResFile(cur) def decode(data): """Decode a resource into a python data structure""" f = StringIO.StringIO(data) aete = generic(getaete, f) aete = simplify(aete) processed = f.tell() unprocessed = len(f.read()) total = f.tell() if unprocessed: sys.stderr.write("%d processed + %d unprocessed = %d total\n" % (processed, unprocessed, total)) return aete def simplify(item): """Recursively replace singleton tuples by their constituent item""" if type(item) is types.ListType: return map(simplify, item) elif type(item) == types.TupleType and len(item) == 2: return simplify(item[1]) else: return item # Here follows the aete resource decoder. # It is presented bottom-up instead of top-down because there are direct # references to the lower-level part-decoders from the high-level part-decoders. def getbyte(f, *args): c = f.read(1) if not c: raise EOFError, 'in getbyte' + str(args) return ord(c) def getword(f, *args): getalign(f) s = f.read(2) if len(s) < 2: raise EOFError, 'in getword' + str(args) return (ord(s[0])<<8) | ord(s[1]) def getlong(f, *args): getalign(f) s = f.read(4) if len(s) < 4: raise EOFError, 'in getlong' + str(args) return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3]) def getostype(f, *args): getalign(f) s = f.read(4) if len(s) < 4: raise EOFError, 'in getostype' + str(args) return s def getpstr(f, *args): c = f.read(1) if len(c) < 1: raise EOFError, 'in getpstr[1]' + str(args) nbytes = ord(c) if nbytes == 0: return '' s = f.read(nbytes) if len(s) < nbytes: raise EOFError, 'in getpstr[2]' + str(args) return s def getalign(f): if f.tell() & 1: c = f.read(1) ##if c <> '\0': ## print 'align:', `c` def getlist(f, description, getitem): count = getword(f) list = [] for i in range(count): list.append(generic(getitem, f)) getalign(f) return list def alt_generic(what, f, *args): print "generic", `what`, args res = vageneric(what, f, args) print '->', `res` return res def generic(what, f, *args): if type(what) == types.FunctionType: return apply(what, (f,) + args) if type(what) == types.ListType: record = [] for thing in what: item = apply(generic, thing[:1] + (f,) + thing[1:]) record.append((thing[1], item)) return record return "BAD GENERIC ARGS: %s" % `what` getdata = [ (getostype, "type"), (getpstr, "description"), (getword, "flags") ] getargument = [ (getpstr, "name"), (getostype, "keyword"), (getdata, "what") ] getevent = [ (getpstr, "name"), (getpstr, "description"), (getostype, "suite code"), (getostype, "event code"), (getdata, "returns"), (getdata, "accepts"), (getlist, "optional arguments", getargument) ] getproperty = [ (getpstr, "name"), (getostype, "code"), (getdata, "what") ] getelement = [ (getostype, "type"), (getlist, "keyform", getostype) ] getclass = [ (getpstr, "name"), (getostype, "class code"), (getpstr, "description"), (getlist, "properties", getproperty), (getlist, "elements", getelement) ] getcomparison = [ (getpstr, "operator name"), (getostype, "operator ID"), (getpstr, "operator comment"), ] getenumerator = [ (getpstr, "enumerator name"), (getostype, "enumerator ID"), (getpstr, "enumerator comment") ] getenumeration = [ (getostype, "enumeration ID"), (getlist, "enumerator", getenumerator) ] getsuite = [ (getpstr, "suite name"), (getpstr, "suite description"), (getostype, "suite ID"), (getword, "suite level"), (getword, "suite version"), (getlist, "events", getevent), (getlist, "classes", getclass), (getlist, "comparisons", getcomparison), (getlist, "enumerations", getenumeration) ] getaete = [ (getword, "major/minor version in BCD"), (getword, "language code"), (getword, "script code"), (getlist, "suites", getsuite) ] def compileaete(aete, fname): """Generate code for a full aete resource. fname passed for doc purposes""" [version, language, script, suites] = aete major, minor = divmod(version, 256) for suite in suites: compilesuite(suite, major, minor, language, script, fname) def compilesuite(suite, major, minor, language, script, fname): """Generate code for a single suite""" [name, desc, code, level, version, events, classes, comps, enums] = suite modname = identify(name) fss, ok = macfs.StandardPutFile('Python output file', modname+'.py') if not ok: return fp = open(fss.as_pathname(), 'w') fss.SetCreatorType('Pyth', 'TEXT') fp.write('"""Suite %s: %s\n' % (name, desc)) fp.write("Level %d, version %d\n\n" % (level, version)) fp.write("Generated from %s\n"%fname) fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \ (major, minor, language, script)) fp.write('"""\n\n') fp.write('import aetools\n') fp.write('import MacOS\n\n') fp.write("_code = %s\n\n"% `code`) enum_names = [] for enum in enums: name = compileenumeration(fp, enum) enum_names.append(enum) compileclassheader(fp, modname) if events: for event in events: compileevent(fp, event) else: fp.write("\tpass\n\n") for cls in classes: compileclass(fp, cls) for comp in comps: compilecomparison(fp, comp) def compileclassheader(fp, name): """Generate class boilerplate""" fp.write("class %s:\n\n"%name) def compileevent(fp, event): """Generate code for a single event""" [name, desc, code, subcode, returns, accepts, arguments] = event funcname = identify(name) # # generate name->keyword map # if arguments: fp.write("\t_argmap_%s = {\n"%funcname) for a in arguments: fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`)) fp.write("\t}\n\n") # # Generate function header # has_arg = (not is_null(accepts)) opt_arg = (has_arg and is_optional(accepts)) fp.write("\tdef %s(self, "%funcname) if has_arg: if not opt_arg: fp.write("_object, ") # Include direct object, if it has one else: fp.write("_object=None, ") # Also include if it is optional else: fp.write("_no_object=None, ") # For argument checking fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args # # Generate doc string (important, since it may be the only # available documentation, due to our name-remaping) # fp.write('\t\t"""%s: %s\n'%(name, desc)) if has_arg: fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts)) elif opt_arg: fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts)) for arg in arguments: fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]), getdatadoc(arg[2]))) fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n") if not is_null(returns): fp.write("\t\tReturns: %s\n"%getdatadoc(returns)) fp.write('\t\t"""\n') # # Fiddle the args so everything ends up in 'arguments' dictionary # fp.write("\t\t_code = %s\n"% `code`) fp.write("\t\t_subcode = %s\n\n"% `subcode`) # # Do keyword name substitution # if arguments: fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname) else: fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n") # # Stuff required arg (if there is one) into arguments # if has_arg: fp.write("\t\t_arguments['----'] = _object\n") elif opt_arg: fp.write("\t\tif _object:\n") fp.write("\t\t\t_arguments['----'] = _object\n") else: fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n") fp.write("\n") # # Do enum-name substitution # for a in arguments: if is_enum(a[2]): kname = a[1] ename = a[2][0] if ename <> '****': fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" % (`kname`, ename)) fp.write("\n") # # Do the transaction # fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n") fp.write("\t\t\t\t_arguments, _attributes)\n") # # Error handling # fp.write("\t\tif _arguments.has_key('errn'):\n") fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n") fp.write("\t\t# XXXX Optionally decode result\n") # # Decode result # fp.write("\t\tif _arguments.has_key('----'):\n") if is_enum(returns): fp.write("\t\t\t# XXXX Should do enum remapping here...\n") fp.write("\t\t\treturn _arguments['----']\n") fp.write("\n") # print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`) # print "# returns", compiledata(returns) # print "# accepts", compiledata(accepts) # for arg in arguments: # compileargument(arg) def compileargument(arg): [name, keyword, what] = arg print "# %s (%s)" % (name, `keyword`), compiledata(what) def compileclass(fp, cls): [name, code, desc, properties, elements] = cls fp.write("\n# Class %s (%s) -- %s\n" % (`name`, `code`, `desc`)) for prop in properties: compileproperty(fp, prop) for elem in elements: compileelement(fp, elem) def compileproperty(fp, prop): [name, code, what] = prop fp.write("# property %s (%s) %s\n" % (`name`, `code`, compiledata(what))) def compileelement(fp, elem): [code, keyform] = elem fp.write("# element %s as %s\n" % (`code`, keyform)) def compilecomparison(fp, comp): [name, code, comment] = comp fp.write("# comparison %s (%s) -- %s\n" % (`name`, `code`, comment)) def compileenumeration(fp, enum): [code, items] = enum fp.write("_Enum_%s = {\n" % identify(code)) for item in items: compileenumerator(fp, item) fp.write("}\n\n") return code def compileenumerator(fp, item): [name, code, desc] = item fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc)) def compiledata(data): [type, description, flags] = data return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags)) def is_null(data): return data[0] == 'null' def is_optional(data): return (data[2] & 0x8000) def is_enum(data): return (data[2] & 0x2000) def getdatadoc(data): [type, descr, flags] = data if descr: return descr if type == '****': return 'anything' if type == 'obj ': return 'an AE object reference' return "undocumented, typecode %s"%`type` dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"} def compiledataflags(flags): bits = [] for i in range(16): if flags & (1<