/Lib/plat-mac/aepack.py

http://unladen-swallow.googlecode.com/ · Python · 369 lines · 330 code · 9 blank · 30 comment · 5 complexity · dad571a31556474e1c8b58b161f338ea MD5 · raw file

  1. """Tools for use in AppleEvent clients and servers:
  2. conversion between AE types and python types
  3. pack(x) converts a Python object to an AEDesc object
  4. unpack(desc) does the reverse
  5. coerce(x, wanted_sample) coerces a python object to another python object
  6. """
  7. #
  8. # This code was originally written by Guido, and modified/extended by Jack
  9. # to include the various types that were missing. The reference used is
  10. # Apple Event Registry, chapter 9.
  11. #
  12. from warnings import warnpy3k
  13. warnpy3k("In 3.x, the aepack module is removed.", stacklevel=2)
  14. import struct
  15. import types
  16. from types import *
  17. from Carbon import AE
  18. from Carbon.AppleEvents import *
  19. import MacOS
  20. import Carbon.File
  21. import aetypes
  22. from aetypes import mkenum, ObjectSpecifier
  23. # These ones seem to be missing from AppleEvents
  24. # (they're in AERegistry.h)
  25. #typeColorTable = 'clrt'
  26. #typeDrawingArea = 'cdrw'
  27. #typePixelMap = 'cpix'
  28. #typePixelMapMinus = 'tpmm'
  29. #typeRotation = 'trot'
  30. #typeTextStyles = 'tsty'
  31. #typeStyledText = 'STXT'
  32. #typeAEText = 'tTXT'
  33. #typeEnumeration = 'enum'
  34. #
  35. # Some AE types are immedeately coerced into something
  36. # we like better (and which is equivalent)
  37. #
  38. unpacker_coercions = {
  39. typeComp : typeFloat,
  40. typeColorTable : typeAEList,
  41. typeDrawingArea : typeAERecord,
  42. typeFixed : typeFloat,
  43. typeExtended : typeFloat,
  44. typePixelMap : typeAERecord,
  45. typeRotation : typeAERecord,
  46. typeStyledText : typeAERecord,
  47. typeTextStyles : typeAERecord,
  48. };
  49. #
  50. # Some python types we need in the packer:
  51. #
  52. AEDescType = AE.AEDescType
  53. try:
  54. FSSType = Carbon.File.FSSpecType
  55. except AttributeError:
  56. class FSSType:
  57. pass
  58. FSRefType = Carbon.File.FSRefType
  59. AliasType = Carbon.File.AliasType
  60. def packkey(ae, key, value):
  61. if hasattr(key, 'which'):
  62. keystr = key.which
  63. elif hasattr(key, 'want'):
  64. keystr = key.want
  65. else:
  66. keystr = key
  67. ae.AEPutParamDesc(keystr, pack(value))
  68. def pack(x, forcetype = None):
  69. """Pack a python object into an AE descriptor"""
  70. if forcetype:
  71. if type(x) is StringType:
  72. return AE.AECreateDesc(forcetype, x)
  73. else:
  74. return pack(x).AECoerceDesc(forcetype)
  75. if x is None:
  76. return AE.AECreateDesc('null', '')
  77. if isinstance(x, AEDescType):
  78. return x
  79. if isinstance(x, FSSType):
  80. return AE.AECreateDesc('fss ', x.data)
  81. if isinstance(x, FSRefType):
  82. return AE.AECreateDesc('fsrf', x.data)
  83. if isinstance(x, AliasType):
  84. return AE.AECreateDesc('alis', x.data)
  85. if isinstance(x, IntType):
  86. return AE.AECreateDesc('long', struct.pack('l', x))
  87. if isinstance(x, FloatType):
  88. return AE.AECreateDesc('doub', struct.pack('d', x))
  89. if isinstance(x, StringType):
  90. return AE.AECreateDesc('TEXT', x)
  91. if isinstance(x, UnicodeType):
  92. data = x.encode('utf16')
  93. if data[:2] == '\xfe\xff':
  94. data = data[2:]
  95. return AE.AECreateDesc('utxt', data)
  96. if isinstance(x, ListType):
  97. list = AE.AECreateList('', 0)
  98. for item in x:
  99. list.AEPutDesc(0, pack(item))
  100. return list
  101. if isinstance(x, DictionaryType):
  102. record = AE.AECreateList('', 1)
  103. for key, value in x.items():
  104. packkey(record, key, value)
  105. #record.AEPutParamDesc(key, pack(value))
  106. return record
  107. if type(x) == types.ClassType and issubclass(x, ObjectSpecifier):
  108. # Note: we are getting a class object here, not an instance
  109. return AE.AECreateDesc('type', x.want)
  110. if hasattr(x, '__aepack__'):
  111. return x.__aepack__()
  112. if hasattr(x, 'which'):
  113. return AE.AECreateDesc('TEXT', x.which)
  114. if hasattr(x, 'want'):
  115. return AE.AECreateDesc('TEXT', x.want)
  116. return AE.AECreateDesc('TEXT', repr(x)) # Copout
  117. def unpack(desc, formodulename=""):
  118. """Unpack an AE descriptor to a python object"""
  119. t = desc.type
  120. if unpacker_coercions.has_key(t):
  121. desc = desc.AECoerceDesc(unpacker_coercions[t])
  122. t = desc.type # This is a guess by Jack....
  123. if t == typeAEList:
  124. l = []
  125. for i in range(desc.AECountItems()):
  126. keyword, item = desc.AEGetNthDesc(i+1, '****')
  127. l.append(unpack(item, formodulename))
  128. return l
  129. if t == typeAERecord:
  130. d = {}
  131. for i in range(desc.AECountItems()):
  132. keyword, item = desc.AEGetNthDesc(i+1, '****')
  133. d[keyword] = unpack(item, formodulename)
  134. return d
  135. if t == typeAEText:
  136. record = desc.AECoerceDesc('reco')
  137. return mkaetext(unpack(record, formodulename))
  138. if t == typeAlias:
  139. return Carbon.File.Alias(rawdata=desc.data)
  140. # typeAppleEvent returned as unknown
  141. if t == typeBoolean:
  142. return struct.unpack('b', desc.data)[0]
  143. if t == typeChar:
  144. return desc.data
  145. if t == typeUnicodeText:
  146. return unicode(desc.data, 'utf16')
  147. # typeColorTable coerced to typeAEList
  148. # typeComp coerced to extended
  149. # typeData returned as unknown
  150. # typeDrawingArea coerced to typeAERecord
  151. if t == typeEnumeration:
  152. return mkenum(desc.data)
  153. # typeEPS returned as unknown
  154. if t == typeFalse:
  155. return 0
  156. if t == typeFloat:
  157. data = desc.data
  158. return struct.unpack('d', data)[0]
  159. if t == typeFSS:
  160. return Carbon.File.FSSpec(rawdata=desc.data)
  161. if t == typeFSRef:
  162. return Carbon.File.FSRef(rawdata=desc.data)
  163. if t == typeInsertionLoc:
  164. record = desc.AECoerceDesc('reco')
  165. return mkinsertionloc(unpack(record, formodulename))
  166. # typeInteger equal to typeLongInteger
  167. if t == typeIntlText:
  168. script, language = struct.unpack('hh', desc.data[:4])
  169. return aetypes.IntlText(script, language, desc.data[4:])
  170. if t == typeIntlWritingCode:
  171. script, language = struct.unpack('hh', desc.data)
  172. return aetypes.IntlWritingCode(script, language)
  173. if t == typeKeyword:
  174. return mkkeyword(desc.data)
  175. if t == typeLongInteger:
  176. return struct.unpack('l', desc.data)[0]
  177. if t == typeLongDateTime:
  178. a, b = struct.unpack('lL', desc.data)
  179. return (long(a) << 32) + b
  180. if t == typeNull:
  181. return None
  182. if t == typeMagnitude:
  183. v = struct.unpack('l', desc.data)
  184. if v < 0:
  185. v = 0x100000000L + v
  186. return v
  187. if t == typeObjectSpecifier:
  188. record = desc.AECoerceDesc('reco')
  189. # If we have been told the name of the module we are unpacking aedescs for,
  190. # we can attempt to create the right type of python object from that module.
  191. if formodulename:
  192. return mkobjectfrommodule(unpack(record, formodulename), formodulename)
  193. return mkobject(unpack(record, formodulename))
  194. # typePict returned as unknown
  195. # typePixelMap coerced to typeAERecord
  196. # typePixelMapMinus returned as unknown
  197. # typeProcessSerialNumber returned as unknown
  198. if t == typeQDPoint:
  199. v, h = struct.unpack('hh', desc.data)
  200. return aetypes.QDPoint(v, h)
  201. if t == typeQDRectangle:
  202. v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
  203. return aetypes.QDRectangle(v0, h0, v1, h1)
  204. if t == typeRGBColor:
  205. r, g, b = struct.unpack('hhh', desc.data)
  206. return aetypes.RGBColor(r, g, b)
  207. # typeRotation coerced to typeAERecord
  208. # typeScrapStyles returned as unknown
  209. # typeSessionID returned as unknown
  210. if t == typeShortFloat:
  211. return struct.unpack('f', desc.data)[0]
  212. if t == typeShortInteger:
  213. return struct.unpack('h', desc.data)[0]
  214. # typeSMFloat identical to typeShortFloat
  215. # typeSMInt indetical to typeShortInt
  216. # typeStyledText coerced to typeAERecord
  217. if t == typeTargetID:
  218. return mktargetid(desc.data)
  219. # typeTextStyles coerced to typeAERecord
  220. # typeTIFF returned as unknown
  221. if t == typeTrue:
  222. return 1
  223. if t == typeType:
  224. return mktype(desc.data, formodulename)
  225. #
  226. # The following are special
  227. #
  228. if t == 'rang':
  229. record = desc.AECoerceDesc('reco')
  230. return mkrange(unpack(record, formodulename))
  231. if t == 'cmpd':
  232. record = desc.AECoerceDesc('reco')
  233. return mkcomparison(unpack(record, formodulename))
  234. if t == 'logi':
  235. record = desc.AECoerceDesc('reco')
  236. return mklogical(unpack(record, formodulename))
  237. return mkunknown(desc.type, desc.data)
  238. def coerce(data, egdata):
  239. """Coerce a python object to another type using the AE coercers"""
  240. pdata = pack(data)
  241. pegdata = pack(egdata)
  242. pdata = pdata.AECoerceDesc(pegdata.type)
  243. return unpack(pdata)
  244. #
  245. # Helper routines for unpack
  246. #
  247. def mktargetid(data):
  248. sessionID = getlong(data[:4])
  249. name = mkppcportrec(data[4:4+72])
  250. location = mklocationnamerec(data[76:76+36])
  251. rcvrName = mkppcportrec(data[112:112+72])
  252. return sessionID, name, location, rcvrName
  253. def mkppcportrec(rec):
  254. namescript = getword(rec[:2])
  255. name = getpstr(rec[2:2+33])
  256. portkind = getword(rec[36:38])
  257. if portkind == 1:
  258. ctor = rec[38:42]
  259. type = rec[42:46]
  260. identity = (ctor, type)
  261. else:
  262. identity = getpstr(rec[38:38+33])
  263. return namescript, name, portkind, identity
  264. def mklocationnamerec(rec):
  265. kind = getword(rec[:2])
  266. stuff = rec[2:]
  267. if kind == 0: stuff = None
  268. if kind == 2: stuff = getpstr(stuff)
  269. return kind, stuff
  270. def mkunknown(type, data):
  271. return aetypes.Unknown(type, data)
  272. def getpstr(s):
  273. return s[1:1+ord(s[0])]
  274. def getlong(s):
  275. return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
  276. def getword(s):
  277. return (ord(s[0])<<8) | (ord(s[1])<<0)
  278. def mkkeyword(keyword):
  279. return aetypes.Keyword(keyword)
  280. def mkrange(dict):
  281. return aetypes.Range(dict['star'], dict['stop'])
  282. def mkcomparison(dict):
  283. return aetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
  284. def mklogical(dict):
  285. return aetypes.Logical(dict['logc'], dict['term'])
  286. def mkstyledtext(dict):
  287. return aetypes.StyledText(dict['ksty'], dict['ktxt'])
  288. def mkaetext(dict):
  289. return aetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
  290. def mkinsertionloc(dict):
  291. return aetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])
  292. def mkobject(dict):
  293. want = dict['want'].type
  294. form = dict['form'].enum
  295. seld = dict['seld']
  296. fr = dict['from']
  297. if form in ('name', 'indx', 'rang', 'test'):
  298. if want == 'text': return aetypes.Text(seld, fr)
  299. if want == 'cha ': return aetypes.Character(seld, fr)
  300. if want == 'cwor': return aetypes.Word(seld, fr)
  301. if want == 'clin': return aetypes.Line(seld, fr)
  302. if want == 'cpar': return aetypes.Paragraph(seld, fr)
  303. if want == 'cwin': return aetypes.Window(seld, fr)
  304. if want == 'docu': return aetypes.Document(seld, fr)
  305. if want == 'file': return aetypes.File(seld, fr)
  306. if want == 'cins': return aetypes.InsertionPoint(seld, fr)
  307. if want == 'prop' and form == 'prop' and aetypes.IsType(seld):
  308. return aetypes.Property(seld.type, fr)
  309. return aetypes.ObjectSpecifier(want, form, seld, fr)
  310. # Note by Jack: I'm not 100% sure of the following code. This was
  311. # provided by Donovan Preston, but I wonder whether the assignment
  312. # to __class__ is safe. Moreover, shouldn't there be a better
  313. # initializer for the classes in the suites?
  314. def mkobjectfrommodule(dict, modulename):
  315. if type(dict['want']) == types.ClassType and issubclass(dict['want'], ObjectSpecifier):
  316. # The type has already been converted to Python. Convert back:-(
  317. classtype = dict['want']
  318. dict['want'] = aetypes.mktype(classtype.want)
  319. want = dict['want'].type
  320. module = __import__(modulename)
  321. codenamemapper = module._classdeclarations
  322. classtype = codenamemapper.get(want, None)
  323. newobj = mkobject(dict)
  324. if classtype:
  325. assert issubclass(classtype, ObjectSpecifier)
  326. newobj.__class__ = classtype
  327. return newobj
  328. def mktype(typecode, modulename=None):
  329. if modulename:
  330. module = __import__(modulename)
  331. codenamemapper = module._classdeclarations
  332. classtype = codenamemapper.get(typecode, None)
  333. if classtype:
  334. return classtype
  335. return aetypes.mktype(typecode)