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
, ObjectSpecifier
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
= Carbon
.File
.FSSpecType
63 FSRefType
= Carbon
.File
.FSRefType
64 AliasType
= Carbon
.File
.AliasType
66 def packkey(ae
, key
, value
):
67 if hasattr(key
, 'which'):
69 elif hasattr(key
, 'want'):
73 ae
.AEPutParamDesc(keystr
, pack(value
))
75 def pack(x
, forcetype
= None):
76 """Pack a python object into an AE descriptor"""
79 if type(x
) is StringType
:
80 return AE
.AECreateDesc(forcetype
, x
)
82 return pack(x
).AECoerceDesc(forcetype
)
85 return AE
.AECreateDesc('null', '')
87 if isinstance(x
, AEDescType
):
89 if isinstance(x
, FSSType
):
90 return AE
.AECreateDesc('fss ', x
.data
)
91 if isinstance(x
, FSRefType
):
92 return AE
.AECreateDesc('fsrf', x
.data
)
93 if isinstance(x
, AliasType
):
94 return AE
.AECreateDesc('alis', x
.data
)
95 if isinstance(x
, IntType
):
96 return AE
.AECreateDesc('long', struct
.pack('l', x
))
97 if isinstance(x
, FloatType
):
98 return AE
.AECreateDesc('doub', struct
.pack('d', x
))
99 if isinstance(x
, StringType
):
100 return AE
.AECreateDesc('TEXT', x
)
101 if isinstance(x
, UnicodeType
):
102 data
= x
.encode('utf16')
103 if data
[:2] == '\xfe\xff':
105 return AE
.AECreateDesc('utxt', data
)
106 if isinstance(x
, ListType
):
107 list = AE
.AECreateList('', 0)
109 list.AEPutDesc(0, pack(item
))
111 if isinstance(x
, DictionaryType
):
112 record
= AE
.AECreateList('', 1)
113 for key
, value
in x
.items():
114 packkey(record
, key
, value
)
115 #record.AEPutParamDesc(key, pack(value))
117 if type(x
) == types
.ClassType
and issubclass(x
, ObjectSpecifier
):
118 # Note: we are getting a class object here, not an instance
119 return AE
.AECreateDesc('type', x
.want
)
120 if hasattr(x
, '__aepack__'):
121 return x
.__aepack
__()
122 if hasattr(x
, 'which'):
123 return AE
.AECreateDesc('TEXT', x
.which
)
124 if hasattr(x
, 'want'):
125 return AE
.AECreateDesc('TEXT', x
.want
)
126 return AE
.AECreateDesc('TEXT', repr(x
)) # Copout
128 def unpack(desc
, formodulename
=""):
129 """Unpack an AE descriptor to a python object"""
132 if unpacker_coercions
.has_key(t
):
133 desc
= desc
.AECoerceDesc(unpacker_coercions
[t
])
134 t
= desc
.type # This is a guess by Jack....
138 for i
in range(desc
.AECountItems()):
139 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
140 l
.append(unpack(item
, formodulename
))
142 if t
== typeAERecord
:
144 for i
in range(desc
.AECountItems()):
145 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
146 d
[keyword
] = unpack(item
, formodulename
)
149 record
= desc
.AECoerceDesc('reco')
150 return mkaetext(unpack(record
, formodulename
))
152 return Carbon
.File
.Alias(rawdata
=desc
.data
)
153 # typeAppleEvent returned as unknown
155 return struct
.unpack('b', desc
.data
)[0]
158 if t
== typeUnicodeText
:
159 return unicode(desc
.data
, 'utf16')
160 # typeColorTable coerced to typeAEList
161 # typeComp coerced to extended
162 # typeData returned as unknown
163 # typeDrawingArea coerced to typeAERecord
164 if t
== typeEnumeration
:
165 return mkenum(desc
.data
)
166 # typeEPS returned as unknown
171 return struct
.unpack('d', data
)[0]
173 return Carbon
.File
.FSSpec(rawdata
=desc
.data
)
175 return Carbon
.File
.FSRef(rawdata
=desc
.data
)
176 if t
== typeInsertionLoc
:
177 record
= desc
.AECoerceDesc('reco')
178 return mkinsertionloc(unpack(record
, formodulename
))
179 # typeInteger equal to typeLongInteger
180 if t
== typeIntlText
:
181 script
, language
= struct
.unpack('hh', desc
.data
[:4])
182 return aetypes
.IntlText(script
, language
, desc
.data
[4:])
183 if t
== typeIntlWritingCode
:
184 script
, language
= struct
.unpack('hh', desc
.data
)
185 return aetypes
.IntlWritingCode(script
, language
)
187 return mkkeyword(desc
.data
)
188 if t
== typeLongInteger
:
189 return struct
.unpack('l', desc
.data
)[0]
190 if t
== typeLongDateTime
:
191 a
, b
= struct
.unpack('lL', desc
.data
)
192 return (long(a
) << 32) + b
195 if t
== typeMagnitude
:
196 v
= struct
.unpack('l', desc
.data
)
200 if t
== typeObjectSpecifier
:
201 record
= desc
.AECoerceDesc('reco')
202 # If we have been told the name of the module we are unpacking aedescs for,
203 # we can attempt to create the right type of python object from that module.
205 return mkobjectfrommodule(unpack(record
, formodulename
), formodulename
)
206 return mkobject(unpack(record
, formodulename
))
207 # typePict returned as unknown
208 # typePixelMap coerced to typeAERecord
209 # typePixelMapMinus returned as unknown
210 # typeProcessSerialNumber returned as unknown
212 v
, h
= struct
.unpack('hh', desc
.data
)
213 return aetypes
.QDPoint(v
, h
)
214 if t
== typeQDRectangle
:
215 v0
, h0
, v1
, h1
= struct
.unpack('hhhh', desc
.data
)
216 return aetypes
.QDRectangle(v0
, h0
, v1
, h1
)
217 if t
== typeRGBColor
:
218 r
, g
, b
= struct
.unpack('hhh', desc
.data
)
219 return aetypes
.RGBColor(r
, g
, b
)
220 # typeRotation coerced to typeAERecord
221 # typeScrapStyles returned as unknown
222 # typeSessionID returned as unknown
223 if t
== typeShortFloat
:
224 return struct
.unpack('f', desc
.data
)[0]
225 if t
== typeShortInteger
:
226 return struct
.unpack('h', desc
.data
)[0]
227 # typeSMFloat identical to typeShortFloat
228 # typeSMInt indetical to typeShortInt
229 # typeStyledText coerced to typeAERecord
230 if t
== typeTargetID
:
231 return mktargetid(desc
.data
)
232 # typeTextStyles coerced to typeAERecord
233 # typeTIFF returned as unknown
237 return mktype(desc
.data
, formodulename
)
239 # The following are special
242 record
= desc
.AECoerceDesc('reco')
243 return mkrange(unpack(record
, formodulename
))
245 record
= desc
.AECoerceDesc('reco')
246 return mkcomparison(unpack(record
, formodulename
))
248 record
= desc
.AECoerceDesc('reco')
249 return mklogical(unpack(record
, formodulename
))
250 return mkunknown(desc
.type, desc
.data
)
252 def coerce(data
, egdata
):
253 """Coerce a python object to another type using the AE coercers"""
255 pegdata
= pack(egdata
)
256 pdata
= pdata
.AECoerceDesc(pegdata
.type)
260 # Helper routines for unpack
262 def mktargetid(data
):
263 sessionID
= getlong(data
[:4])
264 name
= mkppcportrec(data
[4:4+72])
265 location
= mklocationnamerec(data
[76:76+36])
266 rcvrName
= mkppcportrec(data
[112:112+72])
267 return sessionID
, name
, location
, rcvrName
269 def mkppcportrec(rec
):
270 namescript
= getword(rec
[:2])
271 name
= getpstr(rec
[2:2+33])
272 portkind
= getword(rec
[36:38])
276 identity
= (ctor
, type)
278 identity
= getpstr(rec
[38:38+33])
279 return namescript
, name
, portkind
, identity
281 def mklocationnamerec(rec
):
282 kind
= getword(rec
[:2])
284 if kind
== 0: stuff
= None
285 if kind
== 2: stuff
= getpstr(stuff
)
288 def mkunknown(type, data
):
289 return aetypes
.Unknown(type, data
)
292 return s
[1:1+ord(s
[0])]
295 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
298 return (ord(s
[0])<<8) |
(ord(s
[1])<<0)
300 def mkkeyword(keyword
):
301 return aetypes
.Keyword(keyword
)
304 return aetypes
.Range(dict['star'], dict['stop'])
306 def mkcomparison(dict):
307 return aetypes
.Comparison(dict['obj1'], dict['relo'].enum
, dict['obj2'])
310 return aetypes
.Logical(dict['logc'], dict['term'])
312 def mkstyledtext(dict):
313 return aetypes
.StyledText(dict['ksty'], dict['ktxt'])
316 return aetypes
.AEText(dict[keyAEScriptTag
], dict[keyAEStyles
], dict[keyAEText
])
318 def mkinsertionloc(dict):
319 return aetypes
.InsertionLoc(dict[keyAEObject
], dict[keyAEPosition
])
322 want
= dict['want'].type
323 form
= dict['form'].enum
326 if form
in ('name', 'indx', 'rang', 'test'):
327 if want
== 'text': return aetypes
.Text(seld
, fr
)
328 if want
== 'cha ': return aetypes
.Character(seld
, fr
)
329 if want
== 'cwor': return aetypes
.Word(seld
, fr
)
330 if want
== 'clin': return aetypes
.Line(seld
, fr
)
331 if want
== 'cpar': return aetypes
.Paragraph(seld
, fr
)
332 if want
== 'cwin': return aetypes
.Window(seld
, fr
)
333 if want
== 'docu': return aetypes
.Document(seld
, fr
)
334 if want
== 'file': return aetypes
.File(seld
, fr
)
335 if want
== 'cins': return aetypes
.InsertionPoint(seld
, fr
)
336 if want
== 'prop' and form
== 'prop' and aetypes
.IsType(seld
):
337 return aetypes
.Property(seld
.type, fr
)
338 return aetypes
.ObjectSpecifier(want
, form
, seld
, fr
)
340 # Note by Jack: I'm not 100% sure of the following code. This was
341 # provided by Donovan Preston, but I wonder whether the assignment
342 # to __class__ is safe. Moreover, shouldn't there be a better
343 # initializer for the classes in the suites?
344 def mkobjectfrommodule(dict, modulename
):
345 if type(dict['want']) == types
.ClassType
and issubclass(dict['want'], ObjectSpecifier
):
346 # The type has already been converted to Python. Convert back:-(
347 classtype
= dict['want']
348 dict['want'] = aetypes
.mktype(classtype
.want
)
349 want
= dict['want'].type
350 module
= __import__(modulename
)
351 codenamemapper
= module
._classdeclarations
352 classtype
= codenamemapper
.get(want
, None)
353 newobj
= mkobject(dict)
355 assert issubclass(classtype
, ObjectSpecifier
)
356 newobj
.__class
__ = classtype
359 def mktype(typecode
, modulename
=None):
361 module
= __import__(modulename
)
362 codenamemapper
= module
._classdeclarations
363 classtype
= codenamemapper
.get(typecode
, None)
366 return aetypes
.mktype(typecode
)