]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Split aepack and aetypes off from aetools (it was getting too big)
authorJack Jansen <jack.jansen@cwi.nl>
Mon, 17 Jul 1995 11:42:23 +0000 (11:42 +0000)
committerJack Jansen <jack.jansen@cwi.nl>
Mon, 17 Jul 1995 11:42:23 +0000 (11:42 +0000)
Added support for all basic types mentioned in Apple Event Registry
Added support for automatically-generated suites.

Mac/Lib/toolbox/aepack.py [new file with mode: 0644]
Mac/Lib/toolbox/aetools.py
Mac/Lib/toolbox/aetypes.py [new file with mode: 0644]

diff --git a/Mac/Lib/toolbox/aepack.py b/Mac/Lib/toolbox/aepack.py
new file mode 100644 (file)
index 0000000..8c9def7
--- /dev/null
@@ -0,0 +1,356 @@
+"""Tools for use in AppleEvent clients and servers:
+conversion between AE types and python types
+
+pack(x) converts a Python object to an AEDesc object
+unpack(desc) does the reverse
+coerce(x, wanted_sample) coerces a python object to another python object
+"""
+
+#
+# This code was originally written by Guido, and modified/extended by Jack
+# to include the various types that were missing. The reference used is
+# Apple Event Registry, chapter 9.
+#
+
+import struct
+import string
+import types
+from string import strip
+from types import *
+import AE
+from AppleEvents import *
+from AERegistry import *
+from AEObjects import *
+import MacOS
+import macfs
+import StringIO
+import aetypes
+from aetypes import mkenum, mktype
+
+# These ones seem to be missing from AppleEvents
+# (they're in AERegistry.h)
+
+#typeColorTable = 'clrt'
+#typeDrawingArea = 'cdrw'
+#typePixelMap = 'cpix'
+#typePixelMapMinus = 'tpmm'
+#typeRotation = 'trot'
+#typeTextStyles = 'tsty'
+#typeStyledText = 'STXT'
+#typeAEText = 'tTXT'
+#typeEnumeration = 'enum'
+
+#
+# Some AE types are immedeately coerced into something
+# we like better (and which is equivalent)
+#
+unpacker_coercions = {
+       typeComp : typeExtended,
+       typeColorTable : typeAEList,
+       typeDrawingArea : typeAERecord,
+       typeFixed : typeExtended,
+       typeFloat : typeExtended,
+       typePixelMap : typeAERecord,
+       typeRotation : typeAERecord,
+       typeStyledText : typeAERecord,
+       typeTextStyles : typeAERecord,
+};
+
+#
+# Some python types we need in the packer:
+#
+AEDescType = type(AE.AECreateDesc('TEXT', ''))
+_sample_fss = macfs.FSSpec(':')
+_sample_alias = _sample_fss.NewAliasMinimal()
+FSSType = type(_sample_fss)
+AliasType = type(_sample_alias)
+
+def pack(x, forcetype = None):
+       """Pack a python object into an AE descriptor"""
+       
+       if forcetype:
+               if type(x) is StringType:
+                       return AE.AECreateDesc(forcetype, x)
+               else:
+                       return pack(x).AECoerceDesc(forcetype)
+                       
+       if x == None:
+               return AE.AECreateDesc('null', '')
+               
+       t = type(x)
+       if t == AEDescType:
+               return x
+       if t == FSSType:
+               return AE.AECreateDesc('fss ', x.data)
+       if t == AliasType:
+               return AE.AECreateDesc('alis', x.data)
+       if t == IntType:
+               return AE.AECreateDesc('long', struct.pack('l', x))
+       if t == FloatType:
+               #
+               # XXXX (note by Guido) Weird thing -- Think C's "double" is 10 bytes, but
+               # struct.pack('d') return 12 bytes (and struct.unpack requires
+               # them, too).  The first 2 bytes seem to be repeated...
+               # Probably an alignment problem
+               # XXXX (note by Jack) haven't checked this under MW
+               #
+               return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
+       if t == StringType:
+               return AE.AECreateDesc('TEXT', x)
+       if t == ListType:
+               list = AE.AECreateList('', 0)
+               for item in x:
+                       list.AEPutDesc(0, pack(item))
+               return list
+       if t == DictionaryType:
+               record = AE.AECreateList('', 1)
+               for key, value in x.items():
+                       record.AEPutParamDesc(key, pack(value))
+               return record
+       if t == InstanceType and hasattr(x, '__aepack__'):
+               return x.__aepack__()
+       return AE.AECreateDesc('TEXT', repr(x)) # Copout
+
+def unpack(desc):
+       """Unpack an AE descriptor to a python object"""
+       t = desc.type
+       
+       if unpacker_coercions.has_key(t):
+               desc = desc.AECoerceDesc(unpacker_coercions[t])
+       
+       if t == typeAEList:
+               l = []
+               for i in range(desc.AECountItems()):
+                       keyword, item = desc.AEGetNthDesc(i+1, '****')
+                       l.append(unpack(item))
+               return l
+       if t == typeAERecord:
+               d = {}
+               for i in range(desc.AECountItems()):
+                       keyword, item = desc.AEGetNthDesc(i+1, '****')
+                       d[keyword] = unpack(item)
+               return d
+       if t == typeAEText:
+               record = desc.AECoerceDesc('reco')
+               return mkaetext(unpack(record))
+       if t == typeAlias:
+               return macfs.RawAlias(desc.data)
+       # typeAppleEvent returned as unknown
+       if t == typeBoolean:
+               return struct.unpack('b', desc.data)[0]
+       if t == typeChar:
+               return desc.data
+       # typeColorTable coerced to typeAEList
+       # typeComp coerced to extended
+       # typeData returned as unknown
+       # typeDrawingArea coerced to typeAERecord
+       if t == typeEnumeration:
+               return mkenum(desc.data)
+       # typeEPS returned as unknown
+       if t == typeExtended:
+               data = desc.data
+               # XXX See corresponding note for pack()
+               return struct.unpack('d', data[:2] + data)[0]
+       if t == typeFalse:
+               return 0
+       # typeFixed coerced to extended
+       # typeFloat coerced to extended
+       if t == typeFSS:
+               return macfs.RawFSSpec(desc.data)
+       if t == typeInsertionLoc:
+               record = desc.AECoerceDesc('reco')
+               return mkinsertionloc(unpack(record))
+       # typeInteger equal to typeLongInteger
+       if t == typeIntlText:
+               script, language = struct.unpack('hh', desc.data[:4])
+               return aetypes.IntlText(script, language, desc.data[4:])
+       if t == typeIntlWritingCode:
+               script, language = struct.unpack('hh', desc.data)
+               return aetypes.IntlWritingCode(script, language)
+       if t == typeKeyword:
+               return mkkeyword(desc.data)
+       # typeLongFloat is equal to typeFloat
+       if t == typeLongInteger:
+               return struct.unpack('l', desc.data)[0]
+       if t == typeNull:
+               return None
+       if t == typeMagnitude:
+               v = struct.unpack('l', desc.data)
+               if v < 0:
+                       v = 0x100000000L + v
+               return v
+       if t == typeObjectSpecifier:
+               record = desc.AECoerceDesc('reco')
+               return mkobject(unpack(record))
+       # typePict returned as unknown
+       # typePixelMap coerced to typeAERecord
+       # typePixelMapMinus returned as unknown
+       # typeProcessSerialNumber returned as unknown
+       if t == typeQDPoint:
+               v, h = struct.unpack('hh', desc.data)
+               return aetypes.QDPoint(v, h)
+       if t == typeQDRectangle:
+               v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
+               return aetypes.QDRectangle(v0, h0, v1, h1)
+       if t == typeRGBColor:
+               r, g, b = struct.unpack('hhh', desc.data)
+               return aetypes.RGBColor(r, g, b)
+       # typeRotation coerced to typeAERecord
+       # typeScrapStyles returned as unknown
+       # typeSessionID returned as unknown
+       if t == typeShortFloat:
+               return struct.unpack('f', desc.data)[0]
+       if t == typeShortInteger:
+               return struct.unpack('h', desc.data)[0]
+       # typeSMFloat identical to typeShortFloat
+       # typeSMInt     indetical to typeShortInt
+       # typeStyledText coerced to typeAERecord
+       if t == typeTargetID:
+               return mktargetid(desc.data)
+       # typeTextStyles coerced to typeAERecord
+       # typeTIFF returned as unknown
+       if t == typeTrue:
+               return 1
+       if t == typeType:
+               return mktype(desc.data)
+       #
+       # The following are special
+       #
+       if t == 'rang':
+               record = desc.AECoerceDesc('reco')
+               return mkrange(unpack(record))
+       if t == 'cmpd':
+               record = desc.AECoerceDesc('reco')
+               return mkcomparison(unpack(record))
+       if t == 'logi':
+               record = desc.AECoerceDesc('reco')
+               return mklogical(unpack(record))
+       return mkunknown(desc.type, desc.data)
+       
+def coerce(data, egdata):
+       """Coerce a python object to another type using the AE coercers"""
+       pdata = pack(data)
+       pegdata = pack(egdata)
+       pdata = pdata.AECoerceDesc(pegdata.type)
+       return unpack(pdata)
+
+#
+# Helper routines for unpack
+#
+def mktargetid(data):
+       sessionID = getlong(data[:4])
+       name = mkppcportrec(data[4:4+72])
+       location = mklocationnamerec(data[76:76+36])
+       rcvrName = mkppcportrec(data[112:112+72])
+       return sessionID, name, location, rcvrName
+
+def mkppcportrec(rec):
+       namescript = getword(rec[:2])
+       name = getpstr(rec[2:2+33])
+       portkind = getword(rec[36:38])
+       if portkind == 1:
+               ctor = rec[38:42]
+               type = rec[42:46]
+               identity = (ctor, type)
+       else:
+               identity = getpstr(rec[38:38+33])
+       return namescript, name, portkind, identity
+
+def mklocationnamerec(rec):
+       kind = getword(rec[:2])
+       stuff = rec[2:]
+       if kind == 0: stuff = None
+       if kind == 2: stuff = getpstr(stuff)
+       return kind, stuff
+
+def mkunknown(type, data):
+       return aetypes.Unknown(type, data)
+
+def getpstr(s):
+       return s[1:1+ord(s[0])]
+
+def getlong(s):
+       return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def getword(s):
+       return (ord(s[0])<<8) | (ord(s[1])<<0)
+
+def mkkeyword(keyword):
+       return aetypes.Keyword(keyword)
+
+def mkrange(dict):
+       return aetypes.Range(dict['star'], dict['stop'])
+
+def mkcomparison(dict):
+       return aetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
+
+def mklogical(dict):
+       return aetypes.Logical(dict['logc'], dict['term'])
+
+def mkstyledtext(dict):
+       return aetypes.StyledText(dict['ksty'], dict['ktxt'])
+       
+def mkaetext(dict):
+       return aetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
+       
+def mkinsertionloc(dict):
+       return aetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])
+
+def mkobject(dict):
+       want = dict['want'].type
+       form = dict['form'].enum
+       seld = dict['seld']
+       fr   = dict['from']
+       if form in ('name', 'indx', 'rang', 'test'):
+               if want == 'text': return aetypes.Text(seld, fr)
+               if want == 'cha ': return aetypes.Character(seld, fr)
+               if want == 'cwor': return aetypes.Word(seld, fr)
+               if want == 'clin': return aetypes.Line(seld, fr)
+               if want == 'cpar': return aetypes.Paragraph(seld, fr)
+               if want == 'cwin': return aetypes.Window(seld, fr)
+               if want == 'docu': return aetypes.Document(seld, fr)
+               if want == 'file': return aetypes.File(seld, fr)
+               if want == 'cins': return aetypes.InsertionPoint(seld, fr)
+       if want == 'prop' and form == 'prop' and aetypes.IsType(seld):
+               return aetypes.Property(seld.type, fr)
+       return aetypes.ObjectSpecifier(want, form, seld, fr)
+
+def _test():
+       """Test program. Pack and unpack various things"""
+       objs = [
+               'a string',
+               12,
+               12.0,
+               None,
+               ['a', 'list', 'of', 'strings'],
+               {'key1': 'value1', 'key2':'value2'},
+               macfs.FSSpec(':'),
+               macfs.FSSpec(':').NewAliasMinimal(),
+               aetypes.Enum('enum'),
+               aetypes.Type('type'),
+               aetypes.Keyword('kwrd'),
+               aetypes.Range(1, 10),
+               aetypes.Comparison(1, '<   ', 10),
+               aetypes.Logical('not ', 1),
+               # Cannot do StyledText
+               # Cannot do AEText
+               aetypes.IntlText(0, 0, 'international text'),
+               aetypes.IntlWritingCode(0,0),
+               aetypes.QDPoint(50,100),
+               aetypes.QDRectangle(50,100,150,200),
+               aetypes.RGBColor(0x7000, 0x6000, 0x5000),
+               aetypes.Unknown('xxxx', 'unknown type data'),
+               aetypes.Character(1),
+               aetypes.Character(2, aetypes.Line(2)),
+       ]
+       for o in objs:
+               print 'BEFORE', o, `o`
+               packed = pack(o)
+               unpacked = unpack(packed)
+               print 'AFTER ', unpacked, `unpacked`
+       import sys
+       sys.exit(1)
+       
+if __name__ == '__main__':
+       _test()
+       
index bb0d20f2eb18b0abc78e6a0052d787c6d282db16..565cdec89b537fb1454c3d7e811548aaa4a3b9f1 100644 (file)
@@ -15,477 +15,22 @@ and pack(x) will create an AE object reference equivalent to AppleScript's
 
        character 1 of document "foobar"
 
