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