From 8b77767094ba4bf3fb497ce974261366470d6aa3 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Wed, 7 Aug 2002 14:49:00 +0000 Subject: Donovan Preston's patch #538395, with some mods by me. This patch makes inheritance for OSA classes work. The implementation is a bit convoluted, but I don't immedeately see a simpler way of doing it. I added calls to ascii() everywhere we output strings that may contain non-ascii characters (Python has gotten very picky since the encoding patch:-). I also removed Donovan's different way of opening resource files: I don't seem to need it. --- Mac/Lib/aepack.py | 53 +++++++++++++++---- Mac/Lib/aetools.py | 40 ++++++++++++--- Mac/Lib/aetypes.py | 24 ++++++++- Mac/scripts/gensuitemodule.py | 116 +++++++++++++++++++++++++++++++----------- 4 files changed, 183 insertions(+), 50 deletions(-) diff --git a/Mac/Lib/aepack.py b/Mac/Lib/aepack.py index f58dd33..dfb2953 100644 --- a/Mac/Lib/aepack.py +++ b/Mac/Lib/aepack.py @@ -24,6 +24,7 @@ import macfs import StringIO import aetypes from aetypes import mkenum, mktype +import os # These ones seem to be missing from AppleEvents # (they're in AERegistry.h) @@ -61,6 +62,15 @@ AEDescType = AE.AEDescType FSSType = macfs.FSSpecType AliasType = macfs.AliasType +def packkey(ae, key, value): + if hasattr(key, 'which'): + keystr = key.which + elif hasattr(key, 'want'): + keystr = key.want + else: + keystr = key + ae.AEPutParamDesc(keystr, pack(value)) + def pack(x, forcetype = None): """Pack a python object into an AE descriptor""" @@ -99,13 +109,18 @@ def pack(x, forcetype = None): if t == DictionaryType: record = AE.AECreateList('', 1) for key, value in x.items(): - record.AEPutParamDesc(key, pack(value)) + packkey(record, key, value) + #record.AEPutParamDesc(key, pack(value)) return record if t == InstanceType and hasattr(x, '__aepack__'): return x.__aepack__() + if hasattr(x, 'which'): + return AE.AECreateDesc('TEXT', x.which) + if hasattr(x, 'want'): + return AE.AECreateDesc('TEXT', x.want) return AE.AECreateDesc('TEXT', repr(x)) # Copout -def unpack(desc): +def unpack(desc, formodulename=""): """Unpack an AE descriptor to a python object""" t = desc.type @@ -117,17 +132,17 @@ def unpack(desc): l = [] for i in range(desc.AECountItems()): keyword, item = desc.AEGetNthDesc(i+1, '****') - l.append(unpack(item)) + l.append(unpack(item, formodulename)) return l if t == typeAERecord: d = {} for i in range(desc.AECountItems()): keyword, item = desc.AEGetNthDesc(i+1, '****') - d[keyword] = unpack(item) + d[keyword] = unpack(item, formodulename) return d if t == typeAEText: record = desc.AECoerceDesc('reco') - return mkaetext(unpack(record)) + return mkaetext(unpack(record, formodulename)) if t == typeAlias: return macfs.RawAlias(desc.data) # typeAppleEvent returned as unknown @@ -153,7 +168,7 @@ def unpack(desc): return macfs.RawFSSpec(desc.data) if t == typeInsertionLoc: record = desc.AECoerceDesc('reco') - return mkinsertionloc(unpack(record)) + return mkinsertionloc(unpack(record, formodulename)) # typeInteger equal to typeLongInteger if t == typeIntlText: script, language = struct.unpack('hh', desc.data[:4]) @@ -177,7 +192,11 @@ def unpack(desc): return v if t == typeObjectSpecifier: record = desc.AECoerceDesc('reco') - return mkobject(unpack(record)) + # If we have been told the name of the module we are unpacking aedescs for, + # we can attempt to create the right type of python object from that module. + if formodulename: + return mkobjectfrommodule(unpack(record, formodulename), formodulename) + return mkobject(unpack(record, formodulename)) # typePict returned as unknown # typePixelMap coerced to typeAERecord # typePixelMapMinus returned as unknown @@ -214,13 +233,13 @@ def unpack(desc): # if t == 'rang': record = desc.AECoerceDesc('reco') - return mkrange(unpack(record)) + return mkrange(unpack(record, formodulename)) if t == 'cmpd': record = desc.AECoerceDesc('reco') - return mkcomparison(unpack(record)) + return mkcomparison(unpack(record, formodulename)) if t == 'logi': record = desc.AECoerceDesc('reco') - return mklogical(unpack(record)) + return mklogical(unpack(record, formodulename)) return mkunknown(desc.type, desc.data) def coerce(data, egdata): @@ -311,6 +330,20 @@ def mkobject(dict): return aetypes.Property(seld.type, fr) return aetypes.ObjectSpecifier(want, form, seld, fr) +# Note by Jack: I'm not 100% sure of the following code. This was +# provided by Donovan Preston, but I wonder whether the assignment +# to __class__ is safe. Moreover, shouldn't there be a better +# initializer for the classes in the suites? +def mkobjectfrommodule(dict, modulename): + want = dict['want'].type + module = __import__(modulename) + codenamemapper = module._classdeclarations + classtype = codenamemapper.get(want, None) + newobj = mkobject(dict) + if classtype: + newobj.__class__ = classtype + return newobj + def _test(): """Test program. Pack and unpack various things""" objs = [ diff --git a/Mac/Lib/aetools.py b/Mac/Lib/aetools.py index ba42f03..5495dfa 100644 --- a/Mac/Lib/aetools.py +++ b/Mac/Lib/aetools.py @@ -28,7 +28,7 @@ import MacOS import sys from aetypes import * -from aepack import pack, unpack, coerce, AEDescType +from aepack import packkey, pack, unpack, coerce, AEDescType Error = 'aetools.Error' @@ -56,19 +56,19 @@ def missed(ae): return None return desc.data -def unpackevent(ae): +def unpackevent(ae, formodulename=""): parameters = {} try: dirobj = ae.AEGetParamDesc('----', '****') except AE.Error: pass else: - parameters['----'] = unpack(dirobj) + parameters['----'] = unpack(dirobj, formodulename) del dirobj while 1: key = missed(ae) if not key: break - parameters[key] = unpack(ae.AEGetParamDesc(key, '****')) + parameters[key] = unpack(ae.AEGetParamDesc(key, '****'), formodulename) attributes = {} for key in aekeywords: try: @@ -77,14 +77,14 @@ def unpackevent(ae): if msg[0] != -1701 and msg[0] != -1704: raise sys.exc_type, sys.exc_value continue - attributes[key] = unpack(desc) + attributes[key] = unpack(desc, formodulename) return parameters, attributes def packevent(ae, parameters = {}, attributes = {}): for key, value in parameters.items(): - ae.AEPutParamDesc(key, pack(value)) + packkey(ae, key, value) for key, value in attributes.items(): - ae.AEPutAttributeDesc(key, pack(value)) + packkey(ae, key, value) # # Support routine for automatically generated Suite interfaces @@ -130,6 +130,7 @@ def decodeerror(arguments): class TalkTo: """An AE connection to an application""" _signature = None # Can be overridden by subclasses + _moduleName = None # Can be overridden by subclasses def __init__(self, signature=None, start=0, timeout=0): """Create a communication channel with a particular application. @@ -183,7 +184,7 @@ class TalkTo: reply = event.AESend(self.send_flags, self.send_priority, self.send_timeout) - parameters, attributes = unpackevent(reply) + parameters, attributes = unpackevent(reply, self._moduleName) return reply, parameters, attributes def send(self, code, subcode, parameters = {}, attributes = {}): @@ -218,6 +219,29 @@ class TalkTo: if _arguments.has_key('----'): return _arguments['----'] + if as: + item.__class__ = as + return item + + def _set(self, _object, _arguments = {}, _attributes = {}): + """ _set: set data for an object + Required argument: the object + Keyword argument _parameters: Parameter dictionary for the set operation + Keyword argument _attributes: AppleEvent attribute dictionary + Returns: the data + """ + _code = 'core' + _subcode = 'setd' + + _arguments['----'] = _object + + _reply, _arguments, _attributes = self.send(_code, _subcode, + _arguments, _attributes) + if _arguments.has_key('errn'): + raise Error, decodeerror(_arguments) + + if _arguments.has_key('----'): + return _arguments['----'] # Tiny Finder class, for local use only diff --git a/Mac/Lib/aetypes.py b/Mac/Lib/aetypes.py index 8fe8cf9..d376e74 100644 --- a/Mac/Lib/aetypes.py +++ b/Mac/Lib/aetypes.py @@ -9,9 +9,9 @@ import string # convoluted, since there are cyclic dependencies between this file and # aetools_convert. # -def pack(*args): +def pack(*args, **kwargs): from aepack import pack - return apply(pack, args) + return apply(pack, args, kwargs) def IsSubclass(cls, base): """Test whether CLASS1 is the same as or a subclass of CLASS2""" @@ -69,6 +69,26 @@ def mkenum(enum): if IsEnum(enum): return enum return Enum(enum) +# Jack changed the way this is done +class InsertionLoc: + def __init__(self, of, pos): + self.of = of + self.pos = pos + + def __repr__(self): + return "InsertionLoc(%s, %s)" % (`self.of`, `self.pos`) + + def __aepack__(self): + rec = {'kobj': self.of, 'kpos': self.pos} + return pack(rec, forcetype='insl') + +# Convenience functions for dsp: +def beginning(of): + return InsertionLoc(of, Enum('bgng')) + +def end(of): + return InsertionLoc(of, Enum('end ')) + class Boolean: """An AE boolean value""" diff --git a/Mac/scripts/gensuitemodule.py b/Mac/scripts/gensuitemodule.py index 3e23661..6b17a79 100644 --- a/Mac/scripts/gensuitemodule.py +++ b/Mac/scripts/gensuitemodule.py @@ -15,6 +15,7 @@ import StringIO import macfs import keyword import macresource +from aetools import unpack from Carbon.Res import * @@ -274,7 +275,8 @@ def compileaete(aete, resinfo, fname): fss.SetCreatorType('Pyth', 'TEXT') fp.write('"""\n') fp.write("Package generated from %s\n"%fname) - fp.write("Resource %s resid %d %s\n"%(resinfo[1], resinfo[0], resinfo[2])) + if resinfo: + fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2]))) fp.write('"""\n') fp.write('import aetools\n') fp.write('Error = aetools.Error\n') @@ -282,20 +284,49 @@ def compileaete(aete, resinfo, fname): fp.write("import %s\n" % modname) fp.write("\n\n_code_to_module = {\n") for code, modname in suitelist: - fp.write("\t'%s' : %s,\n"%(code, modname)) + fp.write("\t'%s' : %s,\n"%(ascii(code), modname)) fp.write("}\n\n") fp.write("\n\n_code_to_fullname = {\n") for code, modname in suitelist: - fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(code, packagename, modname, modname)) + fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname)) fp.write("}\n\n") for code, modname in suitelist: fp.write("from %s import *\n"%modname) + + # Generate property dicts and element dicts for all types declared in this module + fp.write("def getbaseclasses(v):\n") + fp.write("\tif hasattr(v, '_superclassnames'):\n") + fp.write("\t\tv._propdict = {}\n") + fp.write("\t\tv._elemdict = {}\n") + fp.write("\t\tfor superclass in v._superclassnames:\n") + fp.write("\t\t\tgetbaseclasses(superclass)\n") + fp.write("\t\t\tv._propdict.update(getattr(eval(superclass), '_privpropdict'))\n") + fp.write("\t\t\tv._elemdict.update(getattr(eval(superclass), '_privelemdict'))\n") + fp.write("\t\tv._propdict.update(v._privpropdict)\n") + fp.write("\t\tv._elemdict.update(v._privelemdict)\n") + fp.write("import StdSuites\n") + if allprecompinfo: + fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n") + for codenamemapper in allprecompinfo: + for k, v in codenamemapper.getall('class'): + fp.write("getbaseclasses(%s)\n" % v) + + # Generate a code-to-name mapper for all of the types (classes) declared in this module + if allprecompinfo: + fp.write("\n#\n# Indices of types declared in this module\n#\n") + fp.write("_classdeclarations = {\n") + for codenamemapper in allprecompinfo: + for k, v in codenamemapper.getall('class'): + fp.write("\t%s : %s,\n" % (`ascii(k)`, v)) + fp.write("}\n") + if suitelist: fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1])) for code, modname in suitelist[1:]: fp.write(",\n\t\t%s_Events"%modname) fp.write(",\n\t\taetools.TalkTo):\n") - fp.write("\t_signature = %s\n\n"%`creatorsignature`) + fp.write("\t_signature = %s\n\n"%`ascii(creatorsignature)`) + fp.write("\t_moduleName = '%s'\n\n"%packagename) fp.close() def precompilesuite(suite, basepackage=None): @@ -353,16 +384,16 @@ def compilesuite((suite, fss, modname), major, minor, language, script, fname, b fp = open(fss.as_pathname(), 'w') fss.SetCreatorType('Pyth', 'TEXT') - fp.write('"""Suite %s: %s\n' % (name, desc)) + fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc))) fp.write("Level %d, version %d\n\n" % (level, version)) - fp.write("Generated from %s\n"%fname) + fp.write("Generated from %s\n"%ascii(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`) + fp.write("_code = %s\n\n"% `ascii(code)`) if basepackage and basepackage._code_to_module.has_key(code): # We are an extension of a baseclass (usually an application extending # Standard_Suite or so). Import everything from our base module @@ -421,7 +452,7 @@ def compileevent(fp, event, enumsneeded): 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\t%s : %s,\n"%(`identify(a[0])`, `ascii(a[1])`)) fp.write("\t}\n\n") # @@ -443,7 +474,7 @@ def compileevent(fp, event, enumsneeded): # 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)) + fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc))) if has_arg: fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts)) elif opt_arg: @@ -458,8 +489,8 @@ def compileevent(fp, event, enumsneeded): # # 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`) + fp.write("\t\t_code = %s\n"% `ascii(code)`) + fp.write("\t\t_subcode = %s\n\n"% `ascii(subcode)`) # # Do keyword name substitution # @@ -487,7 +518,7 @@ def compileevent(fp, event, enumsneeded): ename = a[2][0] if ename <> '****': fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" % - (`kname`, identify(ename))) + (`ascii(kname)`, identify(ename))) enumsneeded[ename] = 1 fp.write("\n") # @@ -665,7 +696,7 @@ class ObjectCompiler: else: if self.fp: self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname) - self.fp.write('\t"""%s - %s """\n' % (name, desc)) + self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc))) self.fp.write('\twant = %s\n' % `code`) self.namemappers[0].addnamecode('class', pname, code) for prop in properties: @@ -689,15 +720,15 @@ class ObjectCompiler: else: if self.fp: self.fp.write("class %s(aetools.NProperty):\n" % pname) - self.fp.write('\t"""%s - %s """\n' % (name, what[1])) - self.fp.write("\twhich = %s\n" % `code`) - self.fp.write("\twant = %s\n" % `what[0]`) + self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1]))) + self.fp.write("\twhich = %s\n" % `ascii(code)`) + self.fp.write("\twant = %s\n" % `ascii(what[0])`) self.namemappers[0].addnamecode('property', pname, code) def compileelement(self, elem): [code, keyform] = elem if self.fp: - self.fp.write("# element %s as %s\n" % (`code`, keyform)) + self.fp.write("# element %s as %s\n" % (`ascii(code)`, ascii(keyform))) def fillclasspropsandelems(self, cls): [name, code, desc, properties, elements] = cls @@ -708,12 +739,25 @@ class ObjectCompiler: return plist = [] elist = [] + superclasses = [] for prop in properties: [pname, pcode, what] = prop + if pcode == "c@#^": + superclasses.append(what) if pcode == 'c@#!': continue pname = identify(pname) plist.append(pname) + + superclassnames = [] + for superclass in superclasses: + superId, superDesc, dummy = superclass + superclassname, fullyqualifiedname, module = self.findcodename("class", superId) + superclassnames.append(superclassname) + + if self.fp: + self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`)) + for elem in elements: [ecode, keyform] = elem if ecode == 'c@#!': @@ -721,16 +765,16 @@ class ObjectCompiler: name, ename, module = self.findcodename('class', ecode) if not name: if self.fp: - self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`)) + self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ascii(ecode)`)) else: elist.append((name, ename)) if self.fp: - self.fp.write("%s._propdict = {\n"%cname) + self.fp.write("%s._privpropdict = {\n"%cname) for n in plist: self.fp.write("\t'%s' : %s,\n"%(n, n)) self.fp.write("}\n") - self.fp.write("%s._elemdict = {\n"%cname) + self.fp.write("%s._privelemdict = {\n"%cname) for n, fulln in elist: self.fp.write("\t'%s' : %s,\n"%(n, fulln)) self.fp.write("}\n") @@ -741,7 +785,7 @@ class ObjectCompiler: self.namemappers[0].addnamecode('comparison', iname, code) if self.fp: self.fp.write("class %s(aetools.NComparison):\n" % iname) - self.fp.write('\t"""%s - %s """\n' % (name, comment)) + self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment))) def compileenumeration(self, enum): [code, items] = enum @@ -756,14 +800,14 @@ class ObjectCompiler: def compileenumerator(self, item): [name, code, desc] = item - self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, desc)) + self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `ascii(code)`, desc)) def checkforenum(self, enum): """This enum code is used by an event. Make sure it's available""" name, fullname, module = self.findcodename('enum', enum) if not name: if self.fp: - self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), enum)) + self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum))) return if module: if self.fp: @@ -775,19 +819,19 @@ class ObjectCompiler: self.fp.write("\n#\n# Indices of types declared in this module\n#\n") self.fp.write("_classdeclarations = {\n") for k, v in self.namemappers[0].getall('class'): - self.fp.write("\t%s : %s,\n" % (`k`, v)) + self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v)) self.fp.write("}\n") self.fp.write("\n_propdeclarations = {\n") for k, v in self.namemappers[0].getall('property'): - self.fp.write("\t%s : %s,\n" % (`k`, v)) + self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v)) self.fp.write("}\n") self.fp.write("\n_compdeclarations = {\n") for k, v in self.namemappers[0].getall('comparison'): - self.fp.write("\t%s : %s,\n" % (`k`, v)) + self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v)) self.fp.write("}\n") self.fp.write("\n_enumdeclarations = {\n") for k, v in self.namemappers[0].getall('enum'): - self.fp.write("\t%s : %s,\n" % (`k`, v)) + self.fp.write("\t%s : %s,\n" % (`ascii(k)`, v)) self.fp.write("}\n") def compiledata(data): @@ -806,12 +850,12 @@ def is_enum(data): def getdatadoc(data): [type, descr, flags] = data if descr: - return descr + return ascii(descr) if type == '****': return 'anything' if type == 'obj ': return 'an AE object reference' - return "undocumented, typecode %s"%`type` + return "undocumented, typecode %s"%`ascii(type)` dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"} def compiledataflags(flags): @@ -824,6 +868,18 @@ def compiledataflags(flags): bits.append(`i`) return '[%s]' % string.join(bits) +def ascii(str): + """Return a string with all non-ascii characters hex-encoded""" + if type(str) != type(''): + return map(ascii, str) + rv = '' + for c in str: + if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f): + rv = rv + c + else: + rv = rv + '\\x%02.2x' % ord(c) + return rv + def identify(str): """Turn any string into an identifier: - replace space by _ @@ -852,4 +908,4 @@ def identify(str): if __name__ == '__main__': main() sys.exit(1) -print identify('for') \ No newline at end of file +print identify('for') -- cgit v0.12