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"
23 from string
import strip
31 AEDescType
= type(AE
.AECreateDesc('TEXT', ''))
33 FSSType
= type(macfs
.FSSpec(':'))
36 def pack(x
, forcetype
= None):
38 if type(x
) is StringType
:
39 return AE
.AECreateDesc(forcetype
, x
)
41 return pack(x
).AECoerceDesc(forcetype
)
43 return AE
.AECreateDesc('null', '')
48 vol
, dir, filename
= x
.as_tuple()
50 header
= struct
.pack('hlb', vol
, dir, fnlen
)
51 padding
= '\0'*(63-fnlen
)
52 return AE
.AECreateDesc('fss ', header
+ filename
+ padding
)
54 return AE
.AECreateDesc('long', struct
.pack('l', x
))
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:])
62 return AE
.AECreateDesc('TEXT', x
)
64 list = AE
.AECreateList('', 0)
66 list.AEPutDesc(0, pack(item
))
68 if t
== DictionaryType
:
69 record
= AE
.AECreateList('', 1)
70 for key
, value
in x
.items():
71 record
.AEPutKeyDesc(key
, pack(value
))
73 if t
== InstanceType
and hasattr(x
, '__aepack__'):
75 return AE
.AECreateDesc('TEXT', repr(x
)) # Copout
87 return mkenum(desc
.data
)
89 return mktype(desc
.data
)
91 return struct
.unpack('l', desc
.data
)[0]
93 return struct
.unpack('h', desc
.data
)[0]
95 return struct
.unpack('f', desc
.data
)[0]
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'))
106 for i
in range(desc
.AECountItems()):
107 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
108 l
.append(unpack(item
))
112 for i
in range(desc
.AECountItems()):
113 keyword
, item
= desc
.AEGetNthDesc(i
+1, '****')
114 d
[keyword
] = unpack(item
)
117 record
= desc
.AECoerceDesc('reco')
118 return mkobject(unpack(record
))
120 record
= desc
.AECoerceDesc('reco')
121 return mkrange(unpack(record
))
123 record
= desc
.AECoerceDesc('reco')
124 return mkcomparison(unpack(record
))
126 record
= desc
.AECoerceDesc('reco')
127 return mklogical(unpack(record
))
129 return mktargetid(desc
.data
)
131 # XXX Can't handle alias records yet, so coerce to FS spec...
132 return unpack(desc
.AECoerceDesc('fss '))
134 return mkfss(desc
.data
)
135 return mkunknown(desc
.type, desc
.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])
161 identity
= (ctor
, type)
163 identity
= getpstr(rec
[38:38+33])
164 return namescript
, name
, portkind
, identity
166 def mklocationnamerec(rec
):
167 kind
= getword(rec
[:2])
169 if kind
== 0: stuff
= None
170 if kind
== 2: stuff
= getpstr(stuff
)
174 return s
[1:1+ord(s
[0])]
177 return (ord(s
[0])<<24) |
(ord(s
[1])<<16) |
(ord(s
[2])<<8) |
ord(s
[3])
180 return (ord(s
[0])<<8) |
(ord(s
[1])<<0)
183 def mkunknown(type, data
):
184 return Unknown(type, data
)
188 def __init__(self
, type, data
):
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
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
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
)
217 if type(s
) is StringType
: return repr(s
)
222 if IsEnum(enum
): return enum
227 def __init__(self
, enum
):
228 self
.enum
= "%-4.4s" % str(enum
)
231 return "Enum(%s)" % `self
.enum`
234 return strip(self
.enum
)
236 def __aepack__(self
):
237 return pack(self
.enum
, 'enum')
240 return IsInstance(x
, Enum
)
244 if IsType(type): return type
249 def __init__(self
, type):
250 self
.type = "%-4.4s" % str(type)
253 return "Type(%s)" % `self
.type`
256 return strip(self
.type)
258 def __aepack__(self
):
259 return pack(self
.type, 'type')
262 return IsInstance(x
, Type
)
266 return Range(dict['star'], dict['stop'])
270 def __init__(self
, start
, stop
):
275 return "Range(%s, %s)" % (`self
.start`
, `self
.stop`
)
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')
284 return IsInstance(x
, Range
)
287 def mkcomparison(dict):
288 return Comparison(dict['obj1'], dict['relo'].enum
, dict['obj2'])
292 def __init__(self
, obj1
, relo
, obj2
):
294 self
.relo
= "%-4.4s" % str(relo
)
298 return "Comparison(%s, %s, %s)" % (`self
.obj1`
, `self
.relo`
, `self
.obj2`
)
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
),
310 return IsInstance(x
, Comparison
)
314 return Logical(dict['logc'], dict['term'])
318 def __init__(self
, logc
, term
):
319 self
.logc
= "%-4.4s" % str(logc
)
323 return "Logical(%s, %s)" % (`self
.logc`
, `self
.term`
)
326 if type(self
.term
) == ListType
and len(self
.term
) == 2:
327 return "%s %s %s" % (nice(self
.term
[0]),
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')
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:
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):
372 s
= "ObjectSpecifier(%s, %s, %s" % (`self
.want`
, `self
.form`
, `self
.seld`
)
374 s
= s
+ ", %s)" % `self
.fr`
379 def __aepack__(self
):
380 return pack({'want': mktype(self
.want
),
381 'form': mkenum(self
.form
),
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
)
398 return "Property(%s, %s)" % (`self
.seld
.enum`
, `self
.fr`
)
400 return "Property(%s)" % `self
.seld
.enum`
404 return "Property %s of %s" % (str(self
.seld
), str(self
.fr
))
406 return "Property %s" % str(self
.seld
)
409 class SelectableItem(ObjectSpecifier
):
411 def __init__(self
, want
, seld
, fr
= None):
417 elif IsComparison(seld
) or IsLogical(seld
):
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
)
432 return "%s(%s)" % (self
.__class
__.__name
__, `self
.seld`
)
433 return "%s(%s, %s)" % (self
.__class
__.__name
__, `self
.seld`
, `self
.fr`
)
437 if type(seld
) == StringType
:
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
)
448 s
= "%s %s" % (self
.__class
__.__name
__, ss
)
449 if self
.fr
: s
= s
+ " of %s" % str(self
.fr
)
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')
469 want
= dict['want'].type
470 form
= dict['form'].enum
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!)
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
506 desc
= ae
.AEGetAttributeDesc('miss', 'keyw')
507 except AE
.Error
, msg
:
516 parameters
[key
] = unpack(ae
.AEGetParamDesc(key
, '****'))
518 for key
in aekeywords
:
520 desc
= ae
.AEGetAttributeDesc(key
, '****')
521 except (AE
.Error
, MacOS
.Error
), msg
:
523 raise sys
.exc_type
, sys
.exc_value
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
))
538 target
= AE
.AECreateDesc('sign', 'KAHL')
539 ae
= AE
.AECreateAppleEvent('aevt', 'oapp', target
, -1, 0)
540 print unpackevent(ae
)
542 ae
= AE
.AECreateAppleEvent('core', 'getd', target
, -1, 0)
543 obj
= Character(2, Word(1, Document(1)))
546 packevent(ae
, {'----': obj
})
547 params
, attrs
= unpackevent(ae
)
551 if __name__
== '__main__':