| """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 * |
| from Carbon import AE |
| from Carbon.AppleEvents 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 : typeFloat, |
| typeColorTable : typeAEList, |
| typeDrawingArea : typeAERecord, |
| typeFixed : typeFloat, |
| typeExtended : typeFloat, |
| typePixelMap : typeAERecord, |
| typeRotation : typeAERecord, |
| typeStyledText : typeAERecord, |
| typeTextStyles : typeAERecord, |
| }; |
| |
| # |
| # Some python types we need in the packer: |
| # |
| AEDescType = AE.AEDescType |
| FSSType = macfs.FSSpecType |
| AliasType = macfs.AliasType |
| |
| 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: |
| return AE.AECreateDesc('doub', struct.pack('d', x)) |
| if t == StringType: |
| return AE.AECreateDesc('TEXT', x) |
| if t == UnicodeType: |
| data = t.encode('utf16') |
| if data[:2] == '\xfe\xff': |
| data = data[2:] |
| return AE.AECreateDesc('utxt', data) |
| 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]) |
| 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 |
| if t == typeUnicodeText: |
| return unicode(desc.data, 'utf16') |
| # 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 == typeFalse: |
| return 0 |
| if t == typeFloat: |
| data = desc.data |
| return struct.unpack('d', data)[0] |
| 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) |
| if t == typeLongInteger: |
| return struct.unpack('l', desc.data)[0] |
| if t == typeLongDateTime: |
| a, b = struct.unpack('lL', desc.data) |
| return (long(a) << 32) + b |
| 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() |
| |