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.
24
from warnings import warnpy3k
25
warnpy3k("In 3.x, the aetools module is removed.", stacklevel=2)
29
from Carbon import Evt
30
from Carbon import AppleEvents
36
from aepack import packkey, pack, unpack, coerce, AEDescType
38
Error = 'aetools.Error'
40
# Amount of time to wait for program to be launched
41
LAUNCH_MAX_WAIT_TIME=10
43
# Special code to unpack an AppleEvent (which is *not* a disguised record!)
44
# Note by Jack: No??!? If I read the docs correctly it *is*....
54
'inte', # this attribute is read only - will be set in AESend
55
'esrc', # this attribute is read only
56
'miss', # this attribute is read only
62
desc = ae.AEGetAttributeDesc('miss', 'keyw')
67
def unpackevent(ae, formodulename=""):
70
dirobj = ae.AEGetParamDesc('----', '****')
74
parameters['----'] = unpack(dirobj, formodulename)
76
# Workaround for what I feel is a bug in OSX 10.2: 'errn' won't show up in missed...
78
dirobj = ae.AEGetParamDesc('errn', '****')
82
parameters['errn'] = unpack(dirobj, formodulename)
87
parameters[key] = unpack(ae.AEGetParamDesc(key, '****'), formodulename)
89
for key in aekeywords:
91
desc = ae.AEGetAttributeDesc(key, '****')
92
except (AE.Error, MacOS.Error), msg:
93
if msg[0] != -1701 and msg[0] != -1704:
96
attributes[key] = unpack(desc, formodulename)
97
return parameters, attributes
99
def packevent(ae, parameters = {}, attributes = {}):
100
for key, value in parameters.items():
101
packkey(ae, key, value)
102
for key, value in attributes.items():
103
ae.AEPutAttributeDesc(key, pack(value))
106
# Support routine for automatically generated Suite interfaces
107
# These routines are also useable for the reverse function.
109
def keysubst(arguments, keydict):
110
"""Replace long name keys by their 4-char counterparts, and check"""
111
ok = keydict.values()
112
for k in arguments.keys():
113
if keydict.has_key(k):
116
arguments[keydict[k]] = v
117
elif k != '----' and k not in ok:
118
raise TypeError, 'Unknown keyword argument: %s'%k
120
def enumsubst(arguments, key, edict):
121
"""Substitute a single enum keyword argument, if it occurs"""
122
if not arguments.has_key(key) or edict is None:
127
arguments[key] = Enum(edict[v])
129
raise TypeError, 'Unknown enumerator: %s'%v
131
def decodeerror(arguments):
132
"""Create the 'best' argument for a raise MacOS.Error"""
133
errn = arguments['errn']
135
if arguments.has_key('errs'):
136
err_a2 = arguments['errs']
138
err_a2 = MacOS.GetErrorString(errn)
139
if arguments.has_key('erob'):
140
err_a3 = arguments['erob']
144
return (err_a1, err_a2, err_a3)
147
"""An AE connection to an application"""
148
_signature = None # Can be overridden by subclasses
149
_moduleName = None # Can be overridden by subclasses
150
_elemdict = {} # Can be overridden by subclasses
151
_propdict = {} # Can be overridden by subclasses
153
__eventloop_initialized = 0
154
def __ensure_WMAvailable(klass):
155
if klass.__eventloop_initialized: return 1
156
if not MacOS.WMAvailable(): return 0
157
# Workaround for a but in MacOSX 10.2: we must have an event
158
# loop before we can call AESend.
159
Evt.WaitNextEvent(0,0)
161
__ensure_WMAvailable = classmethod(__ensure_WMAvailable)
163
def __init__(self, signature=None, start=0, timeout=0):
164
"""Create a communication channel with a particular application.
166
Addressing the application is done by specifying either a
167
4-byte signature, an AEDesc or an object that will __aepack__
170
self.target_signature = None
171
if signature is None:
172
signature = self._signature
173
if type(signature) == AEDescType:
174
self.target = signature
175
elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
176
self.target = signature.__aepack__()
177
elif type(signature) == StringType and len(signature) == 4:
178
self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
179
self.target_signature = signature
181
raise TypeError, "signature should be 4-char string or AEDesc"
182
self.send_flags = AppleEvents.kAEWaitReply
183
self.send_priority = AppleEvents.kAENormalPriority
185
self.send_timeout = timeout
187
self.send_timeout = AppleEvents.kAEDefaultTimeout
192
"""Start the application, if it is not running yet"""
194
self.send('ascr', 'noop')
196
_launch(self.target_signature)
197
for i in range(LAUNCH_MAX_WAIT_TIME):
199
self.send('ascr', 'noop')
207
"""Deprecated, used _start()"""
210
def newevent(self, code, subcode, parameters = {}, attributes = {}):
211
"""Create a complete structure for an apple event"""
213
event = AE.AECreateAppleEvent(code, subcode, self.target,
214
AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
215
packevent(event, parameters, attributes)
218
def sendevent(self, event):
219
"""Send a pre-created appleevent, await the reply and unpack it"""
220
if not self.__ensure_WMAvailable():
221
raise RuntimeError, "No window manager access, cannot send AppleEvent"
222
reply = event.AESend(self.send_flags, self.send_priority,
224
parameters, attributes = unpackevent(reply, self._moduleName)
225
return reply, parameters, attributes
227
def send(self, code, subcode, parameters = {}, attributes = {}):
228
"""Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
229
return self.sendevent(self.newevent(code, subcode, parameters, attributes))
232
# The following events are somehow "standard" and don't seem to appear in any
236
"""Send 'activate' command"""
237
self.send('misc', 'actv')
239
def _get(self, _object, asfile=None, _attributes={}):
240
"""_get: get data from an object
241
Required argument: the object
242
Keyword argument _attributes: AppleEvent attribute dictionary
248
_arguments = {'----':_object}
250
_arguments['rtyp'] = mktype(asfile)
252
_reply, _arguments, _attributes = self.send(_code, _subcode,
253
_arguments, _attributes)
254
if _arguments.has_key('errn'):
255
raise Error, decodeerror(_arguments)
257
if _arguments.has_key('----'):
258
return _arguments['----']
260
item.__class__ = asfile
269
def _set(self, _object, _attributes={}, **_arguments):
270
"""set: Set an object's data.
271
Required argument: the object for the command
272
Keyword argument to: The new value.
273
Keyword argument _attributes: AppleEvent attribute dictionary
278
keysubst(_arguments, self._argmap_set)
279
_arguments['----'] = _object
282
_reply, _arguments, _attributes = self.send(_code, _subcode,
283
_arguments, _attributes)
284
if _arguments.get('errn', 0):
285
raise Error, decodeerror(_arguments)
286
# XXXX Optionally decode result
287
if _arguments.has_key('----'):
288
return _arguments['----']
292
# Magic glue to allow suite-generated classes to function somewhat
293
# like the "application" class in OSA.
295
def __getattr__(self, name):
296
if self._elemdict.has_key(name):
297
cls = self._elemdict[name]
298
return DelayedComponentItem(cls, None)
299
if self._propdict.has_key(name):
300
cls = self._propdict[name]
302
raise AttributeError, name
304
# Tiny Finder class, for local use only
306
class _miniFinder(TalkTo):
307
def open(self, _object, _attributes={}, **_arguments):
308
"""open: Open the specified object(s)
309
Required argument: list of objects to open
310
Keyword argument _attributes: AppleEvent attribute dictionary
315
if _arguments: raise TypeError, 'No optional args expected'
316
_arguments['----'] = _object
319
_reply, _arguments, _attributes = self.send(_code, _subcode,
320
_arguments, _attributes)
321
if _arguments.has_key('errn'):
322
raise Error, decodeerror(_arguments)
323
# XXXX Optionally decode result
324
if _arguments.has_key('----'):
325
return _arguments['----']
328
_finder = _miniFinder('MACS')
330
def _launch(appfile):
331
"""Open a file thru the finder. Specify file by name or fsspec"""
332
_finder.open(_application_file(('ID ', appfile)))
335
class _application_file(ComponentItem):
336
"""application file - An application's file on disk"""
339
_application_file._propdict = {
341
_application_file._elemdict = {
345
# XXXX Should test more, really...
348
target = AE.AECreateDesc('sign', 'quil')
349
ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
350
print unpackevent(ae)
352
ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
353
obj = Character(2, Word(1, Document(1)))
356
packevent(ae, {'----': obj})
357
params, attrs = unpackevent(ae)
361
if __name__ == '__main__':