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():
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 key
not in arguments
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 'errs' in arguments
:
136 err_a2
= arguments
['errs']
138 err_a2
= MacOS
.GetErrorString(errn
)
139 if 'erob' in arguments
:
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 'errn' in _arguments
:
255 raise Error
, decodeerror(_arguments
)
257 if '----' in _arguments
:
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 '----' in _arguments
:
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 name
in self
._elemdict
:
297 cls
= self
._elemdict
[name
]
298 return DelayedComponentItem(cls
, None)
299 if name
in self
._propdict
:
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 'errn' in _arguments
:
322 raise Error
, decodeerror(_arguments
)
323 # XXXX Optionally decode result
324 if '----' in _arguments
:
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__':