Permalink
Browse files

tagged py-aemreceive 0.4.0

git-svn-id: https://appscript.svn.sourceforge.net/svnroot/appscript@745 5e899d65-cff2-4c64-b955-3fa930e4d869
  • Loading branch information...
1 parent 7bf502d commit 4c3f751931303814d003dce6fab4aaea51e48d3f hhas committed Dec 20, 2010
@@ -0,0 +1,39 @@
+2010-12-20 -- 0.4.0
+
+- removed sfba, documentation
+
+2008-11-23 -- 0.3.0
+- renamed distribution to aemreceive
+- updated for py-appscript 0.19.0
+- added top-level kae attribute and deprecated existing kAE attribute. Clients should update their code accordingly.
+
+2008-07-12 -- 0.2.0
+- moved to py-aeserver project
+- updated MiniTC.sdef to newer sdef format
+
+2007-07-27 -- 0.1.6
+- added kArgNull (indicates AEDesc of typeNull is allowed as a parameter)
+- added kArgAny
+
+2006-12-17 -- 0.1.5
+- fixed problem in typedefs where slicing an Exception object causes an error on Python 2.5 (now uses 'e.args[1:]' instead of 'e[1:]')
+- fixed bug in typedefs.ArgDesc.AEM_unpack (wasn't returning a two-item struct)
+- when unpacking event parameters, now ignores any unrecognised parameters instead of raising error (c.f. SIG)
+- added kArgMissingValue; allows 'missing value' constant to be specified as an acceptable parameter type, e.g. ArgMultiChoice(kAE.typeUnicodeText, kArgMissingValue)
+- added keyReplyRequestedAttr ('ReplyRequested') to list of recognised event attributes
+
+2006-03-10 -- 0.1.4
+- changed from LGPL to MIT license
+
+- 2005-12-16 -- 0.1.3
+- _unpackEventAttributes() now unpacks 'Ignore' attribute if present
+
+- 2005-11-26 -- 0.1.2
+- fixed bug when unpacking Apple event attributes for use in event handler callbacks' 'attributes' parameter
+
+- 2005-11-17 -- 0.1.1
+- replaced classic Carbon.Events with OS X Carbon.CarbonEvt in sfba.py
+- converted tutorial to HTML format
+
+- 2005-11-16 -- 0.1.0
+- first release
@@ -0,0 +1,33 @@
+About aemreceive
+================
+
+Provides basic support for handling Apple events.
+
+Used by ASDictionary.
+
+
+Requirements
+------------
+
+- Python 2.3+
+- Mac OS X 10.4+
+- appscript 0.22.0+
+
+
+Installation
+------------
+
+aemreceive is packaged using the standard Python Distribution Utilities
+(a.k.a. Distutils). To install appscript, cd to the aemreceive-0.4.0
+directory and run:
+
+ python setup.py install
+
+Setuptools will be used if available, otherwise the setup.py script will
+revert to distutils.
+
+
+Copyright
+---------
+
+aemreceive is released into the public domain.
@@ -0,0 +1,8 @@
+"""aemreceive -- Install Python functions as Apple event handlers."""
+
+from aem import AEType, AEEnum # re-exported for convenience
+from aem import kae # re-export for convenience
+
+from main import kMissingValue, Codecs, installeventhandler, removeeventhandler, installcoercionhandler, removecoercionhandler
+from handlererror import EventHandlerError
+from typedefs import kArgDesc, kArgMissingValue, ArgType, ArgListOf, ArgEnum, ArgMultiChoice
@@ -0,0 +1,26 @@
+"""handlererror -- Used to pass known errors back to client."""
+
+from aem import kae
+
+
+class EventHandlerError(Exception):
+ """Event-handling callbacks should raise an EventHandlerError exception to send an error message back to client."""
+ def __init__(self, number, message=None, object=None, coercion=None):
+ self.number = number
+ self.message = message
+ self.object = object
+ self.coercion = coercion
+ Exception.__init__(self, number, message, object, coercion)
+
+ def get(self): # used internally by aemreceive
+ result = {kae.keyErrorNumber: self.number}
+ for key, val in [
+ (kae.keyErrorString, self.message),
+ (kae.kOSAErrorOffendingObject, self.object),
+ (kae.kOSAErrorExpectedType, self.coercion)]:
+ if val is not None:
+ result[key] = val
+ return result
+
+ def __str__(self):
+ return '%s (%i)' % (self.message, self.number)
@@ -0,0 +1,233 @@
+"""main -- Provides functions for installing and removing Apple event handlers in Python-based applications."""
+
+from StringIO import StringIO
+from traceback import print_exc
+import struct
+
+import mactypes
+import aem
+from aem import ae, kae
+
+from typedefs import buildDefs
+from handlererror import EventHandlerError
+
+
+if struct.pack("h", 1) == '\x00\x01': # host is big-endian
+ fourCharCode = lambda code: code
+else: # host is small-endian
+ fourCharCode = lambda code: code[::-1]
+
+
+######################################################################
+# PUBLIC
+######################################################################
+
+kMissingValue = ae.newdesc(kae.typeType, fourCharCode('msng')) # 'missing value' constant
+
+
+class Codecs(aem.Codecs):
+ """Default Codecs for aemreceive; same as aem.Codecs except that None is packed as 'missing value' constant instead of 'null'.
+ """
+ def __init__(self):
+ aem.Codecs.__init__(self)
+ self.encoders[type(None)] = lambda value, codecs: kMissingValue
+
+
+######################################################################
+# PRIVATE
+######################################################################
+# Constants
+
+_standardCodecs = Codecs() # default codecs for unpacking incoming events' attribute and parameter data and packing result/error data into reply events
+
+_eventAttributes = [
+ (kae.keyTransactionIDAttr, 'TransactionID'),
+ (kae.keyReturnIDAttr, 'ReturnID'),
+ (kae.keyEventClassAttr, 'EventClass'),
+ (kae.keyEventIDAttr, 'EventID'),
+ (kae.keyAddressAttr, 'Address'),
+ (kae.keyOptionalKeywordAttr, 'OptionalKeyword'),
+ (kae.keyTimeoutAttr, 'Timeout'),
+ (kae.keyInteractLevelAttr, 'InteractLevel'),
+ (kae.keyEventSourceAttr, 'EventSource'),
+ (kae.keyOriginalAddressAttr, 'OriginalAddress'),
+ (kae.keyAcceptTimeoutAttr, 'AcceptTimeout'),
+ (kae.keyReplyRequestedAttr, 'ReplyRequested'),
+ (kae.keySubjectAttr, 'Subject'),
+ (kae.enumConsiderations, 'Ignore'), # deprecated; use enumConsidsAndIgnores instead
+ (kae.enumConsidsAndIgnores, 'ConsiderIgnore'),
+ ]
+
+_attributesArgName = 'attributes'
+
+
+#######
+# Used when installing event handler callbacks
+
+def _processArgDefs(callback, argDefs, eventCode):
+ total = callback.func_code.co_argcount
+ argNames = callback.func_code.co_varnames[:total]
+ optionalArgNames = argNames[total - len(callback.func_defaults or []):]
+ includeAttributes = argNames and argNames[0] == _attributesArgName
+ if includeAttributes:
+ argNames = argNames[1:]
+ if len(argNames) != len(argDefs):
+ raise TypeError, "Can't install event handler %r: expected %i parameters but function %r has %i." % (eventCode, len(argNames), callback.__name__, len(argDefs))
+ requiredArgDefs = []
+ optionalArgDefs = {}
+ for (code, argName, datatypes) in argDefs:
+ if argName not in argNames:
+ raise TypeError, "Can't install event handler %r: function %r has no parameter named %r." % (eventCode, callback.__name__, argName)
+ datatypes = buildDefs(datatypes)
+ if argName in optionalArgNames:
+ optionalArgDefs[code] = (argName, datatypes)
+ else:
+ requiredArgDefs.append((code, argName, datatypes))
+ return includeAttributes, requiredArgDefs, optionalArgDefs
+
+
+#######
+# Used to unpack attributes and parameters of incoming events
+
+def _unpackEventAttributes(event):
+ attributes = {}
+ for code, name in _eventAttributes:
+ try:
+ attributes[name] = _standardCodecs.unpack(event.getattr(code, kae.typeWildCard))
+ except Exception:
+ pass
+ return attributes
+
+
+def _unpackValue(value, datatypes, codecs):
+ success, result = datatypes.AEM_unpack(value, codecs)
+ if success:
+ return result
+ else:
+ raise result
+
+
+def _unpackAppleEvent(event, includeAttributes, requiredArgDefs, optionalArgDefs, codecs):
+ desiredResultType = None
+ if includeAttributes:
+ kargs = {_attributesArgName: _unpackEventAttributes(event)}
+ else:
+ kargs = {}
+ params = dict([event.getitem(i + 1, kae.typeWildCard) for i in range(event.count())])
+ for code, argName, datatypes in requiredArgDefs:
+ try:
+ argValue = params.pop(code)
+ except KeyError:
+ raise EventHandlerError(-1721, "Required parameter %r is missing." % code)
+ else:
+ kargs[argName] = _unpackValue(argValue, datatypes, codecs)
+ for code, argValue in params.items():
+ try:
+ argName, datatypes = optionalArgDefs[code]
+ except KeyError: # (note: SIG says that any unrecognised parameters should be ignored)
+ if code == kae.keyAERequestedType: # event contains a 'desired result type' parameter but callback doesn't handle this explicitly, so have callback wrapper attempt to perform coercion automatically when packing result
+ desiredResultType = argValue
+ else:
+ kargs[argName] = _unpackValue(argValue, datatypes, codecs)
+ return kargs, desiredResultType
+
+
+#######
+# Used to pack result/error data into reply events
+
+def _packAppleEvent(desc, parameters, codecs=_standardCodecs):
+ if desc.type == kae.typeAppleEvent: # only pack and return a result/error where event has requested one
+ for key, value in parameters.items():
+ desc.setparam(key, codecs.pack(value))
+
+
+#######
+# Wrap user-defined callback function with automatic data unpacking/packing and error handling
+
+def makeCallbackWrapper(callback, eventCode, parameterDefinitions, codecs):
+ includeAttributes, requiredArgDefs, optionalArgDefs = _processArgDefs(callback, parameterDefinitions, eventCode)
+ def wrapper(requestDesc, replyDesc):
+ try:
+ kargs, desiredResultType = _unpackAppleEvent(requestDesc, includeAttributes, requiredArgDefs, optionalArgDefs, codecs)
+ reply = callback(**kargs)
+ if desiredResultType:
+ try:
+ reply = codecs.pack(reply).coerce(desiredResultType)
+ except: # TO DECIDE: should we raise coercion error -1700 here, or return value as-is? (latter is safest if we can't find out)
+ pass
+ if reply is not None:
+ _packAppleEvent(replyDesc, {kae.keyAEResult: reply}, codecs)
+ except EventHandlerError, err: # unpacking failed, so send an error message back to client
+ _packAppleEvent(replyDesc, err.get())
+ except: # an untrapped (i.e. unexpected) error occurred, so construct an error message for caller's benefit then rethrow error
+ s = StringIO()
+ print_exc(file=s)
+ _packAppleEvent(replyDesc, {
+ kae.keyErrorNumber: -10000, # Apple event handler failed
+ kae.keyErrorString: 'An internal error occurred: untrapped exception in AE handler %r.\n%s' % (eventCode, s.getvalue())
+ })
+ s.close()
+ raise # re-raise error to be caught by application
+ return wrapper
+
+
+######################################################################
+# PUBLIC
+######################################################################
+
+def installeventhandler(callback, eventCode, *parameterDefinitions, **kargs):
+ """Install an Apple event handler."""
+ codecs = kargs.pop('codecs', _standardCodecs)
+ ae.installeventhandler(eventCode[:4], eventCode[4:],
+ makeCallbackWrapper(callback, eventCode, parameterDefinitions, codecs))
+
+def removeeventhandler(eventCode):
+ """Remove an installed Apple event handler."""
+ ae.removeeventhandler(eventCode[:4], eventCode[4:])
+
+def installcoercionhandler(callback, fromType, toType):
+ """Install an AEDesc coercion handler."""
+ ae.installcoercionhandler(fromType, toType, callback)
+
+def removecoercionhandler(fromType, toType):
+ """Remove an installed AEDesc coercion handler."""
+ ae.removecoercionhandler(fromType, toType)
+
+
+######################################################################
+# PRIVATE
+######################################################################
+# Install various xxxx-to-unicode coercions if the OS (10.2, 10.3) doesn't already provide them
+
+def _coerceTypeAndEnum(desc, toType):
+ return _standardCodecs.pack(unicode(fourCharCode(desc.data)))
+
+def _coerceBoolAndNum(desc, toType):
+ return desc.coerce('TEXT').coerce('utxt')
+
+def _coerceFileTypes(desc, toType):
+ return desc.coerce('furl').coerce('utxt')
+
+_extraCoercions = [
+ (aem.AEType('utxt'), kae.typeType, _coerceTypeAndEnum),
+ (aem.AEEnum('yes '), kae.typeEnumerated, _coerceTypeAndEnum),
+ (True, kae.typeBoolean, _coerceBoolAndNum),
+ (3, kae.typeInteger, _coerceBoolAndNum),
+ (3.1, kae.typeFloat, _coerceBoolAndNum),
+# (mactypes.File('/').fsalias, kae.typeAlias, _coerceFileTypes),
+# (mactypes.File('/').fsref, kae.typeFSRef, _coerceFileTypes),
+# (mactypes.File('/').fsspec, kae.typeFSS, _coerceFileTypes),
+ ]
+
+
+for testVal, fromType, func in _extraCoercions:
+ try:
+ _standardCodecs.pack(testVal).coerce(kae.typeUnicodeText)
+ except ae.MacOSError, err:
+ if err[0] == -1700:
+ try:
+ installcoercionhandler(func, fromType, kae.typeUnicodeText)
+ except:
+ pass
+ else:
+ raise
Oops, something went wrong.

0 comments on commit 4c3f751

Please sign in to comment.