~malept/ubuntu/lucid/python2.6/dev-dependency-fix

« back to all changes in this revision

Viewing changes to Lib/plat-mac/aetools.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-02-13 12:51:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090213125100-uufgcb9yeqzujpqw
Tags: upstream-2.6.1
ImportĀ upstreamĀ versionĀ 2.6.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Tools for use in AppleEvent clients and servers.
 
2
 
 
3
pack(x) converts a Python object to an AEDesc object
 
4
unpack(desc) does the reverse
 
5
 
 
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
 
8
 
 
9
Plus...  Lots of classes and routines that help representing AE objects,
 
10
ranges, conditionals, logicals, etc., so you can write, e.g.:
 
11
 
 
12
    x = Character(1, Document("foobar"))
 
13
 
 
14
and pack(x) will create an AE object reference equivalent to AppleScript's
 
15
 
 
16
    character 1 of document "foobar"
 
17
 
 
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.
 
20
 
 
21
"""
 
22
 
 
23
 
 
24
from warnings import warnpy3k
 
25
warnpy3k("In 3.x, the aetools module is removed.", stacklevel=2)
 
26
 
 
27
from types import *
 
28
from Carbon import AE
 
29
from Carbon import Evt
 
30
from Carbon import AppleEvents
 
31
import MacOS
 
32
import sys
 
33
import time
 
34
 
 
35
from aetypes import *
 
36
from aepack import packkey, pack, unpack, coerce, AEDescType
 
37
 
 
38
Error = 'aetools.Error'
 
39
 
 
40
# Amount of time to wait for program to be launched
 
41
LAUNCH_MAX_WAIT_TIME=10
 
42
 
 
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*....
 
45
 
 
46
aekeywords = [
 
47
    'tran',
 
48
    'rtid',
 
49
    'evcl',
 
50
    'evid',
 
51
    'addr',
 
52
    'optk',
 
53
    'timo',
 
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
 
57
    'from'  # new in 1.0.1
 
58
]
 
59
 
 
60
def missed(ae):
 
61
    try:
 
62
        desc = ae.AEGetAttributeDesc('miss', 'keyw')
 
63
    except AE.Error, msg:
 
64
        return None
 
65
    return desc.data
 
66
 
 
67
def unpackevent(ae, formodulename=""):
 
68
    parameters = {}
 
69
    try:
 
70
        dirobj = ae.AEGetParamDesc('----', '****')
 
71
    except AE.Error:
 
72
        pass
 
73
    else:
 
74
        parameters['----'] = unpack(dirobj, formodulename)
 
75
        del dirobj
 
76
    # Workaround for what I feel is a bug in OSX 10.2: 'errn' won't show up in missed...
 
77
    try:
 
78
        dirobj = ae.AEGetParamDesc('errn', '****')
 
79
    except AE.Error:
 
80
        pass
 
81
    else:
 
82
        parameters['errn'] = unpack(dirobj, formodulename)
 
83
        del dirobj
 
84
    while 1:
 
85
        key = missed(ae)
 
86
        if not key: break
 
87
        parameters[key] = unpack(ae.AEGetParamDesc(key, '****'), formodulename)
 
88
    attributes = {}
 
89
    for key in aekeywords:
 
90
        try:
 
91
            desc = ae.AEGetAttributeDesc(key, '****')
 
92
        except (AE.Error, MacOS.Error), msg:
 
93
            if msg[0] != -1701 and msg[0] != -1704:
 
94
                raise
 
95
            continue
 
96
        attributes[key] = unpack(desc, formodulename)
 
97
    return parameters, attributes
 
98
 
 
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))
 
104
 
 
105
#
 
106
# Support routine for automatically generated Suite interfaces
 
107
# These routines are also useable for the reverse function.
 
108
#
 
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):
 
114
            v = arguments[k]
 
115
            del arguments[k]
 
116
            arguments[keydict[k]] = v
 
117
        elif k != '----' and k not in ok:
 
118
            raise TypeError, 'Unknown keyword argument: %s'%k
 
119
 
 
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:
 
123
        return
 
124
    v = arguments[key]
 
125
    ok = edict.values()
 
126
    if edict.has_key(v):
 
127
        arguments[key] = Enum(edict[v])
 
128
    elif not v in ok:
 
129
        raise TypeError, 'Unknown enumerator: %s'%v
 
130
 
 
131
def decodeerror(arguments):
 
132
    """Create the 'best' argument for a raise MacOS.Error"""
 
133
    errn = arguments['errn']
 
134
    err_a1 = errn
 
135
    if arguments.has_key('errs'):
 
136
        err_a2 = arguments['errs']
 
137
    else:
 
138
        err_a2 = MacOS.GetErrorString(errn)
 
139
    if arguments.has_key('erob'):
 
140
        err_a3 = arguments['erob']
 
141
    else:
 
142
        err_a3 = None
 
143
 
 
144
    return (err_a1, err_a2, err_a3)
 
145
 
 
146
class TalkTo:
 
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
 
152
 
 
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)
 
160
        return 1
 
161
    __ensure_WMAvailable = classmethod(__ensure_WMAvailable)
 
162
 
 
163
    def __init__(self, signature=None, start=0, timeout=0):
 
164
        """Create a communication channel with a particular application.
 
