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
= 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
= t
.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 hasattr(x
, '__aepack__'):
118 return x
.__aepack
__()
119 if hasattr(x
, 'which'):
120 return AE
.AECreateDesc('TEXT', x
.which
)
121 if hasattr(x
, 'want'):
122 return AE
.AECreateDesc('TEXT', x
.want
)
123 return AE
.AECreateDesc('TEXT', repr(x
)) # Copout
125 def unpack(desc
, formodulename
=""):
126 """Unpack an AE descriptor to a python object"""
129 if unpacker_coercions
.has_key(t
):
130 desc
= desc
.AECoerceDesc(unpacker_coercions
[t
])
131 t
= desc
.type # This is a guess by Jack....
135 for i
in range(desc
.AECountItems()):
136 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
137 l
.append(unpack(item
, formodulename
))
139 if t
== typeAERecord
:
141 for i
in range(desc
.AECountItems()):
142 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
143 d
[keyword
] = unpack(item
, formodulename
)
146 record
= desc
.AECoerceDesc('reco')
147 return mkaetext(unpack(record
, formodulename
))
149 return Carbon
.File
.Alias(rawdata
=desc
.data
)
150 # typeAppleEvent returned as unknown
152 return struct
.unpack('b', desc
.data
)[0]
155 if t
== typeUnicodeText
:
156 return unicode(desc
.data
, 'utf16')
157 # typeColorTable coerced to typeAEList
158 # typeComp coerced to extended
159 # typeData returned as unknown
160 # typeDrawingArea coerced to typeAERecord
161 if t
== typeEnumeration
:
162 return mkenum(desc
.data
)
163 # typeEPS returned as unknown
168 return struct
.unpack('d', data
)[0]
170 return Carbon
.File
.FSSpec(rawdata
=desc
.data
)
172 return Carbon
.File
.FSRef(rawdata
=desc
.data
)
173 if t
== typeInsertionLoc
:
174 record
= desc
.AECoerceDesc('reco')
175 return mkinsertionloc(unpack(record
, formodulename
))
176 # typeInteger equal to typeLongInteger
177 if t
== typeIntlText
:
178 script
, language
= struct
.unpack('hh', desc
.data
[:4])
179 return aetypes
.IntlText(script
, language
, desc
.data
[4:])
180 if t
== typeIntlWritingCode
:
181 script
, language
= struct
.unpack('hh', desc
.data
)
182 return aetypes
.IntlWritingCode(script
, language
)
184 return mkkeyword(desc
.data
)
185 if t
== typeLongInteger
:
186 return struct
.unpack('l', desc
.data
)[0]
187 if t
== typeLongDateTime
:
188 a
, b
= struct
.unpack('lL', desc
.data
)
189 return (long(a
) << 32) + b
192 if t
== typeMagnitude
:
193 v
= struct
.unpack('l', desc
.data
)
197 if t
== typeObjectSpecifier
:
198 record
= desc
.AECoerceDesc('reco')
199 # If we have been told the name of the module we are unpacking aedescs for,
200 # we can attempt to create the right type of python object from that module.
202 return mkobjectfrommodule(unpack(record
, formodulename
), formodulename
)
203 return mkobject(unpack(record
, formodulename
))
204 # typePict returned as unknown
205 # typePixelMap coerced to typeAERecord
206 # typePixelMapMinus returned as unknown
207 # typeProcessSerialNumber returned as unknown
209 v
, h
= struct
.unpack('hh', desc
.data
)
210 return aetypes
.QDPoint(v
, h
)
211 if t
== typeQDRectangle
:
212 v0
, h0
, v1
, h1
= struct
.unpack('hhhh', desc
.data
)
213 return aetypes
.QDRectangle(v0
, h0
, v1
, h1
)
214 if t
== typeRGBColor
:
215 r
, g
, b
= struct
.unpack('hhh', desc
.data
)
216 return aetypes
.RGBColor(r
, g
, b
)
217 # typeRotation coerced to typeAERecord
218 # typeScrapStyles returned as unknown
219 # typeSessionID returned as unknown
220 if t
== typeShortFloat
:
221 return struct
.unpack('f', desc
.data
)[0]
222 if t
== typeShortInteger
:
223 return struct
.unpack('h', desc
.data
)[0]
224 # typeSMFloat identical to typeShortFloat
225 # typeSMInt indetical to typeShortInt
226 # typeStyledText coerced to typeAERecord
227 if t
== typeTargetID
:
228 return mktargetid(desc
.data
)
229 # typeTextStyles coerced to typeAERecord
230 # typeTIFF returned as unknown
234 return mktype(desc
.data
)
236 # The following are special
239 record
= desc
.AECoerceDesc('reco')
240 return mkrange(unpack(record
, formodulename
))
242 record
= desc
.AECoerceDesc('reco')
243 return mkcomparison(unpack(record
, formodulename
))
245 record
= desc
.AECoerceDesc('reco')
246 return mklogical(unpack(record
, formodulename
))
247 return mkunknown(desc
.type, desc
.data
)
249 def coerce(data
, egdata
):
250 """Coerce a python object to another type using the AE coercers"""
252 pegdata
= pack(egdata
)
253 pdata
= pdata
.AECoerceDesc(pegdata
.type)
257 # Helper routines for unpack
259 def mktargetid(data
):
260 sessionID
= getlong(data
[:4])
261 name
= mkppcportrec(data
[4:4+72])
262 location
= mklocationnamerec(data
[76:76+36])
263 rcvrName
= mkppcportrec(data
[112:112+72])
264 return sessionID
, name
, location
, rcvrName
266 def mkppcportrec(rec
):
267 namescript
= getword(rec
[:2])
268 name
= getpstr(rec
[2:2+33])
269 portkind
= getword(rec
[36:38])
273 identity
= (ctor
, type)
275 identity
= getpstr(rec
[38:38+33])
276 return namescript
, name
, portkind
, identity
278 def mklocationnamerec(rec
):
279 kind
= getword(rec
[:2])
281 if kind
== 0: stuff
= None
282 if kind
== 2: stuff
= getpstr(stuff
)
285 def mkunknown(type, data
):
286 return aetypes
.Unknown(type, data
)
289 return s
[1:1+ord(s
[0])]
292 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
295 return (ord(s
[0])<<8) |
(ord(s
[1])<<0)
297 def mkkeyword(keyword
):
298 return aetypes
.Keyword(keyword
)
301 return aetypes
.Range(dict['star'], dict['stop'])
303 def mkcomparison(dict):
304 return aetypes
.Comparison(dict['obj1'], dict['relo'].enum
, dict['obj2'])
307 return aetypes
.Logical(dict['logc'], dict['term'])
309 def mkstyledtext(dict):
310 return aetypes
.StyledText(dict['ksty'], dict['ktxt'])
313 return aetypes
.AEText(dict[keyAEScriptTag
], dict[keyAEStyles
], dict[keyAEText
])
315 def mkinsertionloc(dict):
316 return aetypes
.InsertionLoc(dict[keyAEObject
], dict[keyAEPosition
])
319 want
= dict['want'].type
320 form
= dict['form'].enum
323 if form
in ('name', 'indx', 'rang', 'test'):
324 if want
== 'text': return aetypes
.Text(seld
, fr
)
325 if want
== 'cha ': return aetypes
.Character(seld
, fr
)
326 if want
== 'cwor': return aetypes
.Word(seld
, fr
)
327 if want
== 'clin': return aetypes
.Line(seld
, fr
)
328 if want
== 'cpar': return aetypes
.Paragraph(seld
, fr
)
329 if want
== 'cwin': return aetypes
.Window(seld
, fr
)
330 if want
== 'docu': return aetypes
.Document(seld
, fr
)
331 if want
== 'file': return aetypes
.File(seld
, fr
)
332 if want
== 'cins': return aetypes
.InsertionPoint(seld
, fr
)
333 if want
== 'prop' and form
== 'prop' and aetypes
.IsType(seld
):
334 return aetypes
.Property(seld
.type, fr
)
335 return aetypes
.ObjectSpecifier(want
, form
, seld
, fr
)
337 # Note by Jack: I'm not 100% sure of the following code. This was
338 # provided by Donovan Preston, but I wonder whether the assignment
339 # to __class__ is safe. Moreover, shouldn't there be a better
340 # initializer for the classes in the suites?
341 def mkobjectfrommodule(dict, modulename
):
342 want
= dict['want'].type
343 module
= __import__(modulename
)
344 codenamemapper
= module
._classdeclarations
345 classtype
= codenamemapper
.get(want
, None)
346 newobj
= mkobject(dict)
348 newobj
.__class
__ = classtype