diff options
author | Guido van Rossum <guido@python.org> | 1995-02-05 16:59:27 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1995-02-05 16:59:27 (GMT) |
commit | 315592383e7fd02a73806b986e1038c7daf66777 (patch) | |
tree | bf4327f9ba4f15d7700d55c8cbab4ecb2f1cfbe3 /Mac | |
parent | b19a645b69355bd4ccd6d6618cd00e4047996bbc (diff) | |
download | cpython-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.py | 119 | ||||
-rw-r--r-- | Mac/Lib/toolbox/aetools.py | 608 |
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() |