Don't reference removed files in Makefile
[python/dscho.git] / Mac / Lib / toolbox / aetools.py
blobe91449a20eed2ce31ba47a3bcfcbbc7a91f1ca10
1 """Tools for use in AppleEvent clients and servers.
3 pack(x) converts a Python object to an AEDesc object
4 unpack(desc) does the reverse
6 packevent(event, parameters, attributes) sets params and attrs in an AEAppleEvent record
7 unpackevent(event) returns the parameters and attributes from an AEAppleEvent record
9 Plus... Lots of classes and routines that help representing AE objects,
10 ranges, conditionals, logicals, etc., so you can write, e.g.:
12 x = Character(1, Document("foobar"))
14 and pack(x) will create an AE object reference equivalent to AppleScript's
16 character 1 of document "foobar"
18 """
21 import struct
22 import string
23 from string import strip
24 from types import *
25 import AE
26 import MacOS
27 import macfs
28 import StringIO
31 AEDescType = type(AE.AECreateDesc('TEXT', ''))
33 FSSType = type(macfs.FSSpec(':'))
36 def pack(x, forcetype = None):
37 if forcetype:
38 if type(x) is StringType:
39 return AE.AECreateDesc(forcetype, x)
40 else:
41 return pack(x).AECoerceDesc(forcetype)
42 if x == None:
43 return AE.AECreateDesc('null', '')
44 t = type(x)
45 if t == AEDescType:
46 return x
47 if t == FSSType:
48 vol, dir, filename = x.as_tuple()
49 fnlen = len(filename)
50 header = struct.pack('hlb', vol, dir, fnlen)
51 padding = '\0'*(63-fnlen)
52 return AE.AECreateDesc('fss ', header + filename + padding)
53 if t == IntType:
54 return AE.AECreateDesc('long', struct.pack('l', x))
55 if t == FloatType:
56 # XXX Weird thing -- Think C's "double" is 10 bytes, but
57 # struct.pack('d') return 12 bytes (and struct.unpack requires
58 # them, too). The first 2 bytes seem to be repeated...
59 # Probably an alignment problem
60 return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
61 if t == StringType:
62 return AE.AECreateDesc('TEXT', x)
63 if t == ListType:
64 list = AE.AECreateList('', 0)
65 for item in x:
66 list.AEPutDesc(0, pack(item))
67 return list
68 if t == DictionaryType:
69 record = AE.AECreateList('', 1)
70 for key, value in x.items():
71 record.AEPutKeyDesc(key, pack(value))
72 return record
73 if t == InstanceType and hasattr(x, '__aepack__'):
74 return x.__aepack__()
75 return AE.AECreateDesc('TEXT', repr(x)) # Copout
78 def unpack(desc):
79 t = desc.type
80 if t == 'TEXT':
81 return desc.data
82 if t == 'fals':
83 return 0
84 if t == 'true':
85 return 1
86 if t == 'enum':
87 return mkenum(desc.data)
88 if t == 'type':
89 return mktype(desc.data)
90 if t == 'long':
91 return struct.unpack('l', desc.data)[0]
92 if t == 'shor':
93 return struct.unpack('h', desc.data)[0]
94 if t == 'sing':
95 return struct.unpack('f', desc.data)[0]
96 if t == 'exte':
97 data = desc.data
98 # XXX See corresponding note for pack()
99 return struct.unpack('d', data[:2] + data)[0]
100 if t in ('doub', 'comp', 'magn'):
101 return unpack(desc.AECoerceDesc('exte'))
102 if t == 'null':
103 return None
104 if t == 'list':
105 l = []
106 for i in range(desc.AECountItems()):
107 keyword, item = desc.AEGetNthDesc(i+1, '****')
108 l.append(unpack(item))
109 return l
110 if t == 'reco':
111 d = {}
112 for i in range(desc.AECountItems()):
113 keyword, item = desc.AEGetNthDesc(i+1, '****')
114 d[keyword] = unpack(item)
115 return d
116 if t == 'obj ':
117 record = desc.AECoerceDesc('reco')
118 return mkobject(unpack(record))
119 if t == 'rang':
120 record = desc.AECoerceDesc('reco')
121 return mkrange(unpack(record))
122 if t == 'cmpd':
123 record = desc.AECoerceDesc('reco')
124 return mkcomparison(unpack(record))
125 if t == 'logi':
126 record = desc.AECoerceDesc('reco')
127 return mklogical(unpack(record))
128 if t == 'targ':
129 return mktargetid(desc.data)
130 if t == 'alis':
131 # XXX Can't handle alias records yet, so coerce to FS spec...
132 return unpack(desc.AECoerceDesc('fss '))
133 if t == 'fss ':
134 return mkfss(desc.data)
135 return mkunknown(desc.type, desc.data)
138 def mkfss(data):
139 print "mkfss data =", `data`
140 vol, dir, fnlen = struct.unpack('hlb', data[:7])
141 filename = data[7:7+fnlen]
142 print (vol, dir, fnlen, filename)
143 return macfs.FSSpec((vol, dir, filename))
146 def mktargetid(data):
147 sessionID = getlong(data[:4])
148 name = mkppcportrec(data[4:4+72])
149 print len(name), `name`
150 location = mklocationnamerec(data[76:76+36])
151 rcvrName = mkppcportrec(data[112:112+72])
152 return sessionID, name, location, rcvrName
154 def mkppcportrec(rec):
155 namescript = getword(rec[:2])
156 name = getpstr(rec[2:2+33])
157 portkind = getword(rec[36:38])
158 if portkind == 1:
159 ctor = rec[38:42]
160 type = rec[42:46]
161 identity = (ctor, type)
162 else:
163 identity = getpstr(rec[38:38+33])
164 return namescript, name, portkind, identity
166 def mklocationnamerec(rec):
167 kind = getword(rec[:2])
168 stuff = rec[2:]
169 if kind == 0: stuff = None
170 if kind == 2: stuff = getpstr(stuff)
171 return kind, stuff
173 def getpstr(s):
174 return s[1:1+ord(s[0])]
176 def getlong(s):
177 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
179 def getword(s):
180 return (ord(s[0])<<8) | (ord(s[1])<<0)
183 def mkunknown(type, data):
184 return Unknown(type, data)
186 class Unknown:
188 def __init__(self, type, data):
189 self.type = type
190 self.data = data
192 def __repr__(self):
193 return "Unknown(%s, %s)" % (`self.type`, `self.data`)
195 def __aepack__(self):
196 return pack(self.data, self.type)
199 def IsSubclass(cls, base):
200 """Test whether CLASS1 is the same as or a subclass of CLASS2"""
201 # Loop to optimize for single inheritance
202 while 1:
203 if cls is base: return 1
204 if len(cls.__bases__) <> 1: break
205 cls = cls.__bases__[0]
206 # Recurse to cope with multiple inheritance
207 for c in cls.__bases__:
208 if IsSubclass(c, base): return 1
209 return 0
211 def IsInstance(x, cls):
212 """Test whether OBJECT is an instance of (a subclass of) CLASS"""
213 return type(x) is InstanceType and IsSubclass(x.__class__, cls)
216 def nice(s):
217 if type(s) is StringType: return repr(s)
218 else: return str(s)
221 def mkenum(enum):
222 if IsEnum(enum): return enum
223 return Enum(enum)
225 class Enum:
227 def __init__(self, enum):
228 self.enum = "%-4.4s" % str(enum)
230 def __repr__(self):
231 return "Enum(%s)" % `self.enum`
233 def __str__(self):
234 return strip(self.enum)
236 def __aepack__(self):
237 return pack(self.enum, 'enum')
239 def IsEnum(x):
240 return IsInstance(x, Enum)
243 def mktype(type):
244 if IsType(type): return type
245 return Type(type)
247 class Type:
249 def __init__(self, type):
250 self.type = "%-4.4s" % str(type)
252 def __repr__(self):
253 return "Type(%s)" % `self.type`
255 def __str__(self):
256 return strip(self.type)
258 def __aepack__(self):
259 return pack(self.type, 'type')
261 def IsType(x):
262 return IsInstance(x, Type)
265 def mkrange(dict):
266 return Range(dict['star'], dict['stop'])
268 class Range:
270 def __init__(self, start, stop):
271 self.start = start
272 self.stop = stop
274 def __repr__(self):
275 return "Range(%s, %s)" % (`self.start`, `self.stop`)
277 def __str__(self):
278 return "%s thru %s" % (nice(self.start), nice(self.stop))
280 def __aepack__(self):
281 return pack({'star': self.start, 'stop': self.stop}, 'rang')
283 def IsRange(x):
284 return IsInstance(x, Range)
287 def mkcomparison(dict):
288 return Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
290 class Comparison:
292 def __init__(self, obj1, relo, obj2):
293 self.obj1 = obj1
294 self.relo = "%-4.4s" % str(relo)
295 self.obj2 = obj2
297 def __repr__(self):
298 return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`)
300 def __str__(self):
301 return "%s %s %s" % (nice(self.obj1), strip(self.relo), nice(self.obj2))
303 def __aepack__(self):
304 return pack({'obj1': self.obj1,
305 'relo': mkenum(self.relo),
306 'obj2': self.obj2},
307 'cmpd')
309 def IsComparison(x):
310 return IsInstance(x, Comparison)
313 def mklogical(dict):
314 return Logical(dict['logc'], dict['term'])
316 class Logical:
318 def __init__(self, logc, term):
319 self.logc = "%-4.4s" % str(logc)
320 self.term = term
322 def __repr__(self):
323 return "Logical(%s, %s)" % (`self.logc`, `self.term`)
325 def __str__(self):
326 if type(self.term) == ListType and len(self.term) == 2:
327 return "%s %s %s" % (nice(self.term[0]),
328 strip(self.logc),
329 nice(self.term[1]))
330 else:
331 return "%s(%s)" % (strip(self.logc), nice(self.term))
333 def __aepack__(self):
334 return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi')
336 def IsLogical(x):
337 return IsInstance(x, Logical)
340 class ObjectSpecifier:
342 """A class for constructing and manipulation AE object specifiers in python.
344 An object specifier is actually a record with four fields:
346 key type description
347 --- ---- -----------
349 'want' type what kind of thing we want,
350 e.g. word, paragraph or property
352 'form' enum how we specify the thing(s) we want,
353 e.g. by index, by range, by name, or by property specifier
355 'seld' any which thing(s) we want,
356 e.g. its index, its name, or its property specifier
358 'from' object the object in which it is contained,
359 or null, meaning look for it in the application
361 Note that we don't call this class plain "Object", since that name
362 is likely to be used by the application.
365 def __init__(self, want, form, seld, fr = None):
366 self.want = want
367 self.form = form
368 self.seld = seld
369 self.fr = fr
371 def __repr__(self):
372 s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
373 if self.fr:
374 s = s + ", %s)" % `self.fr`
375 else:
376 s = s + ")"
377 return s
379 def __aepack__(self):
380 return pack({'want': mktype(self.want),
381 'form': mkenum(self.form),
382 'seld': self.seld,
383 'from': self.fr},
384 'obj ')
387 def IsObjectSpecifier(x):
388 return IsInstance(x, ObjectSpecifier)
391 class Property(ObjectSpecifier):
393 def __init__(self, which, fr = None):
394 ObjectSpecifier.__init__(self, 'prop', 'prop', mkenum(which), fr)
396 def __repr__(self):
397 if self.fr:
398 return "Property(%s, %s)" % (`self.seld.enum`, `self.fr`)
399 else:
400 return "Property(%s)" % `self.seld.enum`
402 def __str__(self):
403 if self.fr:
404 return "Property %s of %s" % (str(self.seld), str(self.fr))
405 else:
406 return "Property %s" % str(self.seld)
409 class SelectableItem(ObjectSpecifier):
411 def __init__(self, want, seld, fr = None):
412 t = type(seld)
413 if t == StringType:
414 form = 'name'
415 elif IsRange(seld):
416 form = 'rang'
417 elif IsComparison(seld) or IsLogical(seld):
418 form = 'test'
419 else:
420 form = 'indx'
421 ObjectSpecifier.__init__(self, want, form, seld, fr)
424 class ComponentItem(SelectableItem):
425 # Derived classes *must* set the *class attribute* 'want' to some constant
427 def __init__(self, which, fr = None):
428 SelectableItem.__init__(self, self.want, which, fr)
430 def __repr__(self):
431 if not self.fr:
432 return "%s(%s)" % (self.__class__.__name__, `self.seld`)
433 return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`)
435 def __str__(self):
436 seld = self.seld
437 if type(seld) == StringType:
438 ss = repr(seld)
439 elif IsRange(seld):
440 start, stop = seld.start, seld.stop
441 if type(start) == InstanceType == type(stop) and \
442 start.__class__ == self.__class__ == stop.__class__:
443 ss = str(start.seld) + " thru " + str(stop.seld)
444 else:
445 ss = str(seld)
446 else:
447 ss = str(seld)
448 s = "%s %s" % (self.__class__.__name__, ss)
449 if self.fr: s = s + " of %s" % str(self.fr)
450 return s
453 template = """
454 class %s(ComponentItem): want = '%s'
457 exec template % ("Text", 'text')
458 exec template % ("Character", 'cha ')
459 exec template % ("Word", 'cwor')
460 exec template % ("Line", 'clin')
461 exec template % ("Paragraph", 'cpar')
462 exec template % ("Window", 'cwin')
463 exec template % ("Document", 'docu')
464 exec template % ("File", 'file')
465 exec template % ("InsertionPoint", 'cins')
468 def mkobject(dict):
469 want = dict['want'].type
470 form = dict['form'].enum
471 seld = dict['seld']
472 fr = dict['from']
473 if form in ('name', 'indx', 'rang', 'test'):
474 if want == 'text': return Text(seld, fr)
475 if want == 'cha ': return Character(seld, fr)
476 if want == 'cwor': return Word(seld, fr)
477 if want == 'clin': return Line(seld, fr)
478 if want == 'cpar': return Paragraph(seld, fr)
479 if want == 'cwin': return Window(seld, fr)
480 if want == 'docu': return Document(seld, fr)
481 if want == 'file': return File(seld, fr)
482 if want == 'cins': return InsertionPoint(seld, fr)
483 if want == 'prop' and form == 'prop' and IsType(seld):
484 return Property(seld.type, fr)
485 return ObjectSpecifier(want, form, seld, fr)
488 # Special code to unpack an AppleEvent (which is *not* a disguised record!)
490 aekeywords = [
491 'tran',
492 'rtid',
493 'evcl',
494 'evid',
495 'addr',
496 'optk',
497 'timo',
498 'inte', # this attribute is read only - will be set in AESend
499 'esrc', # this attribute is read only
500 'miss', # this attribute is read only
501 'from' # new in 1.0.1
504 def missed(ae):
505 try:
506 desc = ae.AEGetAttributeDesc('miss', 'keyw')
507 except AE.Error, msg:
508 return None
509 return desc.data
511 def unpackevent(ae):
512 parameters = {}
513 while 1:
514 key = missed(ae)
515 if not key: break
516 parameters[key] = unpack(ae.AEGetParamDesc(key, '****'))
517 attributes = {}
518 for key in aekeywords:
519 try:
520 desc = ae.AEGetAttributeDesc(key, '****')
521 except (AE.Error, MacOS.Error), msg:
522 if msg[0] != -1701:
523 raise sys.exc_type, sys.exc_value
524 continue
525 attributes[key] = unpack(desc)
526 return parameters, attributes
528 def packevent(ae, parameters = {}, attributes = {}):
529 for key, value in parameters.items():
530 ae.AEPutParamDesc(key, pack(value))
531 for key, value in attributes.items():
532 ae.AEPutAttributeDesc(key, pack(value))
535 # Test program
537 def test():
538 target = AE.AECreateDesc('sign', 'KAHL')
539 ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
540 print unpackevent(ae)
541 raw_input(":")
542 ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
543 obj = Character(2, Word(1, Document(1)))
544 print obj
545 print repr(obj)
546 packevent(ae, {'----': obj})
547 params, attrs = unpackevent(ae)
548 print params['----']
549 raw_input(":")
551 if __name__ == '__main__':
552 test()