+Some of the stuff that appears to be exported from this module comes from other
+files: the pack stuff from aepack, the objects from aetypes.
+
 """
 
 
-import struct
-import string
-from string import strip
 from types import *
 import AE
+import AppleEvents
 import MacOS
-import macfs
-import StringIO
-
-
-AEDescType = type(AE.AECreateDesc('TEXT', ''))
-
-FSSType = type(macfs.FSSpec(':'))
-
-
-def pack(x, forcetype = None):
-       if forcetype:
-               if type(x) is StringType:
-                       return AE.AECreateDesc(forcetype, x)
-               else:
-                       return pack(x).AECoerceDesc(forcetype)
-       if x == None:
-               return AE.AECreateDesc('null', '')
-       t = type(x)
-       if t == AEDescType:
-               return x
-       if t == FSSType:
-               vol, dir, filename = x.as_tuple()
-               fnlen = len(filename)
-               header = struct.pack('hlb', vol, dir, fnlen)
-               padding = '\0'*(63-fnlen)
-               return AE.AECreateDesc('fss ', header + filename + padding)
-       if t == IntType:
-               return AE.AECreateDesc('long', struct.pack('l', x))
-       if t == FloatType:
-               # XXX Weird thing -- Think C's "double" is 10 bytes, but
-               # struct.pack('d') return 12 bytes (and struct.unpack requires
-               # them, too).  The first 2 bytes seem to be repeated...
-               # Probably an alignment problem
-               return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
-       if t == StringType:
-               return AE.AECreateDesc('TEXT', x)
-       if t == ListType:
-               list = AE.AECreateList('', 0)
-               for item in x:
-                       list.AEPutDesc(0, pack(item))
-               return list
-       if t == DictionaryType:
-               record = AE.AECreateList('', 1)
-               for key, value in x.items():
-                       record.AEPutParamDesc(key, pack(value))
-               return record
-       if t == InstanceType and hasattr(x, '__aepack__'):
-               return x.__aepack__()
-       return AE.AECreateDesc('TEXT', repr(x)) # Copout
-
-
-def unpack(desc):
-       t = desc.type
-       if t == 'TEXT':
-               return desc.data
-       if t == 'fals':
-               return 0
-       if t == 'true':
-               return 1
-       if t == 'enum':
-               return mkenum(desc.data)
-       if t == 'type':
-               return mktype(desc.data)
-       if t == 'long':
-               return struct.unpack('l', desc.data)[0]
-       if t == 'shor':
-               return struct.unpack('h', desc.data)[0]
-       if t == 'sing':
-               return struct.unpack('f', desc.data)[0]
-       if t == 'exte':
-               data = desc.data
-               # XXX See corresponding note for pack()
-               return struct.unpack('d', data[:2] + data)[0]
-       if t in ('doub', 'comp', 'magn'):
-               return unpack(desc.AECoerceDesc('exte'))
-       if t == 'null':
-               return None
-       if t == 'list':
-               l = []
-               for i in range(desc.AECountItems()):
-                       keyword, item = desc.AEGetNthDesc(i+1, '****')
-                       l.append(unpack(item))
-               return l
-       if t == 'reco':
-               d = {}
-               for i in range(desc.AECountItems()):
-                       keyword, item = desc.AEGetNthDesc(i+1, '****')
-                       d[keyword] = unpack(item)
-               return d
-       if t == 'obj ':
-               record = desc.AECoerceDesc('reco')
-               return mkobject(unpack(record))
-       if t == 'rang':
-               record = desc.AECoerceDesc('reco')
-               return mkrange(unpack(record))
-       if t == 'cmpd':
-               record = desc.AECoerceDesc('reco')
-               return mkcomparison(unpack(record))
-       if t == 'logi':
-               record = desc.AECoerceDesc('reco')
-               return mklogical(unpack(record))
-       if t == 'targ':
-               return mktargetid(desc.data)
-       if t == 'alis':
-               # XXX Can't handle alias records yet, so coerce to FS spec...
-               return unpack(desc.AECoerceDesc('fss '))
-       if t == 'fss ':
-               return mkfss(desc.data)
-       return mkunknown(desc.type, desc.data)
-
-
-def mkfss(data):
-       print "mkfss data =", `data`
-       vol, dir, fnlen = struct.unpack('hlb', data[:7])
-       filename = data[7:7+fnlen]
-       print (vol, dir, fnlen, filename)
-       return macfs.FSSpec((vol, dir, filename))
-
-
-def mktargetid(data):
-       sessionID = getlong(data[:4])
-       name = mkppcportrec(data[4:4+72])
-       print len(name), `name`
-       location = mklocationnamerec(data[76:76+36])
-       rcvrName = mkppcportrec(data[112:112+72])
-       return sessionID, name, location, rcvrName
-
-def mkppcportrec(rec):
-       namescript = getword(rec[:2])
-       name = getpstr(rec[2:2+33])
-       portkind = getword(rec[36:38])
-       if portkind == 1:
-               ctor = rec[38:42]
-               type = rec[42:46]
-               identity = (ctor, type)
-       else:
-               identity = getpstr(rec[38:38+33])
-       return namescript, name, portkind, identity
-
-def mklocationnamerec(rec):
-       kind = getword(rec[:2])
-       stuff = rec[2:]
-       if kind == 0: stuff = None
-       if kind == 2: stuff = getpstr(stuff)
-       return kind, stuff
-
-def getpstr(s):
-       return s[1:1+ord(s[0])]
-
-def getlong(s):
-       return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
-
-def getword(s):
-       return (ord(s[0])<<8) | (ord(s[1])<<0)
-
-
-def mkunknown(type, data):
-       return Unknown(type, data)
-
-class Unknown:
-       
-       def __init__(self, type, data):
-               self.type = type
-               self.data = data
-       
-       def __repr__(self):
-               return "Unknown(%s, %s)" % (`self.type`, `self.data`)
-       
-       def __aepack__(self):
-               return pack(self.data, self.type)
-
-
-def IsSubclass(cls, base):
-       """Test whether CLASS1 is the same as or a subclass of CLASS2"""
-       # Loop to optimize for single inheritance
-       while 1:
-               if cls is base: return 1
-               if len(cls.__bases__) <> 1: break
-               cls = cls.__bases__[0]
-       # Recurse to cope with multiple inheritance
-       for c in cls.__bases__:
-               if IsSubclass(c, base): return 1
-       return 0
-
-def IsInstance(x, cls):
-       """Test whether OBJECT is an instance of (a subclass of) CLASS"""
-       return type(x) is InstanceType and IsSubclass(x.__class__, cls)
-
-
-def nice(s):
-       if type(s) is StringType: return repr(s)
-       else: return str(s)
-
-
-def mkenum(enum):
-       if IsEnum(enum): return enum
-       return Enum(enum)
-
-class Enum:
-       
-       def __init__(self, enum):
-               self.enum = "%-4.4s" % str(enum)
-       
-       def __repr__(self):
-               return "Enum(%s)" % `self.enum`
-       
-       def __str__(self):
-               return strip(self.enum)
-       
-       def __aepack__(self):
-               return pack(self.enum, 'enum')
-
-def IsEnum(x):
-       return IsInstance(x, Enum)
-
-
-def mktype(type):
-       if IsType(type): return type
-       return Type(type)
-
-class Type:
-       
-       def __init__(self, type):
-               self.type = "%-4.4s" % str(type)
-       
-       def __repr__(self):
-               return "Type(%s)" % `self.type`
-       
-       def __str__(self):
-               return strip(self.type)
-       
-       def __aepack__(self):
-               return pack(self.type, 'type')
-
-def IsType(x):
-       return IsInstance(x, Type)
-
-
-def mkrange(dict):
-       return Range(dict['star'], dict['stop'])
-
-class Range:
-       
-       def __init__(self, start, stop):
-               self.start = start
-               self.stop = stop
-       
-       def __repr__(self):
-               return "Range(%s, %s)" % (`self.start`, `self.stop`)
-       
-       def __str__(self):
-               return "%s thru %s" % (nice(self.start), nice(self.stop))
-       
-       def __aepack__(self):
-               return pack({'star': self.start, 'stop': self.stop}, 'rang')
-
-def IsRange(x):
-       return IsInstance(x, Range)
-
-
-def mkcomparison(dict):
-       return Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
-
-class Comparison:
-       
-       def __init__(self, obj1, relo, obj2):
-               self.obj1 = obj1
-               self.relo = "%-4.4s" % str(relo)
-               self.obj2 = obj2
-       
-       def __repr__(self):
-               return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`)
-       
-       def __str__(self):
-               return "%s %s %s" % (nice(self.obj1), strip(self.relo), nice(self.obj2))
-       
-       def __aepack__(self):
-               return pack({'obj1': self.obj1,
-                            'relo': mkenum(self.relo),
-                            'obj2': self.obj2},
-                           'cmpd')
-
-def IsComparison(x):
-       return IsInstance(x, Comparison)
-
-
-def mklogical(dict):
-       return Logical(dict['logc'], dict['term'])
-
-class Logical:
-       
-       def __init__(self, logc, term):
-               self.logc = "%-4.4s" % str(logc)
-               self.term = term
-       
-       def __repr__(self):
-               return "Logical(%s, %s)" % (`self.logc`, `self.term`)
-       
-       def __str__(self):
-               if type(self.term) == ListType and len(self.term) == 2:
-                       return "%s %s %s" % (nice(self.term[0]),
-                                            strip(self.logc),
-                                            nice(self.term[1]))
-               else:
-                       return "%s(%s)" % (strip(self.logc), nice(self.term))
-       
-       def __aepack__(self):
-               return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi')
-
-def IsLogical(x):
-       return IsInstance(x, Logical)
-
-
-class ObjectSpecifier:
-       
-       """A class for constructing and manipulation AE object specifiers in python.
-       
-       An object specifier is actually a record with four fields:
-       
-       key     type    description
-       ---     ----    -----------
-       
-       'want'  type    what kind of thing we want,
-                       e.g. word, paragraph or property
-       
-       'form'  enum    how we specify the thing(s) we want,
-                       e.g. by index, by range, by name, or by property specifier
-       
-       'seld'  any     which thing(s) we want,
-                       e.g. its index, its name, or its property specifier
-       
-       'from'  object  the object in which it is contained,
-                       or null, meaning look for it in the application
-       
-       Note that we don't call this class plain "Object", since that name
-       is likely to be used by the application.
-       """
-       
-       def __init__(self, want, form, seld, fr = None):
-               self.want = want
-               self.form = form
-               self.seld = seld
-               self.fr = fr
-       
-       def __repr__(self):
-               s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
-               if self.fr:
-                       s = s + ", %s)" % `self.fr`
-               else:
-                       s = s + ")"
-               return s
-       
-       def __aepack__(self):
-               return pack({'want': mktype(self.want),
-                            'form': mkenum(self.form),
-                            'seld': self.seld,
-                            'from': self.fr},
-                           'obj ')
-
-
-def IsObjectSpecifier(x):
-       return IsInstance(x, ObjectSpecifier)
-
-
-class Property(ObjectSpecifier):
-
-       def __init__(self, which, fr = None):
-               ObjectSpecifier.__init__(self, 'prop', 'prop', mkenum(which), fr)
-
-       def __repr__(self):
-               if self.fr:
-                       return "Property(%s, %s)" % (`self.seld.enum`, `self.fr`)
-               else:
-                       return "Property(%s)" % `self.seld.enum`
-       
-       def __str__(self):
-               if self.fr:
-                       return "Property %s of %s" % (str(self.seld), str(self.fr))
-               else:
-                       return "Property %s" % str(self.seld)
-
-
-class SelectableItem(ObjectSpecifier):
-       
-       def __init__(self, want, seld, fr = None):
-               t = type(seld)
-               if t == StringType:
-                       form = 'name'
-               elif IsRange(seld):
-                       form = 'rang'
-               elif IsComparison(seld) or IsLogical(seld):
-                       form = 'test'
-               else:
-                       form = 'indx'
-               ObjectSpecifier.__init__(self, want, form, seld, fr)
-
-
-class ComponentItem(SelectableItem):
-       # Derived classes *must* set the *class attribute* 'want' to some constant
-       
-       def __init__(self, which, fr = None):
-               SelectableItem.__init__(self, self.want, which, fr)
-       
-       def __repr__(self):
-               if not self.fr:
-                       return "%s(%s)" % (self.__class__.__name__, `self.seld`)
-               return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`)
-       
-       def __str__(self):
-               seld = self.seld
-               if type(seld) == StringType:
-                       ss = repr(seld)
-               elif IsRange(seld):
-                       start, stop = seld.start, seld.stop
-                       if type(start) == InstanceType == type(stop) and \
-                          start.__class__ == self.__class__ == stop.__class__:
-                               ss = str(start.seld) + " thru " + str(stop.seld)
-                       else:
-                               ss = str(seld)
-               else:
-                       ss = str(seld)
-               s = "%s %s" % (self.__class__.__name__, ss)
-               if self.fr: s = s + " of %s" % str(self.fr)
-               return s
-
-
-template = """
-class %s(ComponentItem): want = '%s'
-"""
-
-exec template % ("Text", 'text')
-exec template % ("Character", 'cha ')
-exec template % ("Word", 'cwor')
-exec template % ("Line", 'clin')
-exec template % ("Paragraph", 'cpar')
-exec template % ("Window", 'cwin')
-exec template % ("Document", 'docu')
-exec template % ("File", 'file')
-exec template % ("InsertionPoint", 'cins')
-
-
-def mkobject(dict):
-       want = dict['want'].type
-       form = dict['form'].enum
-       seld = dict['seld']
-       fr   = dict['from']
-       if form in ('name', 'indx', 'rang', 'test'):
-               if want == 'text': return Text(seld, fr)
-               if want == 'cha ': return Character(seld, fr)
-               if want == 'cwor': return Word(seld, fr)
-               if want == 'clin': return Line(seld, fr)
-               if want == 'cpar': return Paragraph(seld, fr)
-               if want == 'cwin': return Window(seld, fr)
-               if want == 'docu': return Document(seld, fr)
-               if want == 'file': return File(seld, fr)
-               if want == 'cins': return InsertionPoint(seld, fr)
-       if want == 'prop' and form == 'prop' and IsType(seld):
-               return Property(seld.type, fr)
-       return ObjectSpecifier(want, form, seld, fr)
 
+from aetypes import *
+from aepack import pack, unpack, coerce, AEDescType
 
 # Special code to unpack an AppleEvent (which is *not* a disguised record!)
+# Note by Jack: No??!? If I read the docs correctly it *is*....
 
 aekeywords = [
        'tran',
@@ -531,8 +76,90 @@ def packevent(ae, parameters = {}, attributes = {}):
        for key, value in attributes.items():
                ae.AEPutAttributeDesc(key, pack(value))
 
-
+#
+# Support routine for automatically generated Suite interfaces
+#
+def keysubst(arguments, keydict):
+       """Replace long name keys by their 4-char counterparts, and check"""
+       ok = keydict.values()
+       for k in arguments.keys():
+               if keydict.has_key(k):
+                       v = arguments[k]
+                       del arguments[k]
+                       arguments[keydict[k]] = v
+               elif k != '----' and k not in ok:
+                       raise TypeError, 'Unknown keyword argument: %s'%k
+                       
+def enumsubst(arguments, key, edict):
+       """Substitute a single enum keyword argument, if it occurs"""
+       if not arguments.has_key(key):
+               return
+       v = arguments[key]
+       ok = edict.values()
+       if edict.has_key(v):
+               arguments[key] = edict[v]
+       elif not v in ok:
+               raise TypeError, 'Unknown enumerator: %s'%v
+               
+def decodeerror(arguments):
+       """Create the 'best' argument for a raise MacOS.Error"""
+       errn = arguments['errn']
+       errarg = (errn, MacOS.GetErrorString(errn))
+       if arguments.has_key('errs'):
+               errarg = errarg + (arguments['errs'],)
+       if arguments.has_key('erob'):
+               errarg = errarg + (arguments['erob'],)
+       return errarg
+
+class TalkTo:
+       """An AE connection to an application"""
+       
+       def __init__(self, signature):
+               """Create a communication channel with a particular application.
+               
+               Addressing the application is done by specifying either a
+               4-byte signature, an AEDesc or an object that will __aepack__
+               to an AEDesc.
+               """
+               if type(signature) == AEDescType:
+                       self.target = signature
+               elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
+                       self.target = signature.__aepack__()
+               elif type(signature) == StringType and len(signature) != 4:
+                       self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
+               else:
+                       raise TypeError, "signature should be 4-char string or AEDesc"
+               self.send_flags = AppleEvents.kAEWaitReply
+               self.send_priority = AppleEvents.kAENormalPriority
+               self.send_timeout = AppleEvents.kAEDefaultTimeout
+       
+       def newevent(self, code, subcode, parameters = {}, attributes = {}):
+               """Create a complete structure for an apple event"""
+               
+               event = AE.AECreateAppleEvent(code, subcode, self.target,
+                         AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
+               packevent(event, parameters, attributes)
+               return event
+       
+       def sendevent(self, event):
+               """Send a pre-created appleevent, await the reply and unpack it"""
+               
+               reply = event.AESend(self.send_flags, self.send_priority,
+                                         self.send_timeout)
+               parameters, attributes = unpackevent(reply)
+               return reply, parameters, attributes
+               
+       def send(self, code, subcode, parameters = {}, attributes = {}):
+               """Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
+               return self.sendevent(self.newevent(code, subcode, parameters, attributes))
+       
+       def activate(self):
+               """Send 'activate' command"""
+               self.send('misc', 'actv')
+       
+       
 # Test program