165
 
 
166
        Addressing the application is done by specifying either a
 
167
        4-byte signature, an AEDesc or an object that will __aepack__
 
168
        to an AEDesc.
 
169
        """
 
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
 
180
        else:
 
181
            raise TypeError, "signature should be 4-char string or AEDesc"
 
182
        self.send_flags = AppleEvents.kAEWaitReply
 
183
        self.send_priority = AppleEvents.kAENormalPriority
 
184
        if timeout:
 
185
            self.send_timeout = timeout
 
186
        else:
 
187
            self.send_timeout = AppleEvents.kAEDefaultTimeout
 
188
        if start:
 
189
            self._start()
 
190
 
 
191
    def _start(self):
 
192
        """Start the application, if it is not running yet"""
 
193
        try:
 
194
            self.send('ascr', 'noop')
 
195
        except AE.Error:
 
196
            _launch(self.target_signature)
 
197
            for i in range(LAUNCH_MAX_WAIT_TIME):
 
198
                try:
 
199
                    self.send('ascr', 'noop')
 
200
                except AE.Error:
 
201
                    pass
 
202
                else:
 
203
                    break
 
204
                time.sleep(1)
 
205
 
 
206
    def start(self):
 
207
        """Deprecated, used _start()"""
 
208
        self._start()
 
209
 
 
210
    def newevent(self, code, subcode, parameters = {}, attributes = {}):
 
211
        """Create a complete structure for an apple event"""
 
212
 
 
213
        event = AE.AECreateAppleEvent(code, subcode, self.target,
 
214
                  AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
 
215
        packevent(event, parameters, attributes)
 
216
        return event
 
217
 
 
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,
 
223
                                  self.send_timeout)
 
224
        parameters, attributes = unpackevent(reply, self._moduleName)
 
225
        return reply, parameters, attributes
 
226
 
 
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))
 
230
 
 
231
    #
 
232
    # The following events are somehow "standard" and don't seem to appear in any
 
233
    # suite...
 
234
    #
 
235
    def activate(self):
 
236
        """Send 'activate' command"""
 
237
        self.send('misc', 'actv')
 
238
 
 
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
 
243
        Returns: the data
 
244
        """
 
245
        _code = 'core'
 
246
        _subcode = 'getd'
 
247
 
 
248
        _arguments = {'----':_object}
 
249
        if asfile:
 
250
            _arguments['rtyp'] = mktype(asfile)
 
251
 
 
252
        _reply, _arguments, _attributes = self.send(_code, _subcode,
 
253
                _arguments, _attributes)
 
254
        if _arguments.has_key('errn'):
 
255
            raise Error, decodeerror(_arguments)
 
256
 
 
257
        if _arguments.has_key('----'):
 
258
            return _arguments['----']
 
259
            if asfile:
 
260
                item.__class__ = asfile
 
261
            return item
 
262
 
 
263
    get = _get
 
264
 
 
265
    _argmap_set = {
 
266
        'to' : 'data',
 
267
    }
 
268
 
 
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
 
274
        """
 
275
        _code = 'core'
 
276
        _subcode = 'setd'
 
277
 
 
278
        keysubst(_arguments, self._argmap_set)
 
279
        _arguments['----'] = _object
 
280
 
 
281
 
 
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['----']
 
289
 
 
290
    set = _set
 
291
 
 
292
    # Magic glue to allow suite-generated classes to function somewhat
 
293
    # like the "application" class in OSA.
 
294
 
 
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]
 
301
            return cls()
 
302
        raise AttributeError, name
 
303
 
 
304
# Tiny Finder class, for local use only
 
305
 
 
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
 
311
        """
 
312
        _code = 'aevt'
 
313
        _subcode = 'odoc'
 
314
 
 
315
        if _arguments: raise TypeError, 'No optional args expected'
 
316
        _arguments['----'] = _object
 
317
 
 
318
 
 
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['----']
 
326
#pass
 
327
 
 
328
_finder = _miniFinder('MACS')
 
329
 
 
330
def _launch(appfile):
 
331
    """Open a file thru the finder. Specify file by name or fsspec"""
 
332
    _finder.open(_application_file(('ID  ', appfile)))
 
333
 
 
334
 
 
335
class _application_file(ComponentItem):
 
336
    """application file - An application's file on disk"""
 
337
    want = 'appf'
 
338
 
 
339
_application_file._propdict = {
 
340
}
 
341
_application_file._elemdict = {
 
342
}
 
343
 
 
344
# Test program
 
345
# XXXX Should test more, really...
 
346
 
 
347
def test():
 
348
    target = AE.AECreateDesc('sign', 'quil')
 
349
    ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
 
350
    print unpackevent(ae)
 
351
    raw_input(":")
 
352
    ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
 
353
    obj = Character(2, Word(1, Document(1)))
 
354
    print obj
 
355
    print repr(obj)
 
356
    packevent(ae, {'----': obj})
 
357
    params, attrs = unpackevent(ae)
 
358
    print params['----']
 
359
    raw_input(":")
 
360
 
 
361
if __name__ == '__main__':
 
362
    test()
 
363
    sys.exit(1)