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
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.
18 from string
import strip
21 from Carbon
.AppleEvents
import *
26 from aetypes
import mkenum
, mktype
29 # These ones seem to be missing from AppleEvents
30 # (they're in AERegistry.h)
32 #typeColorTable = 'clrt'
33 #typeDrawingArea = 'cdrw'
34 #typePixelMap = 'cpix'
35 #typePixelMapMinus = 'tpmm'
36 #typeRotation = 'trot'
37 #typeTextStyles = 'tsty'
38 #typeStyledText = 'STXT'
40 #typeEnumeration = 'enum'
43 # Some AE types are immedeately coerced into something
44 # we like better (and which is equivalent)
46 unpacker_coercions
= {
48 typeColorTable
: typeAEList
,
49 typeDrawingArea
: typeAERecord
,
50 typeFixed
: typeFloat
,
51 typeExtended
: typeFloat
,
52 typePixelMap
: typeAERecord
,
53 typeRotation
: typeAERecord
,
54 typeStyledText
: typeAERecord
,
55 typeTextStyles
: typeAERecord
,
59 # Some python types we need in the packer:
61 AEDescType
= AE
.AEDescType
62 FSSType
= macfs
.FSSpecType
63 AliasType
= macfs
.AliasType
65 def packkey(ae
, key
, value
):
66 if hasattr(key
, 'which'):
68 elif hasattr(key
, 'want'):
72 ae
.AEPutParamDesc(keystr
, pack(value
))
74 def pack(x
, forcetype
= None):
75 """Pack a python object into an AE descriptor"""
78 if type(x
) is StringType
:
79 return AE
.AECreateDesc(forcetype
, x
)
81 return pack(x
).AECoerceDesc(forcetype
)
84 return AE
.AECreateDesc('null', '')
90 return AE
.AECreateDesc('fss ', x
.data
)
92 return AE
.AECreateDesc('alis', x
.data
)
94 return AE
.AECreateDesc('long', struct
.pack('l', x
))
96 return AE
.AECreateDesc('doub', struct
.pack('d', x
))
98 return AE
.AECreateDesc('TEXT', x
)
100 data
= t
.encode('utf16')
101 if data
[:2] == '\xfe\xff':
103 return AE
.AECreateDesc('utxt', data
)
105 list = AE
.AECreateList('', 0)
107 list.AEPutDesc(0, pack(item
))
109 if t
== DictionaryType
:
110 record
= AE
.AECreateList('', 1)
111 for key
, value
in x
.items():
112 packkey(record
, key
, value
)
113 #record.AEPutParamDesc(key, pack(value))
115 if t
== InstanceType
and hasattr(x
, '__aepack__'):
116 return x
.__aepack
__()
117 if hasattr(x
, 'which'):
118 return AE
.AECreateDesc('TEXT', x
.which
)
119 if hasattr(x
, 'want'):
120 return AE
.AECreateDesc('TEXT', x
.want
)
121 return AE
.AECreateDesc('TEXT', repr(x
)) # Copout
123 def unpack(desc
, formodulename
=""):
124 """Unpack an AE descriptor to a python object"""
127 if unpacker_coercions
.has_key(t
):
128 desc
= desc
.AECoerceDesc(unpacker_coercions
[t
])
129 t
= desc
.type # This is a guess by Jack....
133 for i
in range(desc
.AECountItems()):
134 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
135 l
.append(unpack(item
, formodulename
))
137 if t
== typeAERecord
:
139 for i
in range(desc
.AECountItems()):
140 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
141 d
[keyword
] = unpack(item
, formodulename
)
144 record
= desc
.AECoerceDesc('reco')
145 return mkaetext(unpack(record
, formodulename
))
147 return macfs
.RawAlias(desc
.data
)
148 # typeAppleEvent returned as unknown
150 return struct
.unpack('b', desc
.data
)[0]
153 if t
== typeUnicodeText
:
154 return unicode(desc
.data
, 'utf16')
155 # typeColorTable coerced to typeAEList
156 # typeComp coerced to extended
157 # typeData returned as unknown
158 # typeDrawingArea coerced to typeAERecord
159 if t
== typeEnumeration
:
160 return mkenum(desc
.data
)
161 # typeEPS returned as unknown
166 return struct
.unpack('d', data
)[0]
168 return macfs
.RawFSSpec(desc
.data
)
169 if t
== typeInsertionLoc
:
170 record
= desc
.AECoerceDesc('reco')
171 return mkinsertionloc(unpack(record
, formodulename
))
172 # typeInteger equal to typeLongInteger
173 if t
== typeIntlText
:
174 script
, language
= struct
.unpack('hh', desc
.data
[:4])
175 return aetypes
.IntlText(script
, language
, desc
.data
[4:])
176 if t
== typeIntlWritingCode
:
177 script
, language
= struct
.unpack('hh', desc
.data
)
178 return aetypes
.IntlWritingCode(script
, language
)
180 return mkkeyword(desc
.data
)
181 if t
== typeLongInteger
:
182 return struct
.unpack('l', desc
.data
)[0]
183 if t
== typeLongDateTime
:
184 a
, b
= struct
.unpack('lL', desc
.data
)
185 return (long(a
) << 32) + b
188 if t
== typeMagnitude
:
189 v
= struct
.unpack('l', desc
.data
)
193 if t
== typeObjectSpecifier
:
194 record
= desc
.AECoerceDesc('reco')
195 # If we have been told the name of the module we are unpacking aedescs for,
196 # we can attempt to create the right type of python object from that module.
198 return mkobjectfrommodule(unpack(record
, formodulename
), formodulename
)
199 return mkobject(unpack(record
, formodulename
))
200 # typePict returned as unknown
201 # typePixelMap coerced to typeAERecord
202 # typePixelMapMinus returned as unknown
203 # typeProcessSerialNumber returned as unknown
205 v
, h
= struct
.unpack('hh', desc
.data
)
206 return aetypes
.QDPoint(v
, h
)
207 if t
== typeQDRectangle
:
208 v0
, h0
, v1
, h1
= struct
.unpack('hhhh', desc
.data
)
209 return aetypes
.QDRectangle(v0
, h0
, v1
, h1
)
210 if t
== typeRGBColor
:
211 r
, g
, b
= struct
.unpack('hhh', desc
.data
)
212 return aetypes
.RGBColor(r
, g
, b
)
213 # typeRotation coerced to typeAERecord
214 # typeScrapStyles returned as unknown
215 # typeSessionID returned as unknown
216 if t
== typeShortFloat
:
217 return struct
.unpack('f', desc
.data
)[0]
218 if t
== typeShortInteger
:
219 return struct
.unpack('h', desc
.data
)[0]
220 # typeSMFloat identical to typeShortFloat
221 # typeSMInt indetical to typeShortInt
222 # typeStyledText coerced to typeAERecord
223 if t
== typeTargetID
:
224 return mktargetid(desc
.data
)
225 # typeTextStyles coerced to typeAERecord
226 # typeTIFF returned as unknown
230 return mktype(desc
.data
)
232 # The following are special
235 record
= desc
.AECoerceDesc('reco')
236 return mkrange(unpack(record
, formodulename
))
238 record
= desc
.AECoerceDesc('reco')
239 return mkcomparison(unpack(record
, formodulename
))
241 record
= desc
.AECoerceDesc('reco')
242 return mklogical(unpack(record
, formodulename
))
243 return mkunknown(desc
.type, desc
.data
)
245 def coerce(data
, egdata
):
246 """Coerce a python object to another type using the AE coercers"""
248 pegdata
= pack(egdata
)
249 pdata
= pdata
.AECoerceDesc(pegdata
.type)
253 # Helper routines for unpack
255 def mktargetid(data
):
256 sessionID
= getlong(data
[:4])
257 name
= mkppcportrec(data
[4:4+72])
258 location
= mklocationnamerec(data
[76:76+36])
259 rcvrName
= mkppcportrec(data
[112:112+72])
260 return sessionID
, name
, location
, rcvrName
262 def mkppcportrec(rec
):
263 namescript
= getword(rec
[:2])
264 name
= getpstr(rec
[2:2+33])
265 portkind
= getword(rec
[36:38])
269 identity
= (ctor
, type)
271 identity
= getpstr(rec
[38:38+33])
272 return namescript
, name
, portkind
, identity
274 def mklocationnamerec(rec
):
275 kind
= getword(rec
[:2])
277 if kind
== 0: stuff
= None
278 if kind
== 2: stuff
= getpstr(stuff
)
281 def mkunknown(type, data
):
282 return aetypes
.Unknown(type, data
)
285 return s
[1:1+ord(s
[0])]
288 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
291 return (ord(s
[0])<<8) |
(ord(s
[1])<<0)
293 def mkkeyword(keyword
):
294 return aetypes
.Keyword(keyword
)
297 return aetypes
.Range(dict['star'], dict['stop'])
299 def mkcomparison(dict):
300 return aetypes
.Comparison(dict['obj1'], dict['relo'].enum
, dict['obj2'])
303 return aetypes
.Logical(dict['logc'], dict['term'])
305 def mkstyledtext(dict):
306 return aetypes
.StyledText(dict['ksty'], dict['ktxt'])
309 return aetypes
.AEText(dict[keyAEScriptTag
], dict[keyAEStyles
], dict[keyAEText
])
311 def mkinsertionloc(dict):
312 return aetypes
.InsertionLoc(dict[keyAEObject
], dict[keyAEPosition
])
315 want
= dict['want'].type
316 form
= dict['form'].enum
319 if form
in ('name', 'indx', 'rang', 'test'):
320 if want
== 'text': return aetypes
.Text(seld
, fr
)
321 if want
== 'cha ': return aetypes
.Character(seld
, fr
)
322 if want
== 'cwor': return aetypes
.Word(seld
, fr
)
323 if want
== 'clin': return aetypes
.Line(seld
, fr
)
324 if want
== 'cpar': return aetypes
.Paragraph(seld
, fr
)
325 if want
== 'cwin': return aetypes
.Window(seld
, fr
)
326 if want
== 'docu': return aetypes
.Document(seld
, fr
)
327 if want
== 'file': return aetypes
.File(seld
, fr
)
328 if want
== 'cins': return aetypes
.InsertionPoint(seld
, fr
)
329 if want
== 'prop' and form
== 'prop' and aetypes
.IsType(seld
):
330 return aetypes
.Property(seld
.type, fr
)
331 return aetypes
.ObjectSpecifier(want
, form
, seld
, fr
)
333 # Note by Jack: I'm not 100% sure of the following code. This was
334 # provided by Donovan Preston, but I wonder whether the assignment
335 # to __class__ is safe. Moreover, shouldn't there be a better
336 # initializer for the classes in the suites?
337 def mkobjectfrommodule(dict, modulename
):
338 want
= dict['want'].type
339 module
= __import__(modulename
)
340 codenamemapper
= module
._classdeclarations
341 classtype
= codenamemapper
.get(want
, None)
342 newobj
= mkobject(dict)
344 newobj
.__class
__ = classtype
348 """Test program. Pack and unpack various things"""
354 ['a', 'list', 'of', 'strings'],
355 {'key1': 'value1', 'key2':'value2'},
357 macfs
.FSSpec(':').NewAliasMinimal(),
358 aetypes
.Enum('enum'),
359 aetypes
.Type('type'),
360 aetypes
.Keyword('kwrd'),
361 aetypes
.Range(1, 10),
362 aetypes
.Comparison(1, '< ', 10),
363 aetypes
.Logical('not ', 1),
364 # Cannot do StyledText
366 aetypes
.IntlText(0, 0, 'international text'),
367 aetypes
.IntlWritingCode(0,0),
368 aetypes
.QDPoint(50,100),
369 aetypes
.QDRectangle(50,100,150,200),
370 aetypes
.RGBColor(0x7000, 0x6000, 0x5000),
371 aetypes
.Unknown('xxxx', 'unknown type data'),
372 aetypes
.Character(1),
373 aetypes
.Character(2, aetypes
.Line(2)),
376 print 'BEFORE', o
, `o`
378 unpacked
= unpack(packed
)
379 print 'AFTER ', unpacked
, `unpacked`
383 if __name__
== '__main__':