|  | """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 baetypes | 
|  | from baetypes import mkenum, mktype | 
|  |  | 
|  | import calldll | 
|  |  | 
|  | OSL = calldll.getlibrary('ObjectSupportLib') | 
|  |  | 
|  | # 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""" | 
|  | #	print 'aepack', x, type(x), forcetype | 
|  | #	if type(x) == TupleType: | 
|  | #		forcetype, x = x | 
|  | if forcetype: | 
|  | print x, 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:]) | 
|  | return AE.AECreateDesc('exte', struct.pack('d', x)) | 
|  | 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 | 
|  | #	print t | 
|  |  | 
|  | if unpacker_coercions.has_key(t): | 
|  | desc = desc.AECoerceDesc(unpacker_coercions[t]) | 
|  | t = desc.type # This is a guess by Jack.... | 
|  |  | 
|  | 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: | 
|  | #		print desc, type(desc), len(desc) | 
|  | data = desc.data | 
|  | #		print `data[:8]`, type(data), len(data[:8]) | 
|  | #		print struct.unpack('=d', data[:8])[0] | 
|  | #		print string.atoi(data), type(data), len(data) | 
|  | #		print struct.calcsize(data) | 
|  | # XXX See corresponding note for pack() | 
|  | #		return struct.unpack('d', data[:2] + data)[0] | 
|  | return struct.unpack('d', data[:8])[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 baetypes.IntlText(script, language, desc.data[4:]) | 
|  | if t == typeIntlWritingCode: | 
|  | script, language = struct.unpack('hh', desc.data) | 
|  | return baetypes.IntlWritingCode(script, language) | 
|  | if t == typeKeyword: | 
|  | return mkkeyword(desc.data) | 
|  | # typeLongFloat is equal to typeFloat | 
|  | if t == typeLongInteger: | 
|  | #		print t, struct.unpack('l', desc.data) | 
|  | 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: | 
|  | import Res | 
|  | #		print desc, type(desc) | 
|  | #		print desc.__members__ | 
|  | #		print desc.data, desc.type | 
|  | #		print unpack(desc) | 
|  | #		getOSL = calldll.newcall(OSL.AEResolve, 'OSErr', 'InHandle', 'InShort')#, 'InString') | 
|  | #		print 'OSL', getOSL(rdesc, 0)#, desc.data) | 
|  | record = desc.AECoerceDesc('reco') | 
|  | #		print record | 
|  | 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 baetypes.QDPoint(v, h) | 
|  | if t == typeQDRectangle: | 
|  | v0, h0, v1, h1 = struct.unpack('hhhh', desc.data) | 
|  | return baetypes.QDRectangle(v0, h0, v1, h1) | 
|  | if t == typeRGBColor: | 
|  | r, g, b = struct.unpack('hhh', desc.data) | 
|  | return baetypes.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: | 
|  | #		print t, desc.data | 
|  | #		print struct.unpack('h', desc.data)[0] | 
|  | 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: | 
|  | #		print t, desc.data | 
|  | 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 baetypes.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 baetypes.Keyword(keyword) | 
|  |  | 
|  | def mkrange(dict): | 
|  | return baetypes.Range(dict['star'], dict['stop']) | 
|  |  | 
|  | def mkcomparison(dict): | 
|  | return baetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2']) | 
|  |  | 
|  | def mklogical(dict): | 
|  | return baetypes.Logical(dict['logc'], dict['term']) | 
|  |  | 
|  | def mkstyledtext(dict): | 
|  | return baetypes.StyledText(dict['ksty'], dict['ktxt']) | 
|  |  | 
|  | def mkaetext(dict): | 
|  | return baetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText]) | 
|  |  | 
|  | def mkinsertionloc(dict): | 
|  | return baetypes.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 baetypes.Text(seld, fr) | 
|  | if want == 'cha ': return baetypes.Character(seld, fr) | 
|  | if want == 'cwor': return baetypes.Word(seld, fr) | 
|  | if want == 'clin': return baetypes.Line(seld, fr) | 
|  | if want == 'cpar': return baetypes.Paragraph(seld, fr) | 
|  | if want == 'cwin': return baetypes.Window(seld, fr) | 
|  | if want == 'docu': return baetypes.Document(seld, fr) | 
|  | if want == 'file': return baetypes.File(seld, fr) | 
|  | if want == 'cins': return baetypes.InsertionPoint(seld, fr) | 
|  | if want == 'prop' and form == 'prop' and baetypes.IsType(seld): | 
|  | return baetypes.Property(seld.type, fr) | 
|  | return baetypes.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(), | 
|  | baetypes.Enum('enum'), | 
|  | baetypes.Type('type'), | 
|  | baetypes.Keyword('kwrd'), | 
|  | baetypes.Range(1, 10), | 
|  | baetypes.Comparison(1, '<   ', 10), | 
|  | baetypes.Logical('not ', 1), | 
|  | # Cannot do StyledText | 
|  | # Cannot do AEText | 
|  | baetypes.IntlText(0, 0, 'international text'), | 
|  | baetypes.IntlWritingCode(0,0), | 
|  | baetypes.QDPoint(50,100), | 
|  | baetypes.QDRectangle(50,100,150,200), | 
|  | baetypes.RGBColor(0x7000, 0x6000, 0x5000), | 
|  | baetypes.Unknown('xxxx', 'unknown type data'), | 
|  | baetypes.Character(1), | 
|  | baetypes.Character(2, baetypes.Line(2)), | 
|  | ] | 
|  | for o in objs: | 
|  | print 'BEFORE', o, `o` | 
|  | print type(o) | 
|  | packed = pack(o) | 
|  | unpacked = unpack(packed) | 
|  | print 'AFTER ', unpacked, `unpacked` | 
|  | import sys | 
|  | sys.exit(1) | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | _test() | 
|  |  |