diff options
Diffstat (limited to 'Mac/Contrib/PythonScript/baepack.py')
-rw-r--r-- | Mac/Contrib/PythonScript/baepack.py | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/Mac/Contrib/PythonScript/baepack.py b/Mac/Contrib/PythonScript/baepack.py new file mode 100644 index 0000000..a0bbe50 --- /dev/null +++ b/Mac/Contrib/PythonScript/baepack.py @@ -0,0 +1,385 @@ +"""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() + |