+# XXXX Should test more, really...
 
 def test():
        target = AE.AECreateDesc('sign', 'KAHL')
diff --git a/Mac/Lib/toolbox/aetypes.py b/Mac/Lib/toolbox/aetypes.py
new file mode 100644 (file)
index 0000000..e0a466f
--- /dev/null
@@ -0,0 +1,452 @@
+"""aetypes - Python objects representing various AE types."""
+
+from AppleEvents import *
+from AERegistry import *
+from AEObjects import *
+import struct
+from types import *
+import string
+
+#
+# convoluted, since there are cyclic dependencies between this file and
+# aetools_convert.
+#
+def pack(*args):
+       from aepack import pack
+       return apply(pack, args)
+       
+def IsSubclass(cls, base):
+       """Test whether CLASS1 is the same as or a subclass of CLASS2"""
+       # Loop to optimize for single inheritance
+       while 1:
+               if cls is base: return 1
+               if len(cls.__bases__) <> 1: break
+               cls = cls.__bases__[0]
+       # Recurse to cope with multiple inheritance
+       for c in cls.__bases__:
+               if IsSubclass(c, base): return 1
+       return 0
+
+def IsInstance(x, cls):
+       """Test whether OBJECT is an instance of (a subclass of) CLASS"""
+       return type(x) is InstanceType and IsSubclass(x.__class__, cls)
+
+def nice(s):
+       """'nice' representation of an object"""
+       if type(s) is StringType: return repr(s)
+       else: return str(s)
+
+class Unknown:
+       """An uninterpreted AE object"""
+       
+       def __init__(self, type, data):
+               self.type = type
+               self.data = data
+       
+       def __repr__(self):
+               return "Unknown(%s, %s)" % (`self.type`, `self.data`)
+       
+       def __aepack__(self):
+               return pack(self.data, self.type)
+
+class Enum:
+       """An AE enumeration value"""
+       
+       def __init__(self, enum):
+               self.enum = "%-4.4s" % str(enum)
+       
+       def __repr__(self):
+               return "Enum(%s)" % `self.enum`
+       
+       def __str__(self):
+               return string.strip(self.enum)
+       
+       def __aepack__(self):
+               return pack(self.enum, typeEnumeration)
+
+def IsEnum(x):
+       return IsInstance(x, Enum)
+
+def mkenum(enum):
+       if IsEnum(enum): return enum
+       return Enum(enum)
+
+class Type:
+       """An AE 4-char typename object"""
+       
+       def __init__(self, type):
+               self.type = "%-4.4s" % str(type)
+       
+       def __repr__(self):
+               return "Type(%s)" % `self.type`
+       
+       def __str__(self):
+               return string.strip(self.type)
+       
+       def __aepack__(self):
+               return pack(self.type, typeType)
+
+def IsType(x):
+       return IsInstance(x, Type)
+
+def mktype(type):
+       if IsType(type): return type
+       return Type(type)
+
+
+class Keyword:
+       """An AE 4-char keyword object"""
+       
+       def __init__(self, keyword):
+               self.keyword = "%-4.4s" % str(keyword)
+       
+       def __repr__(self):
+               return "Keyword(%s)" % `self.keyword`
+       
+       def __str__(self):
+               return string.strip(self.keyword)
+       
+       def __aepack__(self):
+               return pack(self.keyword, typeKeyword)
+
+def IsKeyword(x):
+       return IsInstance(x, Keyword)
+
+class Range:
+       """An AE range object"""
+       
+       def __init__(self, start, stop):
+               self.start = start
+               self.stop = stop
+       
+       def __repr__(self):
+               return "Range(%s, %s)" % (`self.start`, `self.stop`)
+       
+       def __str__(self):
+               return "%s thru %s" % (nice(self.start), nice(self.stop))
+       
+       def __aepack__(self):
+               return pack({'star': self.start, 'stop': self.stop}, 'rang')
+
+def IsRange(x):
+       return IsInstance(x, Range)
+
+class Comparison:
+       """An AE Comparison"""
+       
+       def __init__(self, obj1, relo, obj2):
+               self.obj1 = obj1
+               self.relo = "%-4.4s" % str(relo)
+               self.obj2 = obj2
+       
+       def __repr__(self):
+               return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`)
+       
+       def __str__(self):
+               return "%s %s %s" % (nice(self.obj1), string.strip(self.relo), nice(self.obj2))
+       
+       def __aepack__(self):
+               return pack({'obj1': self.obj1,
+                            'relo': mkenum(self.relo),
+                            'obj2': self.obj2},
+                           'cmpd')
+
+def IsComparison(x):
+       return IsInstance(x, Comparison)
+
+class Logical:
+       """An AE logical expression object"""
+       
+       def __init__(self, logc, term):
+               self.logc = "%-4.4s" % str(logc)
+               self.term = term
+       
+       def __repr__(self):
+               return "Logical(%s, %s)" % (`self.logc`, `self.term`)
+       
+       def __str__(self):
+               if type(self.term) == ListType and len(self.term) == 2:
+                       return "%s %s %s" % (nice(self.term[0]),
+                                            string.strip(self.logc),
+                                            nice(self.term[1]))
+               else:
+                       return "%s(%s)" % (string.strip(self.logc), nice(self.term))
+       
+       def __aepack__(self):
+               return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi')
+
+def IsLogical(x):
+       return IsInstance(x, Logical)
+
+class StyledText:
+       """An AE object respresenting text in a certain style"""
+       
+       def __init__(self, style, text):
+               self.style = style
+               self.text = text
+       
+       def __repr__(self):
+               return "StyledText(%s, %s)" % (`self.style`, `self.text`)
+       
+       def __str__(self):
+               return self.text
+       
+       def __aepack__(self):
+               return pack({'ksty': self.style, 'ktxt': self.text}, 'STXT')
+
+def IsStyledText(x):
+       return IsInstance(x, StyledText)
+
+class AEText:
+       """An AE text object with style, script and language specified"""
+       
+       def __init__(self, script, style, text):
+               self.script = script
+               self.style = style
+               self.text = text
+       
+       def __repr__(self):
+               return "AEText(%s, %s, %s)" % (`self.script`, `self.style`, `self.text`)
+       
+       def __str__(self):
+               return self.text
+       
+       def __aepack__(self):
+               return pack({keyAEScriptTag: self.script, keyAEStyles: self.style,
+                                keyAEText: self.text}, typeAEText)
+
+def IsAEText(x):
+       return IsInstance(x, AEText)
+
+class IntlText:
+       """A text object with script and language specified"""
+       
+       def __init__(self, script, language, text):
+               self.script = script
+               self.language = language
+               self.text = text
+       
+       def __repr__(self):
+               return "IntlText(%s, %s, %s)" % (`self.script`, `self.language`, `self.text`)
+       
+       def __str__(self):
+               return self.text
+       
+       def __aepack__(self):
+               return pack(struct.pack('hh', self.script, self.language)+self.text,
+                       typeIntlText)
+
+def IsIntlText(x):
+       return IsInstance(x, IntlText)
+
+class IntlWritingCode:
+       """An object representing script and language"""
+       
+       def __init__(self, script, language):
+               self.script = script
+               self.language = language
+       
+       def __repr__(self):
+               return "IntlWritingCode(%s, %s)" % (`self.script`, `self.language`)
+       
+       def __str__(self):
+               return "script system %d, language %d"%(self.script, self.language)
+       
+       def __aepack__(self):
+               return pack(struct.pack('hh', self.script, self.language),
+                       typeIntlWritingCode)
+
+def IsIntlWritingCode(x):
+       return IsInstance(x, IntlWritingCode)
+
+class QDPoint:
+       """A point"""
+       
+       def __init__(self, v, h):
+               self.v = v
+               self.h = h
+       
+       def __repr__(self):
+               return "QDPoint(%s, %s)" % (`self.v`, `self.h`)
+       
+       def __str__(self):
+               return "(%d, %d)"%(self.v, self.h)
+       
+       def __aepack__(self):
+               return pack(struct.pack('hh', self.v, self.h),
+                       typeQDPoint)
+
+def IsQDPoint(x):
+       return IsInstance(x, QDPoint)
+
+class QDRectangle:
+       """A rectangle"""
+       
+       def __init__(self, v0, h0, v1, h1):
+               self.v0 = v0
+               self.h0 = h0
+               self.v1 = v1
+               self.h1 = h1
+       
+       def __repr__(self):
+               return "QDRectangle(%s, %s, %s, %s)" % (`self.v0`, `self.h0`,
+                               `self.v1`, `self.h1`)
+       
+       def __str__(self):
+               return "(%d, %d)-(%d, %d)"%(self.v0, self.h0, self.v1, self.h1)
+       
+       def __aepack__(self):
+               return pack(struct.pack('hhhh', self.v0, self.h0, self.v1, self.h1),
+                       typeQDRectangle)
+
+def IsQDRectangle(x):
+       return IsInstance(x, QDRectangle)
+
+class RGBColor:
+       """An RGB color"""
+       
+       def __init__(self, r, g, b):
+               self.r = r
+               self.g = g
+               self.b = b
+                       
+       def __repr__(self):
+               return "RGBColor(%s, %s, %s)" % (`self.r`, `self.g`, `self.b`)
+       
+       def __str__(self):
+               return "0x%x red, 0x%x green, 0x%x blue"% (self.r, self.g, self.b)
+       
+       def __aepack__(self):
+               return pack(struct.pack('hhh', self.r, self.g, self.b),
+                       typeRGBColor)
+
+def IsRGBColor(x):
+       return IsInstance(x, RGBColor)
+
+class ObjectSpecifier:
+       
+       """A class for constructing and manipulation AE object specifiers in python.
+       
+       An object specifier is actually a record with four fields:
+       
+       key     type    description
+       ---     ----    -----------
+       
+       'want'  type    what kind of thing we want,
+                       e.g. word, paragraph or property
+       
+       'form'  enum    how we specify the thing(s) we want,
+                       e.g. by index, by range, by name, or by property specifier
+       
+       'seld'  any     which thing(s) we want,
+                       e.g. its index, its name, or its property specifier
+       
+       'from'  object  the object in which it is contained,
+                       or null, meaning look for it in the application
+       
+       Note that we don't call this class plain "Object", since that name
+       is likely to be used by the application.
+       """
+       
+       def __init__(self, want, form, seld, fr = None):
+               self.want = want
+               self.form = form
+               self.seld = seld
+               self.fr = fr
+       
+       def __repr__(self):
+               s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
+               if self.fr:
+                       s = s + ", %s)" % `self.fr`
+               else:
+                       s = s + ")"
+               return s
+       
+       def __aepack__(self):
+               return pack({'want': mktype(self.want),
+                            'form': mkenum(self.form),
+                            'seld': self.seld,
+                            'from': self.fr},
+                           'obj ')
+
+
+def IsObjectSpecifier(x):
+       return IsInstance(x, ObjectSpecifier)
+
+
+class Property(ObjectSpecifier):
+
+       def __init__(self, which, fr = None):
+               ObjectSpecifier.__init__(self, 'prop', 'prop', mkenum(which), fr)
+
+       def __repr__(self):
+               if self.fr:
+                       return "Property(%s, %s)" % (`self.seld.enum`, `self.fr`)
+               else:
+                       return "Property(%s)" % `self.seld.enum`
+       
+       def __str__(self):
+               if self.fr:
+                       return "Property %s of %s" % (str(self.seld), str(self.fr))
+               else:
+                       return "Property %s" % str(self.seld)
+
+
+class SelectableItem(ObjectSpecifier):
+       
+       def __init__(self, want, seld, fr = None):
+               t = type(seld)
+               if t == StringType:
+                       form = 'name'
+               elif IsRange(seld):
+                       form = 'rang'
+               elif IsComparison(seld) or IsLogical(seld):
+                       form = 'test'
+               else:
+                       form = 'indx'
+               ObjectSpecifier.__init__(self, want, form, seld, fr)
+
+
+class ComponentItem(SelectableItem):
+       # Derived classes *must* set the *class attribute* 'want' to some constant
+       
+       def __init__(self, which, fr = None):
+               SelectableItem.__init__(self, self.want, which, fr)
+       
+       def __repr__(self):
+               if not self.fr:
+                       return "%s(%s)" % (self.__class__.__name__, `self.seld`)
+               return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`)
+       
+       def __str__(self):
+               seld = self.seld
+               if type(seld) == StringType:
+                       ss = repr(seld)
+               elif IsRange(seld):
+                       start, stop = seld.start, seld.stop
+                       if type(start) == InstanceType == type(stop) and \
+                          start.__class__ == self.__class__ == stop.__class__:
+                               ss = str(start.seld) + " thru " + str(stop.seld)
+                       else:
+                               ss = str(seld)
+               else:
+                       ss = str(seld)
+               s = "%s %s" % (self.__class__.__name__, ss)
+               if self.fr: s = s + " of %s" % str(self.fr)
+               return s
+
+
+template = """
+class %s(ComponentItem): want = '%s'
+"""
+
+exec template % ("Text", 'text')
+exec template % ("Character", 'cha ')
+exec template % ("Word", 'cwor')
+exec template % ("Line", 'clin')
+exec template % ("Paragraph", 'cpar')
+exec template % ("Window", 'cwin')
+exec template % ("Document", 'docu')
+exec template % ("File", 'file')
+exec template % ("InsertionPoint", 'cins')
+