AddressList.__str__(): Get rid of useless, and broken method. Closes
[python/dscho.git] / Lib / plat-mac / aetools.py
blob92d6ec90e40aa41a2319ab88bafc11dd1d1b3ad9
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 """
24 from types import *
25 from Carbon import AE
26 from Carbon import Evt
27 from Carbon import AppleEvents
28 import MacOS
29 import sys
30 import time
32 from aetypes import *
33 from aepack import packkey, pack, unpack, coerce, AEDescType
35 Error = 'aetools.Error'
37 # Amount of time to wait for program to be launched
38 LAUNCH_MAX_WAIT_TIME=10
40 # Special code to unpack an AppleEvent (which is *not* a disguised record!)
41 # Note by Jack: No??!? If I read the docs correctly it *is*....
43 aekeywords = [
44 'tran',
45 'rtid',
46 'evcl',
47 'evid',
48 'addr',
49 'optk',
50 'timo',
51 'inte', # this attribute is read only - will be set in AESend
52 'esrc', # this attribute is read only
53 'miss', # this attribute is read only
54 'from' # new in 1.0.1
57 def missed(ae):
58 try:
59 desc = ae.AEGetAttributeDesc('miss', 'keyw')
60 except AE.Error, msg:
61 return None
62 return desc.data
64 def unpackevent(ae, formodulename=""):
65 parameters = {}
66 try:
67 dirobj = ae.AEGetParamDesc('----', '****')
68 except AE.Error:
69 pass
70 else:
71 parameters['----'] = unpack(dirobj, formodulename)
72 del dirobj
73 # Workaround for what I feel is a bug in OSX 10.2: 'errn' won't show up in missed...
74 try:
75 dirobj = ae.AEGetParamDesc('errn', '****')
76 except AE.Error:
77 pass
78 else:
79 parameters['errn'] = unpack(dirobj, formodulename)
80 del dirobj
81 while 1:
82 key = missed(ae)
83 if not key: break
84 parameters[key] = unpack(ae.AEGetParamDesc(key, '****'), formodulename)
85 attributes = {}
86 for key in aekeywords:
87 try:
88 desc = ae.AEGetAttributeDesc(key, '****')
89 except (AE.Error, MacOS.Error), msg:
90 if msg[0] != -1701 and msg[0] != -1704:
91 raise
92 continue
93 attributes[key] = unpack(desc, formodulename)
94 return parameters, attributes
96 def packevent(ae, parameters = {}, attributes = {}):
97 for key, value in parameters.items():
98 packkey(ae, key, value)
99 for key, value in attributes.items():
100 ae.AEPutAttributeDesc(key, pack(value))
103 # Support routine for automatically generated Suite interfaces
104 # These routines are also useable for the reverse function.
106 def keysubst(arguments, keydict):
107 """Replace long name keys by their 4-char counterparts, and check"""
108 ok = keydict.values()
109 for k in arguments.keys():
110 if keydict.has_key(k):
111 v = arguments[k]
112 del arguments[k]
113 arguments[keydict[k]] = v
114 elif k != '----' and k not in ok:
115 raise TypeError, 'Unknown keyword argument: %s'%k
117 def enumsubst(arguments, key, edict):
118 """Substitute a single enum keyword argument, if it occurs"""
119 if not arguments.has_key(key) or edict is None:
120 return
121 v = arguments[key]
122 ok = edict.values()
123 if edict.has_key(v):
124 arguments[key] = Enum(edict[v])
125 elif not v in ok:
126 raise TypeError, 'Unknown enumerator: %s'%v
128 def decodeerror(arguments):
129 """Create the 'best' argument for a raise MacOS.Error"""
130 errn = arguments['errn']
131 err_a1 = errn
132 if arguments.has_key('errs'):
133 err_a2 = arguments['errs']
134 else:
135 err_a2 = MacOS.GetErrorString(errn)
136 if arguments.has_key('erob'):
137 err_a3 = arguments['erob']
138 else:
139 err_a3 = None
141 return (err_a1, err_a2, err_a3)
143 class TalkTo:
144 """An AE connection to an application"""
145 _signature = None # Can be overridden by subclasses
146 _moduleName = None # Can be overridden by subclasses
148 __eventloop_initialized = 0
149 def __ensure_WMAvailable(klass):
150 if klass.__eventloop_initialized: return 1
151 if not MacOS.WMAvailable(): return 0
152 # Workaround for a but in MacOSX 10.2: we must have an event
153 # loop before we can call AESend.
154 Evt.WaitNextEvent(0,0)
155 return 1
156 __ensure_WMAvailable = classmethod(__ensure_WMAvailable)
158 def __init__(self, signature=None, start=0, timeout=0):
159 """Create a communication channel with a particular application.
161 Addressing the application is done by specifying either a
162 4-byte signature, an AEDesc or an object that will __aepack__
163 to an AEDesc.
165 self.target_signature = None
166 if signature is None:
167 signature = self._signature
168 if type(signature) == AEDescType:
169 self.target = signature
170 elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
171 self.target = signature.__aepack__()
172 elif type(signature) == StringType and len(signature) == 4:
173 self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
174 self.target_signature = signature
175 else:
176 raise TypeError, "signature should be 4-char string or AEDesc"
177 self.send_flags = AppleEvents.kAEWaitReply
178 self.send_priority = AppleEvents.kAENormalPriority
179 if timeout:
180 self.send_timeout = timeout
181 else:
182 self.send_timeout = AppleEvents.kAEDefaultTimeout
183 if start:
184 self._start()
186 def _start(self):
187 """Start the application, if it is not running yet"""
188 try:
189 self.send('ascr', 'noop')
190 except AE.Error:
191 _launch(self.target_signature)
192 for i in range(LAUNCH_MAX_WAIT_TIME):
193 try:
194 self.send('ascr', 'noop')
195 except AE.Error:
196 pass
197 else:
198 break
199 time.sleep(1)
201 def start(self):
202 """Deprecated, used _start()"""
203 self._start()
205 def newevent(self, code, subcode, parameters = {}, attributes = {}):
206 """Create a complete structure for an apple event"""
208 event = AE.AECreateAppleEvent(code, subcode, self.target,
209 AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
210 packevent(event, parameters, attributes)
211 return event
213 def sendevent(self, event):
214 """Send a pre-created appleevent, await the reply and unpack it"""
215 if not self.__ensure_WMAvailable():
216 raise RuntimeError, "No window manager access, cannot send AppleEvent"
217 reply = event.AESend(self.send_flags, self.send_priority,
218 self.send_timeout)
219 parameters, attributes = unpackevent(reply, self._moduleName)
220 return reply, parameters, attributes
222 def send(self, code, subcode, parameters = {}, attributes = {}):
223 """Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
224 return self.sendevent(self.newevent(code, subcode, parameters, attributes))
227 # The following events are somehow "standard" and don't seem to appear in any
228 # suite...
230 def activate(self):
231 """Send 'activate' command"""
232 self.send('misc', 'actv')
234 def _get(self, _object, as=None, _attributes={}):
235 """_get: get data from an object
236 Required argument: the object
237 Keyword argument _attributes: AppleEvent attribute dictionary
238 Returns: the data
240 _code = 'core'
241 _subcode = 'getd'
243 _arguments = {'----':_object}
244 if as:
245 _arguments['rtyp'] = mktype(as)
247 _reply, _arguments, _attributes = self.send(_code, _subcode,
248 _arguments, _attributes)
249 if _arguments.has_key('errn'):
250 raise Error, decodeerror(_arguments)
252 if _arguments.has_key('----'):
253 return _arguments['----']
254 if as:
255 item.__class__ = as
256 return item
258 get = _get
260 _argmap_set = {
261 'to' : 'data',
264 def _set(self, _object, _attributes={}, **_arguments):
265 """set: Set an object's data.
266 Required argument: the object for the command
267 Keyword argument to: The new value.
268 Keyword argument _attributes: AppleEvent attribute dictionary
270 _code = 'core'
271 _subcode = 'setd'
273 keysubst(_arguments, self._argmap_set)
274 _arguments['----'] = _object
277 _reply, _arguments, _attributes = self.send(_code, _subcode,
278 _arguments, _attributes)
279 if _arguments.get('errn', 0):
280 raise Error, decodeerror(_arguments)
281 # XXXX Optionally decode result
282 if _arguments.has_key('----'):
283 return _arguments['----']
285 set = _set
287 # Magic glue to allow suite-generated classes to function somewhat
288 # like the "application" class in OSA.
290 def __getattr__(self, name):
291 if self._elemdict.has_key(name):
292 cls = self._elemdict[name]
293 return DelayedComponentItem(cls, None)
294 if self._propdict.has_key(name):
295 cls = self._propdict[name]
296 return cls()
297 raise AttributeError, name
299 # Tiny Finder class, for local use only
301 class _miniFinder(TalkTo):
302 def open(self, _object, _attributes={}, **_arguments):
303 """open: Open the specified object(s)
304 Required argument: list of objects to open
305 Keyword argument _attributes: AppleEvent attribute dictionary
307 _code = 'aevt'
308 _subcode = 'odoc'
310 if _arguments: raise TypeError, 'No optional args expected'
311 _arguments['----'] = _object
314 _reply, _arguments, _attributes = self.send(_code, _subcode,
315 _arguments, _attributes)
316 if _arguments.has_key('errn'):
317 raise Error, decodeerror(_arguments)
318 # XXXX Optionally decode result
319 if _arguments.has_key('----'):
320 return _arguments['----']
321 #pass
323 _finder = _miniFinder('MACS')
325 def _launch(appfile):
326 """Open a file thru the finder. Specify file by name or fsspec"""
327 _finder.open(_application_file(('ID ', appfile)))
330 class _application_file(ComponentItem):
331 """application file - An application's file on disk"""
332 want = 'appf'
334 _application_file._propdict = {
336 _application_file._elemdict = {
339 # Test program
340 # XXXX Should test more, really...
342 def test():
343 target = AE.AECreateDesc('sign', 'quil')
344 ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
345 print unpackevent(ae)
346 raw_input(":")
347 ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
348 obj = Character(2, Word(1, Document(1)))
349 print obj
350 print repr(obj)
351 packevent(ae, {'----': obj})
352 params, attrs = unpackevent(ae)
353 print params['----']
354 raw_input(":")
356 if __name__ == '__main__':
357 test()
358 sys.exit(1)