The 0.5 release happened on 2/15, not on 2/14. :-)
[python/dscho.git] / Mac / Contrib / PythonScript / baepack.py
bloba0bbe5056bea7c4e9c1ecbd222592962b6c2aefd
1 """Tools for use in AppleEvent clients and servers:
2 conversion between AE types and python types
4 pack(x) converts a Python object to an AEDesc object
5 unpack(desc) does the reverse
6 coerce(x, wanted_sample) coerces a python object to another python object
7 """
10 # This code was originally written by Guido, and modified/extended by Jack
11 # to include the various types that were missing. The reference used is
12 # Apple Event Registry, chapter 9.
15 import struct
16 import string
17 import types
18 from string import strip
19 from types import *
20 import AE
21 from AppleEvents import *
22 from AERegistry import *
23 from AEObjects import *
24 import MacOS
25 import macfs
26 import StringIO
27 import baetypes
28 from baetypes import mkenum, mktype
30 import calldll
32 OSL = calldll.getlibrary('ObjectSupportLib')
34 # These ones seem to be missing from AppleEvents
35 # (they're in AERegistry.h)
37 #typeColorTable = 'clrt'
38 #typeDrawingArea = 'cdrw'
39 #typePixelMap = 'cpix'
40 #typePixelMapMinus = 'tpmm'
41 #typeRotation = 'trot'
42 #typeTextStyles = 'tsty'
43 #typeStyledText = 'STXT'
44 #typeAEText = 'tTXT'
45 #typeEnumeration = 'enum'
48 # Some AE types are immedeately coerced into something
49 # we like better (and which is equivalent)
51 unpacker_coercions = {
52 typeComp : typeExtended,
53 typeColorTable : typeAEList,
54 typeDrawingArea : typeAERecord,
55 typeFixed : typeExtended,
56 typeFloat : typeExtended,
57 typePixelMap : typeAERecord,
58 typeRotation : typeAERecord,
59 typeStyledText : typeAERecord,
60 typeTextStyles : typeAERecord,
64 # Some python types we need in the packer:
66 AEDescType = type(AE.AECreateDesc('TEXT', ''))
67 _sample_fss = macfs.FSSpec(':')
68 _sample_alias = _sample_fss.NewAliasMinimal()
69 FSSType = type(_sample_fss)
70 AliasType = type(_sample_alias)
72 def pack(x, forcetype = None):
73 """Pack a python object into an AE descriptor"""
74 # print 'aepack', x, type(x), forcetype
75 # if type(x) == TupleType:
76 # forcetype, x = x
77 if forcetype:
78 print x, forcetype
79 if type(x) is StringType:
80 return AE.AECreateDesc(forcetype, x)
81 else:
82 return pack(x).AECoerceDesc(forcetype)
84 if x == None:
85 return AE.AECreateDesc('null', '')
87 t = type(x)
88 if t == AEDescType:
89 return x
90 if t == FSSType:
91 return AE.AECreateDesc('fss ', x.data)
92 if t == AliasType:
93 return AE.AECreateDesc('alis', x.data)
94 if t == IntType:
95 return AE.AECreateDesc('long', struct.pack('l', x))
96 if t == FloatType:
98 # XXXX (note by Guido) Weird thing -- Think C's "double" is 10 bytes, but
99 # struct.pack('d') return 12 bytes (and struct.unpack requires
100 # them, too). The first 2 bytes seem to be repeated...
101 # Probably an alignment problem
102 # XXXX (note by Jack) haven't checked this under MW
104 # return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
105 return AE.AECreateDesc('exte', struct.pack('d', x))
106 if t == StringType:
107 return AE.AECreateDesc('TEXT', x)
108 if t == ListType:
109 list = AE.AECreateList('', 0)
110 for item in x:
111 list.AEPutDesc(0, pack(item))
112 return list
113 if t == DictionaryType:
114 record = AE.AECreateList('', 1)
115 for key, value in x.items():
116 record.AEPutParamDesc(key, pack(value))
117 return record
118 if t == InstanceType and hasattr(x, '__aepack__'):
119 return x.__aepack__()
120 return AE.AECreateDesc('TEXT', repr(x)) # Copout
122 def unpack(desc):
123 """Unpack an AE descriptor to a python object"""
124 t = desc.type
125 # print t
127 if unpacker_coercions.has_key(t):
128 desc = desc.AECoerceDesc(unpacker_coercions[t])
129 t = desc.type # This is a guess by Jack....
131 if t == typeAEList:
132 l = []
133 for i in range(desc.AECountItems()):
134 keyword, item = desc.AEGetNthDesc(i+1, '****')
135 l.append(unpack(item))
136 return l
137 if t == typeAERecord:
138 d = {}
139 for i in range(desc.AECountItems()):
140 keyword, item = desc.AEGetNthDesc(i+1, '****')
141 d[keyword] = unpack(item)
142 return d
143 if t == typeAEText:
144 record = desc.AECoerceDesc('reco')
145 return mkaetext(unpack(record))
146 if t == typeAlias:
147 return macfs.RawAlias(desc.data)
148 # typeAppleEvent returned as unknown
149 if t == typeBoolean:
150 return struct.unpack('b', desc.data)[0]
151 if t == typeChar:
152 return desc.data
153 # typeColorTable coerced to typeAEList
154 # typeComp coerced to extended
155 # typeData returned as unknown
156 # typeDrawingArea coerced to typeAERecord
157 if t == typeEnumeration:
158 return mkenum(desc.data)
159 # typeEPS returned as unknown
160 if t == typeExtended:
161 # print desc, type(desc), len(desc)
162 data = desc.data
163 # print `data[:8]`, type(data), len(data[:8])
164 # print struct.unpack('=d', data[:8])[0]
165 # print string.atoi(data), type(data), len(data)
166 # print struct.calcsize(data)
167 # XXX See corresponding note for pack()
168 # return struct.unpack('d', data[:2] + data)[0]
169 return struct.unpack('d', data[:8])[0]
170 if t == typeFalse:
171 return 0
172 # typeFixed coerced to extended
173 # typeFloat coerced to extended
174 if t == typeFSS:
175 return macfs.RawFSSpec(desc.data)
176 if t == typeInsertionLoc:
177 record = desc.AECoerceDesc('reco')
178 return mkinsertionloc(unpack(record))
179 # typeInteger equal to typeLongInteger
180 if t == typeIntlText:
181 script, language = struct.unpack('hh', desc.data[:4])
182 return baetypes.IntlText(script, language, desc.data[4:])
183 if t == typeIntlWritingCode:
184 script, language = struct.unpack('hh', desc.data)
185 return baetypes.IntlWritingCode(script, language)
186 if t == typeKeyword:
187 return mkkeyword(desc.data)
188 # typeLongFloat is equal to typeFloat
189 if t == typeLongInteger:
190 # print t, struct.unpack('l', desc.data)
191 return struct.unpack('l', desc.data)[0]
192 if t == typeNull:
193 return None
194 if t == typeMagnitude:
195 v = struct.unpack('l', desc.data)
196 if v < 0:
197 v = 0x100000000L + v
198 return v
199 if t == typeObjectSpecifier:
200 import Res
201 # print desc, type(desc)
202 # print desc.__members__
203 # print desc.data, desc.type
204 # print unpack(desc)
205 # getOSL = calldll.newcall(OSL.AEResolve, 'OSErr', 'InHandle', 'InShort')#, 'InString')
206 # print 'OSL', getOSL(rdesc, 0)#, desc.data)
207 record = desc.AECoerceDesc('reco')
208 # print record
209 return mkobject(unpack(record))
210 # typePict returned as unknown
211 # typePixelMap coerced to typeAERecord
212 # typePixelMapMinus returned as unknown
213 # typeProcessSerialNumber returned as unknown
214 if t == typeQDPoint:
215 v, h = struct.unpack('hh', desc.data)
216 return baetypes.QDPoint(v, h)
217 if t == typeQDRectangle:
218 v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
219 return baetypes.QDRectangle(v0, h0, v1, h1)
220 if t == typeRGBColor:
221 r, g, b = struct.unpack('hhh', desc.data)
222 return baetypes.RGBColor(r, g, b)
223 # typeRotation coerced to typeAERecord
224 # typeScrapStyles returned as unknown
225 # typeSessionID returned as unknown
226 if t == typeShortFloat:
227 return struct.unpack('f', desc.data)[0]
228 if t == typeShortInteger:
229 # print t, desc.data
230 # print struct.unpack('h', desc.data)[0]
231 return struct.unpack('h', desc.data)[0]
232 # typeSMFloat identical to typeShortFloat
233 # typeSMInt indetical to typeShortInt
234 # typeStyledText coerced to typeAERecord
235 if t == typeTargetID:
236 return mktargetid(desc.data)
237 # typeTextStyles coerced to typeAERecord
238 # typeTIFF returned as unknown
239 if t == typeTrue:
240 return 1
241 if t == typeType:
242 # print t, desc.data
243 return mktype(desc.data)
245 # The following are special
247 if t == 'rang':
248 record = desc.AECoerceDesc('reco')
249 return mkrange(unpack(record))
250 if t == 'cmpd':
251 record = desc.AECoerceDesc('reco')
252 return mkcomparison(unpack(record))
253 if t == 'logi':
254 record = desc.AECoerceDesc('reco')
255 return mklogical(unpack(record))
256 return mkunknown(desc.type, desc.data)
258 def coerce(data, egdata):
259 """Coerce a python object to another type using the AE coercers"""
260 pdata = pack(data)
261 pegdata = pack(egdata)
262 pdata = pdata.AECoerceDesc(pegdata.type)
263 return unpack(pdata)
266 # Helper routines for unpack
268 def mktargetid(data):
269 sessionID = getlong(data[:4])
270 name = mkppcportrec(data[4:4+72])
271 location = mklocationnamerec(data[76:76+36])
272 rcvrName = mkppcportrec(data[112:112+72])
273 return sessionID, name, location, rcvrName
275 def mkppcportrec(rec):
276 namescript = getword(rec[:2])
277 name = getpstr(rec[2:2+33])
278 portkind = getword(rec[36:38])
279 if portkind == 1:
280 ctor = rec[38:42]
281 type = rec[42:46]
282 identity = (ctor, type)
283 else:
284 identity = getpstr(rec[38:38+33])
285 return namescript, name, portkind, identity
287 def mklocationnamerec(rec):
288 kind = getword(rec[:2])
289 stuff = rec[2:]
290 if kind == 0: stuff = None
291 if kind == 2: stuff = getpstr(stuff)
292 return kind, stuff
294 def mkunknown(type, data):
295 return baetypes.Unknown(type, data)
297 def getpstr(s):
298 return s[1:1+ord(s[0])]
300 def getlong(s):
301 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
303 def getword(s):
304 return (ord(s[0])<<8) | (ord(s[1])<<0)
306 def mkkeyword(keyword):
307 return baetypes.Keyword(keyword)
309 def mkrange(dict):
310 return baetypes.Range(dict['star'], dict['stop'])
312 def mkcomparison(dict):
313 return baetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
315 def mklogical(dict):
316 return baetypes.Logical(dict['logc'], dict['term'])
318 def mkstyledtext(dict):
319 return baetypes.StyledText(dict['ksty'], dict['ktxt'])
321 def mkaetext(dict):
322 return baetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
324 def mkinsertionloc(dict):
325 return baetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])
327 def mkobject(dict):
328 want = dict['want'].type
329 form = dict['form'].enum
330 seld = dict['seld']
331 fr = dict['from']
332 if form in ('name', 'indx', 'rang', 'test'):
333 if want == 'text': return baetypes.Text(seld, fr)
334 if want == 'cha ': return baetypes.Character(seld, fr)
335 if want == 'cwor': return baetypes.Word(seld, fr)
336 if want == 'clin': return baetypes.Line(seld, fr)
337 if want == 'cpar': return baetypes.Paragraph(seld, fr)
338 if want == 'cwin': return baetypes.Window(seld, fr)
339 if want == 'docu': return baetypes.Document(seld, fr)
340 if want == 'file': return baetypes.File(seld, fr)
341 if want == 'cins': return baetypes.InsertionPoint(seld, fr)
342 if want == 'prop' and form == 'prop' and baetypes.IsType(seld):
343 return baetypes.Property(seld.type, fr)
344 return baetypes.ObjectSpecifier(want, form, seld, fr)
346 def _test():
347 """Test program. Pack and unpack various things"""
348 objs = [
349 'a string',
351 12.0,
352 None,
353 ['a', 'list', 'of', 'strings'],
354 {'key1': 'value1', 'key2':'value2'},
355 macfs.FSSpec(':'),
356 macfs.FSSpec(':').NewAliasMinimal(),
357 baetypes.Enum('enum'),
358 baetypes.Type('type'),
359 baetypes.Keyword('kwrd'),
360 baetypes.Range(1, 10),
361 baetypes.Comparison(1, '< ', 10),
362 baetypes.Logical('not ', 1),
363 # Cannot do StyledText
364 # Cannot do AEText
365 baetypes.IntlText(0, 0, 'international text'),
366 baetypes.IntlWritingCode(0,0),
367 baetypes.QDPoint(50,100),
368 baetypes.QDRectangle(50,100,150,200),
369 baetypes.RGBColor(0x7000, 0x6000, 0x5000),
370 baetypes.Unknown('xxxx', 'unknown type data'),
371 baetypes.Character(1),
372 baetypes.Character(2, baetypes.Line(2)),
374 for o in objs:
375 print 'BEFORE', o, `o`
376 print type(o)
377 packed = pack(o)
378 unpacked = unpack(packed)
379 print 'AFTER ', unpacked, `unpacked`
380 import sys
381 sys.exit(1)
383 if __name__ == '__main__':
384 _test()