summaryrefslogtreecommitdiffstats
path: root/Mac
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1995-02-05 16:59:27 (GMT)
committerGuido van Rossum <guido@python.org>1995-02-05 16:59:27 (GMT)
commit315592383e7fd02a73806b986e1038c7daf66777 (patch)
treebf4327f9ba4f15d7700d55c8cbab4ecb2f1cfbe3 /Mac
parentb19a645b69355bd4ccd6d6618cd00e4047996bbc (diff)
downloadcpython-315592383e7fd02a73806b986e1038c7daf66777.zip
cpython-315592383e7fd02a73806b986e1038c7daf66777.tar.gz
cpython-315592383e7fd02a73806b986e1038c7daf66777.tar.bz2
more support for different kinds of objects
Diffstat (limited to 'Mac')
-rw-r--r--Mac/Lib/test/echo.py119
-rw-r--r--Mac/Lib/toolbox/aetools.py608
2 files changed, 550 insertions, 177 deletions
diff --git a/Mac/Lib/test/echo.py b/Mac/Lib/test/echo.py
new file mode 100644
index 0000000..12fbf92
--- /dev/null
+++ b/Mac/Lib/test/echo.py
@@ -0,0 +1,119 @@
+"""'echo' -- an AppleEvent handler which handles all events the same.
+
+It replies to each event by echoing the parameter back to the client.
+This is a good way to find out how the Script Editor formats AppleEvents,
+especially to figure out all the different forms an object specifier
+can have (without having to rely on Apple's implementation).
+"""
+
+import AE
+from AppleEvents import *
+import Evt
+from Events import *
+import aetools
+import sys
+import MacOS
+import traceback
+
+kHighLevelEvent = 23 # Not defined anywhere for Python yet?
+
+
+def main():
+ echo = EchoServer()
+ MacOS.EnableAppswitch(0) # Disable Python's own "event handling"
+ try:
+ echo.mainloop()
+ finally:
+ MacOS.EnableAppswitch(1) # Let Python have a go at events
+ echo.close()
+
+
+class EchoServer:
+
+ suites = ['aevt', 'core']
+
+ def __init__(self):
+ self.active = 0
+ for suite in self.suites:
+ AE.AEInstallEventHandler(suite, typeWildCard, self.aehandler)
+ self.active = 1
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ if self.active:
+ self.active = 0
+ for suite in self.suites:
+ AE.AERemoveEventHandler(suite, typeWildCard)
+
+ def mainloop(self, mask = everyEvent, timeout = 60*60):
+ while 1:
+ got, event = Evt.WaitNextEvent(mask, timeout)
+ if got:
+ self.lowlevelhandler(event)
+
+ def lowlevelhandler(self, event):
+ what, message, when, (h, v), modifiers = event
+ if what == kHighLevelEvent:
+ print "High Level Event:", `code(message)`, `code(h | (v<<16))`
+ try:
+ AE.AEProcessAppleEvent(event)
+ except AE.Error, msg:
+ print "AEProcessAppleEvent error:"
+ traceback.print_exc()
+ elif what == keyDown:
+ c = chr(message & charCodeMask)
+ if c == '.' and modifiers & cmdKey:
+ raise KeyboardInterrupt, "Command-period"
+ MacOS.HandleEvent(event)
+ elif what <> autoKey:
+ print "Event:", (eventname(what), message, when, (h, v), modifiers)
+ MacOS.HandleEvent(event)
+
+ def aehandler(self, request, reply):
+ print "Apple Event",
+ parameters, attributes = aetools.unpackevent(request)
+ print "class =", `attributes['evcl'].type`,
+ print "id =", `attributes['evid'].type`
+ print "Parameters:"
+ keys = parameters.keys()
+ keys.sort()
+ for key in keys:
+ print "%s: %.150s" % (`key`, `parameters[key]`)
+ print " :", str(parameters[key])
+ print "Attributes:"
+ keys = attributes.keys()
+ keys.sort()
+ for key in keys:
+ print "%s: %.150s" % (`key`, `attributes[key]`)
+ aetools.packevent(reply, parameters)
+
+
+_eventnames = {
+ keyDown: 'keyDown',
+ autoKey: 'autoKey',
+ mouseDown: 'mouseDown',
+ mouseUp: 'mouseUp',
+ updateEvt: 'updateEvt',
+ diskEvt: 'diskEvt',
+ activateEvt: 'activateEvt',
+ osEvt: 'osEvt',
+}
+
+def eventname(what):
+ if _eventnames.has_key(what): return _eventnames[what]
+ else: return `what`
+
+def code(x):
+ "Convert a long int to the 4-character code it really is"
+ s = ''
+ for i in range(4):
+ x, c = divmod(x, 256)
+ s = chr(c) + s
+ return s
+
+
+if __name__ == '__main__':
+ main()
+else: main()
diff --git a/Mac/Lib/toolbox/aetools.py b/Mac/Lib/toolbox/aetools.py
index 745bce7..ae8b71a 100644
--- a/Mac/Lib/toolbox/aetools.py
+++ b/Mac/Lib/toolbox/aetools.py
@@ -1,39 +1,80 @@
+"""Tools for use in AppleEvent clients and servers.
+
+pack(x) converts a Python object to an AEDesc object
+unpack(desc) does the reverse
+
+packevent(event, parameters, attributes) sets params and attrs in an AEAppleEvent record
+unpackevent(event) returns the parameters and attributes from an AEAppleEvent record
+
+Plus... Lots of classes and routines that help representing AE objects,
+ranges, conditionals, logicals, etc., so you can write, e.g.:
+
+ x = Character(1, Document("foobar"))
+
+and pack(x) will create an AE object reference equivalent to AppleScript's
+
+ character 1 of document "foobar"
+
+"""
+
+
import struct
-import types
+import string
+from string import strip
+from types import *
import AE
import MacOS
+import macfs
import StringIO
+
AEDescType = type(AE.AECreateDesc('TEXT', ''))
-def pack(x):
+FSSType = type(macfs.FSSpec(':'))
+
+
+def pack(x, forcetype = None):
+ if forcetype:
+ if type(x) is StringType:
+ return AE.AECreateDesc(forcetype, x)
+ else:
+ return pack(x).AECoerceDesc(forcetype)
if x == None:
return AE.AECreateDesc('null', '')
t = type(x)
if t == AEDescType:
return x
- if t == types.IntType:
+ if t == FSSType:
+ vol, dir, filename = x.as_tuple()
+ fnlen = len(filename)
+ header = struct.pack('hlb', vol, dir, fnlen)
+ padding = '\0'*(63-fnlen)
+ return AE.AECreateDesc('fss ', header + filename + padding)
+ if t == IntType:
return AE.AECreateDesc('long', struct.pack('l', x))
- if t == types.FloatType:
+ if t == FloatType:
+ # XXX Weird thing -- Think C's "double" is 10 bytes, but
+ # struct.pack('d') return 12 bytes (and struct.unpack requires
+ # them, too). The first 2 bytes seem to be repeated...
+ # Probably an alignment problem
return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
- if t == types.StringType:
+ if t == StringType:
return AE.AECreateDesc('TEXT', x)
- if t == types.ListType:
+ if t == ListType:
list = AE.AECreateList('', 0)
for item in x:
list.AEPutDesc(0, pack(item))
return list
- if t == types.TupleType:
- t, d = x
- return AE.AECreateDesc(t, d)
- if t == types.DictionaryType:
+ if t == DictionaryType:
record = AE.AECreateList('', 1)
for key, value in x.items():
record.AEPutKeyDesc(key, pack(value))
- if t == types.InstanceType and hasattr(x, '__aepack__'):
+ return record
+ if t == InstanceType and hasattr(x, '__aepack__'):
return x.__aepack__()
return AE.AECreateDesc('TEXT', repr(x)) # Copout
+
def unpack(desc):
t = desc.type
if t == 'TEXT':
@@ -42,6 +83,10 @@ def unpack(desc):
return 0
if t == 'true':
return 1
+ if t == 'enum':
+ return mkenum(desc.data)
+ if t == 'type':
+ return mktype(desc.data)
if t == 'long':
return struct.unpack('l', desc.data)[0]
if t == 'shor':
@@ -50,11 +95,10 @@ def unpack(desc):
return struct.unpack('f', desc.data)[0]
if t == 'exte':
data = desc.data
+ # XXX See corresponding note for pack()
return struct.unpack('d', data[:2] + data)[0]
if t in ('doub', 'comp', 'magn'):
return unpack(desc.AECoerceDesc('exte'))
- if t == 'enum':
- return ('enum', desc.data)
if t == 'null':
return None
if t == 'list':
@@ -70,178 +114,376 @@ def unpack(desc):
d[keyword] = unpack(item)
return d
if t == 'obj ':
- return unpackobject(desc.data)
- return desc.type, desc.data # Copout
-
-class Object:
- def __init__(self, dict = {}):
- self.dict = dict
- for key, value in dict.items():
- self.dict[key] = value
+ record = desc.AECoerceDesc('reco')
+ return mkobject(unpack(record))
+ if t == 'rang':
+ record = desc.AECoerceDesc('reco')
+ return mkrange(unpack(record))
+ if t == 'cmpd':
+ record = desc.AECoerceDesc('reco')
+ return mkcomparison(unpack(record))
+ if t == 'logi':
+ record = desc.AECoerceDesc('reco')
+ return mklogical(unpack(record))
+ if t == 'targ':
+ return mktargetid(desc.data)
+ if t == 'alis':
+ # XXX Can't handle alias records yet, so coerce to FS spec...
+ return unpack(desc.AECoerceDesc('fss '))
+ if t == 'fss ':
+ return mkfss(desc.data)
+ return mkunknown(desc.type, desc.data)
+
+
+def mkfss(data):
+ vol, dir, fnlen = struct.unpack('hlb', data[:7])
+ filename = data[7:7+fnlen]
+ return macfs.FSSpec((vol, dir, filename))
+
+
+def mktargetid(data):
+ sessionID = getlong(data[:4])
+ name = mkppcportrec(data[4:4+72])
+ print len(name), `name`
+ location = mklocationnamerec(data[76:76+36])
+ rcvrName = mkppcportrec(data[112:112+72])
+ return sessionID, name, location, rcvrName
+
+def mkppcportrec(rec):
+ namescript = getword(rec[:2])
+ name = getpstr(rec[2:2+33])
+ portkind = getword(rec[36:38])
+ if portkind == 1:
+ ctor = rec[38:42]
+ type = rec[42:46]
+ identity = (ctor, type)
+ else:
+ identity = getpstr(rec[38:38+33])
+ return namescript, name, portkind, identity
+
+def mklocationnamerec(rec):
+ kind = getword(rec[:2])
+ stuff = rec[2:]
+ if kind == 0: stuff = None
+ if kind == 2: stuff = getpstr(stuff)
+ return kind, stuff
+
+def getpstr(s):
+ return s[1:1+ord(s[0])]
+
+def getlong(s):
+ return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def getword(s):
+ return (ord(s[0])<<8) | (ord(s[1])<<0)
+
+
+def mkunknown(type, data):
+ return Unknown(type, data)
+
+class Unknown:
+
+ def __init__(self, type, data):
+ self.type = type
+ self.data = data
+
+ def __repr__(self):
+ return "Unknown(%s, %s)" % (`self.type`, `self.data`)
+
+ def __aepack__(self):
+ return pack(self.data, self.type)
+
+
+def IsSubclass(cls, base):
+ """Test whether CLASS1 is the same as or a subclass of CLASS2"""
+ # Loop to optimize for single inheritance
+ while 1:
+ if cls is base: return 1
+ if len(cls.__bases__) <> 1: break
+ cls = cls.__bases__[0]
+ # Recurse to cope with multiple inheritance
+ for c in cls.__bases__:
+ if IsSubclass(c, base): return 1
+ return 0
+
+def IsInstance(x, cls):
+ """Test whether OBJECT is an instance of (a subclass of) CLASS"""
+ return type(x) is InstanceType and IsSubclass(x.__class__, cls)
+
+
+def nice(s):
+ if type(s) is StringType: return repr(s)
+ else: return str(s)
+
+
+def mkenum(enum):
+ if IsEnum(enum): return enum
+ return Enum(enum)
+
+class Enum:
+
+ def __init__(self, enum):
+ self.enum = "%-4.4s" % str(enum)
+
+ def __repr__(self):
+ return "Enum(%s)" % `self.enum`
+
+ def __str__(self):
+ return strip(self.enum)
+
+ def __aepack__(self):
+ return pack(self.enum, 'enum')
+
+def IsEnum(x):
+ return IsInstance(x, Enum)
+
+
+def mktype(type):
+ if IsType(type): return type
+ return Type(type)
+
+class Type:
+
+ def __init__(self, type):
+ self.type = "%-4.4s" % str(type)
+
+ def __repr__(self):
+ return "Type(%s)" % `self.type`
+
+ def __str__(self):
+ return strip(self.type)
+
+ def __aepack__(self):
+ return pack(self.type, 'type')
+
+def IsType(x):
+ return IsInstance(x, Type)
+
+
+def mkrange(dict):
+ return Range(dict['star'], dict['stop'])
+
+class Range:
+
+ def __init__(self, start, stop):
+ self.start = start
+ self.stop = stop
+
+ def __repr__(self):
+ return "Range(%s, %s)" % (`self.start`, `self.stop`)
+
+ def __str__(self):
+ return "%s thru %s" % (nice(self.start), nice(self.stop))
+
+ def __aepack__(self):
+ return pack({'star': self.start, 'stop': self.stop}, 'rang')
+
+def IsRange(x):
+ return IsInstance(x, Range)
+
+
+def mkcomparison(dict):
+ return Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
+
+class Comparison:
+
+ def __init__(self, obj1, relo, obj2):
+ self.obj1 = obj1
+ self.relo = "%-4.4s" % str(relo)
+ self.obj2 = obj2
+
def __repr__(self):
- return "Object(%s)" % `self.dict`
+ return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`)
+
def __str__(self):
- want = self.dict['want']
- form = self.dict['form']
- seld = self.dict['seld']
- s = "%s %s %s" % (nicewant(want), niceform(form), niceseld(seld))
- fr = self.dict['from']
- if fr:
- s = s + " of " + str(fr)
+ return "%s %s %s" % (nice(self.obj1), strip(self.relo), nice(self.obj2))
+
+ def __aepack__(self):
+ return pack({'obj1': self.obj1,
+ 'relo': mkenum(self.relo),
+ 'obj2': self.obj2},
+ 'cmpd')
+
+def IsComparison(x):
+ return IsInstance(x, Comparison)
+
+
+def mklogical(dict):
+ return Logical(dict['logc'], dict['term'])
+
+class Logical:
+
+ def __init__(self, logc, term):
+ self.logc = "%-4.4s" % str(logc)
+ self.term = term
+
+ def __repr__(self):
+ return "Logical(%s, %s)" % (`self.logc`, `self.term`)
+
+ def __str__(self):
+ if type(self.term) == ListType and len(self.term) == 2:
+ return "%s %s %s" % (nice(self.term[0]),
+ strip(self.logc),
+ nice(self.term[1]))
+ else:
+ return "%s(%s)" % (strip(self.logc), nice(self.term))
+
+ def __aepack__(self):
+ return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi')
+
+def IsLogical(x):
+ return IsInstance(x, Logical)
+
+
+class ObjectSpecifier:
+
+ """A class for constructing and manipulation AE object specifiers in python.
+
+ An object specifier is actually a record with four fields:
+
+ key type description
+ --- ---- -----------
+
+ 'want' type what kind of thing we want,
+ e.g. word, paragraph or property
+
+ 'form' enum how we specify the thing(s) we want,
+ e.g. by index, by range, by name, or by property specifier
+
+ 'seld' any which thing(s) we want,
+ e.g. its index, its name, or its property specifier
+
+ 'from' object the object in which it is contained,
+ or null, meaning look for it in the application
+
+ Note that we don't call this class plain "Object", since that name
+ is likely to be used by the application.
+ """
+
+ def __init__(self, want, form, seld, fr = None):
+ self.want = want
+ self.form = form
+ self.seld = seld
+ self.fr = fr
+
+ def __repr__(self):
+ s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
+ if self.fr:
+ s = s + ", %s)" % `self.fr`
+ else:
+ s = s + ")"
return s
+
def __aepack__(self):
- f = StringIO.StringIO()
- putlong(f, len(self.dict))
- putlong(f, 0)
- for key, value in self.dict.items():
- putcode(f, key)
- desc = pack(value)
- putcode(f, desc.type)
- data = desc.data
- putlong(f, len(data))
- f.write(data)
- return AE.AECreateDesc('obj ', f.getvalue())
-
-def nicewant(want):
- if type(want) == types.TupleType and len(want) == 2:
- return reallynicewant(want)
- else:
- return `want`
-
-def reallynicewant((t, w)):
- if t != 'type': return `t, w`
- # These should be taken from the "elements" of the 'aete' resource
- if w == 'cins': return 'insertion point'
- if w == 'cha ': return 'character'
- if w == 'word': return 'word'
- if w == 'para': return 'paragraph'
- if w == 'ccel': return 'cell'
- if w == 'ccol': return 'column'
- if w == 'crow': return 'row'
- if w == 'crng': return 'range'
- if w == 'wind': return 'window'
- if w == 'docu': return 'document'
- return `w`
-
-def niceform(form):
- if type(form) == types.TupleType and len(form) == 2:
- return reallyniceform(form)
- else:
- return `form`
-
-def reallyniceform((t, f)):
- if t <> 'enum': return `t, f`
- if f == 'indx': return ''
- if f == 'name': return ''
- if f == 'rele': return ''
- return `f`
-
-def niceseld(seld):
- if type(seld) == types.TupleType and len(seld) == 2:
- return reallyniceseld(seld)
- else:
- return `seld`
+ return pack({'want': mktype(self.want),
+ 'form': mkenum(self.form),
+ 'seld': self.seld,
+ 'from': self.fr},
+ 'obj ')
-def reallyniceseld((t, s)):
- if t == 'long': return `s`
- if t == 'TEXT': return `s`
- if t == 'enum':
- if s == 'next': return 'after'
- if s == 'prev': return 'before'
- return `t, s`
-
-def unpackobject(data):
- f = StringIO.StringIO(data)
- nkey = getlong(f)
- dumm = getlong(f)
- dict = {}
- for i in range(nkey):
- keyw = getcode(f)
- type = getcode(f)
- size = getlong(f)
- if size:
- data = f.read(size)
+
+def IsObjectSpecifier(x):
+ return IsInstance(x, ObjectSpecifier)
+
+
+class Property(ObjectSpecifier):
+
+ def __init__(self, which, fr = None):
+ ObjectSpecifier.__init__(self, 'prop', 'prop', mkenum(which), fr)
+
+ def __repr__(self):
+ if self.fr:
+ return "Property(%s, %s)" % (`self.seld.enum`, `self.fr`)
else:
- data = ''
- desc = AE.AECreateDesc(type, data)
- dict[keyw] = unpack(desc)
- return Object(dict)
-
-
-# --- get various data types from a "file"
-
-def getword(f, *args):
- getalgn(f)
- s = f.read(2)
- if len(s) < 2:
- raise EOFError, 'in getword' + str(args)
- return (ord(s[0])<<8) | ord(s[1])
-
-def getlong(f, *args):
- getalgn(f)
- s = f.read(4)
- if len(s) < 4:
- raise EOFError, 'in getlong' + str(args)
- return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+ return "Property(%s)" % `self.seld.enum`
+
+ def __str__(self):
+ if self.fr:
+ return "Property %s of %s" % (str(self.seld), str(self.fr))
+ else:
+ return "Property %s" % str(self.seld)
+
+
+class SelectableItem(ObjectSpecifier):
+
+ def __init__(self, want, seld, fr = None):
+ t = type(seld)
+ if t == StringType:
+ form = 'name'
+ elif IsRange(seld):
+ form = 'rang'
+ elif IsComparison(seld) or IsLogical(seld):
+ form = 'test'
+ else:
+ form = 'indx'
+ ObjectSpecifier.__init__(self, want, form, seld, fr)
-def getcode(f, *args):
- getalgn(f)
- s = f.read(4)
- if len(s) < 4:
- raise EOFError, 'in getcode' + str(args)
- return s
-
-def getpstr(f, *args):
- c = f.read(1)
- if len(c) < 1:
- raise EOFError, 'in getpstr[1]' + str(args)
- nbytes = ord(c)
- if nbytes == 0: return ''
- s = f.read(nbytes)
- if len(s) < nbytes:
- raise EOFError, 'in getpstr[2]' + str(args)
- return s
-
-def getalgn(f):
- if f.tell() & 1:
- c = f.read(1)
- ##if c <> '\0':
- ## print 'align:', `c`
-
-# ---- end get routines
-
-
-# ---- put various data types to a "file"
-
-def putlong(f, value):
- putalgn(f)
- f.write(chr((value>>24)&0xff))
- f.write(chr((value>>16)&0xff))
- f.write(chr((value>>8)&0xff))
- f.write(chr(value&0xff))
-
-def putword(f, value):
- putalgn(f)
- f.write(chr((value>>8)&0xff))
- f.write(chr(value&0xff))
-
-def putcode(f, value):
- if type(value) != types.StringType or len(value) != 4:
- raise TypeError, "ostype must be 4-char string"
- putalgn(f)
- f.write(value)
-
-def putpstr(f, value):
- if type(value) != types.StringType or len(value) > 255:
- raise TypeError, "pstr must be string <= 255 chars"
- f.write(chr(len(value)) + value)
-
-def putalgn(f):
- if f.tell() & 1:
- f.write('\0')
-
-# ---- end put routines
+class ComponentItem(SelectableItem):
+ # Derived classes *must* set the *class attribute* 'want' to some constant
+
+ def __init__(self, which, fr = None):
+ SelectableItem.__init__(self, self.want, which, fr)
+
+ def __repr__(self):
+ if not self.fr:
+ return "%s(%s)" % (self.__class__.__name__, `self.seld`)
+ return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`)
+
+ def __str__(self):
+ seld = self.seld
+ if type(seld) == StringType:
+ ss = repr(seld)
+ elif IsRange(seld):
+ start, stop = seld.start, seld.stop
+ if type(start) == InstanceType == type(stop) and \
+ start.__class__ == self.__class__ == stop.__class__:
+ ss = str(start.seld) + " thru " + str(stop.seld)
+ else:
+ ss = str(seld)
+ else:
+ ss = str(seld)
+ s = "%s %s" % (self.__class__.__name__, ss)
+ if self.fr: s = s + " of %s" % str(self.fr)
+ return s
+
+
+template = """
+class %s(ComponentItem): want = '%s'
+"""
+
+exec template % ("Text", 'text')
+exec template % ("Character", 'cha ')
+exec template % ("Word", 'cwor')
+exec template % ("Line", 'clin')
+exec template % ("Paragraph", 'cpar')
+exec template % ("Window", 'cwin')
+exec template % ("Document", 'docu')
+exec template % ("File", 'file')
+exec template % ("InsertionPoint", 'cins')
+
+
+def mkobject(dict):
+ want = dict['want'].type
+ form = dict['form'].enum
+ seld = dict['seld']
+ fr = dict['from']
+ if form in ('name', 'indx', 'rang', 'test'):
+ if want == 'text': return Text(seld, fr)
+ if want == 'cha ': return Character(seld, fr)
+ if want == 'cwor': return Word(seld, fr)
+ if want == 'clin': return Line(seld, fr)
+ if want == 'cpar': return Paragraph(seld, fr)
+ if want == 'cwin': return Window(seld, fr)
+ if want == 'docu': return Document(seld, fr)
+ if want == 'file': return File(seld, fr)
+ if want == 'cins': return InsertionPoint(seld, fr)
+ if want == 'prop' and form == 'prop' and IsType(seld):
+ return Property(seld.type, fr)
+ return ObjectSpecifier(want, form, seld, fr)
+
+
+# Special code to unpack an AppleEvent (which is *not* a disguised record!)
aekeywords = [
'tran',
@@ -287,10 +529,22 @@ def packevent(ae, parameters = {}, attributes = {}):
for key, value in attributes.items():
ae.AEPutAttributeDesc(key, pack(value))
+
+# Test program
+
def test():
target = AE.AECreateDesc('sign', 'KAHL')
ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
print unpackevent(ae)
+ raw_input(":")
+ ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
+ obj = Character(2, Word(1, Document(1)))
+ print obj
+ print repr(obj)
+ packevent(ae, {'----': obj})
+ params, attrs = unpackevent(ae)
+ print params['----']
+ raw_input(":")
if __name__ == '__main__':
test()