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.
15 from warnings
import warnpy3k
16 warnpy3k("In 3.x, the aepack module is removed.", stacklevel
=2)
22 from Carbon
.AppleEvents
import *
26 from aetypes
import mkenum
, ObjectSpecifier
28 # These ones seem to be missing from AppleEvents
29 # (they're in AERegistry.h)
31 #typeColorTable = 'clrt'
32 #typeDrawingArea = 'cdrw'
33 #typePixelMap = 'cpix'
34 #typePixelMapMinus = 'tpmm'
35 #typeRotation = 'trot'
36 #typeTextStyles = 'tsty'
37 #typeStyledText = 'STXT'
39 #typeEnumeration = 'enum'
42 # Some AE types are immedeately coerced into something
43 # we like better (and which is equivalent)
45 unpacker_coercions
= {
47 typeColorTable
: typeAEList
,
48 typeDrawingArea
: typeAERecord
,
49 typeFixed
: typeFloat
,
50 typeExtended
: typeFloat
,
51 typePixelMap
: typeAERecord
,
52 typeRotation
: typeAERecord
,
53 typeStyledText
: typeAERecord
,
54 typeTextStyles
: typeAERecord
,
58 # Some python types we need in the packer:
60 AEDescType
= AE
.AEDescType
61 FSSType
= Carbon
.File
.FSSpecType
62 FSRefType
= Carbon
.File
.FSRefType
63 AliasType
= Carbon
.File
.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', '')
86 if isinstance(x
, AEDescType
):
88 if isinstance(x
, FSSType
):
89 return AE
.AECreateDesc('fss ', x
.data
)
90 if isinstance(x
, FSRefType
):
91 return AE
.AECreateDesc('fsrf', x
.data
)
92 if isinstance(x
, AliasType
):
93 return AE
.AECreateDesc('alis', x
.data
)
94 if isinstance(x
, IntType
):
95 return AE
.AECreateDesc('long', struct
.pack('l', x
))
96 if isinstance(x
, FloatType
):
97 return AE
.AECreateDesc('doub', struct
.pack('d', x
))
98 if isinstance(x
, StringType
):
99 return AE
.AECreateDesc('TEXT', x
)
100 if isinstance(x
, UnicodeType
):
101 data
= x
.encode('utf16')
102 if data
[:2] == '\xfe\xff':
104 return AE
.AECreateDesc('utxt', data
)
105 if isinstance(x
, ListType
):
106 list = AE
.AECreateList('', 0)
108 list.AEPutDesc(0, pack(item
))
110 if isinstance(x
, DictionaryType
):
111 record
= AE
.AECreateList('', 1)
112 for key
, value
in x
.items():
113 packkey(record
, key
, value
)
114 #record.AEPutParamDesc(key, pack(value))
116 if type(x
) == types
.ClassType
and issubclass(x
, ObjectSpecifier
):
117 # Note: we are getting a class object here, not an instance
118 return AE
.AECreateDesc('type', x
.want
)
119 if hasattr(x
, '__aepack__'):
120 return x
.__aepack
__()
121 if hasattr(x
, 'which'):
122 return AE
.AECreateDesc('TEXT', x
.which
)
123 if hasattr(x
, 'want'):
124 return AE
.AECreateDesc('TEXT', x
.want
)
125 return AE
.AECreateDesc('TEXT', repr(x
)) # Copout
127 def unpack(desc
, formodulename
=""):
128 """Unpack an AE descriptor to a python object"""
131 if unpacker_coercions
.has_key(t
):
132 desc
= desc
.AECoerceDesc(unpacker_coercions
[t
])
133 t
= desc
.type # This is a guess by Jack....
137 for i
in range(desc
.AECountItems()):
138 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
139 l
.append(unpack(item
, formodulename
))
141 if t
== typeAERecord
:
143 for i
in range(desc
.AECountItems()):
144 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
145 d
[keyword
] = unpack(item
, formodulename
)
148 record
= desc
.AECoerceDesc('reco')
149 return mkaetext(unpack(record
, formodulename
))
151 return Carbon
.File
.Alias(rawdata
=desc
.data
)
152 # typeAppleEvent returned as unknown
154 return struct
.unpack('b', desc
.data
)[0]
157 if t
== typeUnicodeText
:
158 return unicode(desc
.data
, 'utf16')
159 # typeColorTable coerced to typeAEList
160 # typeComp coerced to extended
161 # typeData returned as unknown
162 # typeDrawingArea coerced to typeAERecord
163 if t
== typeEnumeration
:
164 return mkenum(desc
.data
)
165 # typeEPS returned as unknown
170 return struct
.unpack('d', data
)[0]
172 return Carbon
.File
.FSSpec(rawdata
=desc
.data
)
174 return Carbon
.File
.FSRef(rawdata
=desc
.data
)
175 if t
== typeInsertionLoc
:
176 record
= desc
.AECoerceDesc('reco')
177 return mkinsertionloc(unpack(record
, formodulename
))
178 # typeInteger equal to typeLongInteger
179 if t
== typeIntlText
:
180 script
, language
= struct
.unpack('hh', desc
.data
[:4])
181 return aetypes
.IntlText(script
, language
, desc
.data
[4:])
182 if t
== typeIntlWritingCode
:
183 script
, language
= struct
.unpack('hh', desc
.data
)
184 return aetypes
.IntlWritingCode(script
, language
)
186 return mkkeyword(desc
.data
)
187 if t
== typeLongInteger
:
188 return struct
.unpack('l', desc
.data
)[0]
189 if t
== typeLongDateTime
:
190 a
, b
= struct
.unpack('lL', desc
.data
)
191 return (long(a
) << 32) + b
194 if t
== typeMagnitude
:
195 v
= struct
.unpack('l', desc
.data
)
199 if t
== typeObjectSpecifier
:
200 record
= desc
.AECoerceDesc('reco')
201 # If we have been told the name of the module we are unpacking aedescs for,
202 # we can attempt to create the right type of python object from that module.
204 return mkobjectfrommodule(unpack(record
, formodulename
), formodulename
)
205 return mkobject(unpack(record
, formodulename
))
206 # typePict returned as unknown
207 # typePixelMap coerced to typeAERecord
208 # typePixelMapMinus returned as unknown
209 # typeProcessSerialNumber returned as unknown
211 v
, h
= struct
.unpack('hh', desc
.data
)
212 return aetypes
.QDPoint(v
, h
)
213 if t
== typeQDRectangle
:
214 v0
, h0
, v1
, h1
= struct
.unpack('hhhh', desc
.data
)
215 return aetypes
.QDRectangle(v0
, h0
, v1
, h1
)
216 if t
== typeRGBColor
:
217 r
, g
, b
= struct
.unpack('hhh', desc
.data
)
218 return aetypes
.RGBColor(r
, g
, b
)
219 # typeRotation coerced to typeAERecord
220 # typeScrapStyles returned as unknown
221 # typeSessionID returned as unknown
222 if t
== typeShortFloat
:
223 return struct
.unpack('f', desc
.data
)[0]
224 if t
== typeShortInteger
:
225 return struct
.unpack('h', desc
.data
)[0]
226 # typeSMFloat identical to typeShortFloat
227 # typeSMInt indetical to typeShortInt
228 # typeStyledText coerced to typeAERecord
229 if t
== typeTargetID
:
230 return mktargetid(desc
.data
)
231 # typeTextStyles coerced to typeAERecord
232 # typeTIFF returned as unknown
236 return mktype(desc
.data
, formodulename
)
238 # The following are special
241 record
= desc
.AECoerceDesc('reco')
242 return mkrange(unpack(record
, formodulename
))
244 record
= desc
.AECoerceDesc('reco')
245 return mkcomparison(unpack(record
, formodulename
))
247 record
= desc
.AECoerceDesc('reco')
248 return mklogical(unpack(record
, formodulename
))
249 return mkunknown(desc
.type, desc
.data
)
251 def coerce(data
, egdata
):
252 """Coerce a python object to another type using the AE coercers"""
254 pegdata
= pack(egdata
)
255 pdata
= pdata
.AECoerceDesc(pegdata
.type)
259 # Helper routines for unpack
261 def mktargetid(data
):
262 sessionID
= getlong(data
[:4])
263 name
= mkppcportrec(data
[4:4+72])
264 location
= mklocationnamerec(data
[76:76+36])
265 rcvrName
= mkppcportrec(data
[112:112+72])
266 return sessionID
, name
, location
, rcvrName
268 def mkppcportrec(rec
):
269 namescript
= getword(rec
[:2])
270 name
= getpstr(rec
[2:2+33])
271 portkind
= getword(rec
[36:38])
275 identity
= (ctor
, type)
277 identity
= getpstr(rec
[38:38+33])
278 return namescript
, name
, portkind
, identity
280 def mklocationnamerec(rec
):
281 kind
= getword(rec
[:2])
283 if kind
== 0: stuff
= None
284 if kind
== 2: stuff
= getpstr(stuff
)
287 def mkunknown(type, data
):
288 return aetypes
.Unknown(type, data
)
291 return s
[1:1+ord(s
[0])]
294 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
297 return (ord(s
[0])<<8) |
(ord(s
[1])<<0)
299 def mkkeyword(keyword
):
300 return aetypes
.Keyword(keyword
)
303 return aetypes
.Range(dict['star'], dict['stop'])
305 def mkcomparison(dict):
306 return aetypes
.Comparison(dict['obj1'], dict['relo'].enum
, dict['obj2'])
309 return aetypes
.Logical(dict['logc'], dict['term'])
311 def mkstyledtext(dict):
312 return aetypes
.StyledText(dict['ksty'], dict['ktxt'])
315 return aetypes
.AEText(dict[keyAEScriptTag
], dict[keyAEStyles
], dict[keyAEText
])
317 def mkinsertionloc(dict):
318 return aetypes
.InsertionLoc(dict[keyAEObject
], dict[keyAEPosition
])
321 want
= dict['want'].type
322 form
= dict['form'].enum
325 if form
in ('name', 'indx', 'rang', 'test'):
326 if want
== 'text': return aetypes
.Text(seld
, fr
)
327 if want
== 'cha ': return aetypes
.Character(seld
, fr
)
328 if want
== 'cwor': return aetypes
.Word(seld
, fr
)
329 if want
== 'clin': return aetypes
.Line(seld
, fr
)
330 if want
== 'cpar': return aetypes
.Paragraph(seld
, fr
)
331 if want
== 'cwin': return aetypes
.Window(seld
, fr
)
332 if want
== 'docu': return aetypes
.Document(seld
, fr
)
333 if want
== 'file': return aetypes
.File(seld
, fr
)
334 if want
== 'cins': return aetypes
.InsertionPoint(seld
, fr
)
335 if want
== 'prop' and form
== 'prop' and aetypes
.IsType(seld
):
336 return aetypes
.Property(seld
.type, fr
)
337 return aetypes
.ObjectSpecifier(want
, form
, seld
, fr
)
339 # Note by Jack: I'm not 100% sure of the following code. This was
340 # provided by Donovan Preston, but I wonder whether the assignment
341 # to __class__ is safe. Moreover, shouldn't there be a better
342 # initializer for the classes in the suites?
343 def mkobjectfrommodule(dict, modulename
):
344 if type(dict['want']) == types
.ClassType
and issubclass(dict['want'], ObjectSpecifier
):
345 # The type has already been converted to Python. Convert back:-(
346 classtype
= dict['want']
347 dict['want'] = aetypes
.mktype(classtype
.want
)
348 want
= dict['want'].type
349 module
= __import__(modulename
)
350 codenamemapper
= module
._classdeclarations
351 classtype
= codenamemapper
.get(want
, None)
352 newobj
= mkobject(dict)
354 assert issubclass(classtype
, ObjectSpecifier
)
355 newobj
.__class
__ = classtype
358 def mktype(typecode
, modulename
=None):
360 module
= __import__(modulename
)
361 codenamemapper
= module
._classdeclarations
362 classtype
= codenamemapper
.get(typecode
, None)
365 return aetypes
.mktype(typecode
)