Donovan Preston's patch #538395, with some mods by me.
authorJack Jansen <jack.jansen@cwi.nl>
Wed, 7 Aug 2002 14:49:00 +0000 (14:49 +0000)
committerJack Jansen <jack.jansen@cwi.nl>
Wed, 7 Aug 2002 14:49:00 +0000 (14:49 +0000)
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
Mac/Lib/aetools.py
Mac/Lib/aetypes.py
Mac/scripts/gensuitemodule.py

index f58dd33f4a6d5401cf127ace5eaef2148f716c61..dfb295391cbf07c9c4998a4cd504d20672f199a9 100644 (file)
@@ -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 = [
index ba42f03c1b96c7ea065bcaa114b2b5c187eee241..5495dfafb6a9dba0afbda4e9a1c4335352b04177 100644 (file)
@@ -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
 
index 8fe8cf9ac13fb8b8754f3e1dc5efd47e154ca298..d376e74f50718af92e8a8c553291ea378d504151 100644 (file)
@@ -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"""
        
index 3e2366165ff71773854474910edfdead62eec8cf..6b17a7980860a5587dda58dff42127e22b5ca814 100644 (file)
@@ -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')