symbian-qemu-0.9.1-12/python-2.6.1/Lib/plat-mac/aetools.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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)