Bump version to 0.9.1.
[python/dscho.git] / Mac / Contrib / PythonScript / baetools.py
blob8f0bb0486dd0c83a23a3b29756242a3335be7e2f
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 Some of the stuff that appears to be exported from this module comes from other
19 files: the pack stuff from aepack, the objects from aetypes.
21 """
23 from types import *
24 import AE
25 import AppleEvents
26 import MacOS
27 import sys
29 from baetypes import *
30 from baepack import pack, unpack, coerce, AEDescType
32 Error = 'baetools.Error'
34 # Special code to unpack an AppleEvent (which is *not* a disguised record!)
35 # Note by Jack: No??!? If I read the docs correctly it *is*....
37 aekeywords = [
38 'tran',
39 'rtid',
40 'evcl',
41 'evid',
42 'addr',
43 'optk',
44 'timo',
45 'inte', # this attribute is read only - will be set in AESend
46 'esrc', # this attribute is read only
47 'miss', # this attribute is read only
48 'from' # new in 1.0.1
51 def missed(ae):
52 try:
53 desc = ae.AEGetAttributeDesc('miss', 'keyw')
54 except AE.Error, msg:
55 return None
56 return desc.data
58 def unpackevent(ae):
59 parameters = {}
60 while 1:
61 key = missed(ae)
62 if not key: break
63 parameters[key] = unpack(ae.AEGetParamDesc(key, '****'))
64 attributes = {}
65 for key in aekeywords:
66 try:
67 desc = ae.AEGetAttributeDesc(key, '****')
68 except (AE.Error, MacOS.Error), msg:
69 if msg[0] != -1701 and msg[0] != -1704:
70 raise sys.exc_type, sys.exc_value
71 continue
72 attributes[key] = unpack(desc)
73 return parameters, attributes
75 def packevent(ae, parameters = {}, attributes = {}):
76 for key, value in parameters.items():
77 ae.AEPutParamDesc(key, pack(value))
78 for key, value in attributes.items():
79 ae.AEPutAttributeDesc(key, pack(value))
82 # Support routine for automatically generated Suite interfaces
83 # These routines are also useable for the reverse function.
85 def keysubst(arguments, keydict):
86 """Replace long name keys by their 4-char counterparts, and check"""
87 ok = keydict.values()
88 for k in arguments.keys():
89 if keydict.has_key(k):
90 v = arguments[k]
91 del arguments[k]
92 arguments[keydict[k]] = v
93 elif k != '----' and k not in ok:
94 raise TypeError, 'Unknown keyword argument: %s'%k
96 def enumsubst(arguments, key, edict):
97 """Substitute a single enum keyword argument, if it occurs"""
98 if not arguments.has_key(key):
99 return
100 v = arguments[key]
101 ok = edict.values()
102 if edict.has_key(v):
103 arguments[key] = edict[v]
104 elif not v in ok:
105 raise TypeError, 'Unknown enumerator: %s'%v
107 def decodeerror(arguments):
108 """Create the 'best' argument for a raise MacOS.Error"""
109 errn = arguments['errn']
110 err_a1 = errn
111 if arguments.has_key('errs'):
112 err_a2 = arguments['errs']
113 else:
114 err_a2 = MacOS.GetErrorString(errn)
115 if arguments.has_key('erob'):
116 err_a3 = arguments['erob']
117 else:
118 err_a3 = None
120 return (err_a1, err_a2, err_a3)
122 class TalkTo:
123 """An AE connection to an application"""
125 def __init__(self, signature, start=0, timeout=0):
126 """Create a communication channel with a particular application.
127 Addressing the application is done by specifying either a
128 4-byte signature, an AEDesc or an object that will __aepack__
129 to an AEDesc.
131 self.target_signature = None
132 if type(signature) == AEDescType:
133 self.target = signature
134 elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
135 self.target = signature.__aepack__()
136 elif type(signature) == StringType and len(signature) == 4:
137 self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
138 self.target_signature = signature
139 else:
140 raise TypeError, "signature should be 4-char string or AEDesc"
141 self.send_flags = AppleEvents.kAEWaitReply
142 self.send_priority = AppleEvents.kAENormalPriority
143 if timeout:
144 self.send_timeout = timeout
145 else:
146 self.send_timeout = AppleEvents.kAEDefaultTimeout
147 if start:
148 self.start()
150 def start(self):
151 """Start the application, if it is not running yet"""
152 self.send_flags = AppleEvents.kAENoReply
153 _launch(self.target_signature)
155 def newevent(self, code, subcode, parameters = {}, attributes = {}):
156 """Create a complete structure for an apple event"""
157 event = AE.AECreateAppleEvent(code, subcode, self.target,
158 AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
159 # print parameters, attributes
160 packevent(event, parameters, attributes)
161 return event
163 def sendevent(self, event):
164 """Send a pre-created appleevent, await the reply and unpack it"""
166 reply = event.AESend(self.send_flags, self.send_priority,
167 self.send_timeout)
168 parameters, attributes = unpackevent(reply)
169 return reply, parameters, attributes
171 def send(self, code, subcode, parameters = {}, attributes = {}):
172 """Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
173 return self.sendevent(self.newevent(code, subcode, parameters, attributes))
176 # The following events are somehow "standard" and don't seem to appear in any
177 # suite...
179 def activate(self):
180 """Send 'activate' command"""
181 self.send('misc', 'actv')
183 def _get(self, _object, as=None, _attributes={}):
184 """_get: get data from an object
185 Required argument: the object
186 Keyword argument _attributes: AppleEvent attribute dictionary
187 Returns: the data
189 _code = 'core'
190 _subcode = 'getd'
192 _arguments = {'----':_object}
193 if as:
194 _arguments['rtyp'] = mktype(as)
196 _reply, _arguments, _attributes = self.send(_code, _subcode,
197 _arguments, _attributes)
198 if _arguments.has_key('errn'):
199 raise Error, decodeerror(_arguments)
201 if _arguments.has_key('----'):
202 return _arguments['----']
204 # Tiny Finder class, for local use only
206 class _miniFinder(TalkTo):
207 def open(self, _object, _attributes={}, **_arguments):
208 """open: Open the specified object(s)
209 Required argument: list of objects to open
210 Keyword argument _attributes: AppleEvent attribute dictionary
212 _code = 'aevt'
213 _subcode = 'odoc'
215 if _arguments: raise TypeError, 'No optional args expected'
216 _arguments['----'] = _object
219 _reply, _arguments, _attributes = self.send(_code, _subcode,
220 _arguments, _attributes)
221 if _arguments.has_key('errn'):
222 raise aetools.Error, aetools.decodeerror(_arguments)
223 # XXXX Optionally decode result
224 if _arguments.has_key('----'):
225 return _arguments['----']
226 #pass
228 _finder = _miniFinder('MACS')
230 def _launch(appfile):
231 """Open a file thru the finder. Specify file by name or fsspec"""
232 _finder.open(_application_file(('ID ', appfile)))
235 class _application_file(ComponentItem):
236 """application file - An application's file on disk"""
237 want = 'appf'
239 _application_file._propdict = {
241 _application_file._elemdict = {
244 # Test program
245 # XXXX Should test more, really...
247 def test():
248 target = AE.AECreateDesc('sign', 'quil')
249 ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
250 print unpackevent(ae)
251 raw_input(":")
252 ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
253 obj = Character(2, Word(1, Document(1)))
254 print obj
255 print repr(obj)
256 packevent(ae, {'----': obj})
257 params, attrs = unpackevent(ae)
258 print params['----']
259 raw_input(":")
261 if __name__ == '__main__':
262 test()
263 sys.exit(1)