[Python-checkins] CVS: python/dist/src/Mac/Unsupported/PythonScript PyScriptTest.py,NONE,1.1 PythonScript.py,NONE,1.1 ReadMe.txt,NONE,1.1 baepack.py,NONE,1.1 baetools.py,NONE,1.1 baetypes.py,NONE,1.1 getaete.py,NONE,1.1 printaete.py,NONE,1.1 testeudora.py,NONE,1.1

Jack Jansen jackjansen@users.sourceforge.net
Fri, 26 Oct 2001 05:55:42 -0700


Update of /cvsroot/python/python/dist/src/Mac/Unsupported/PythonScript
In directory usw-pr-cvs1:/tmp/cvs-serv2683/Unsupported/PythonScript

Added Files:
	PyScriptTest.py PythonScript.py ReadMe.txt baepack.py 
	baetools.py baetypes.py getaete.py printaete.py testeudora.py 
Log Message:
Moved PythonScript to unsupported at Bill Bedford's request. It'll go
away completely next release, unless someone complains.


--- NEW FILE: PyScriptTest.py ---
'''
Minimal test module
'''#

import sys
import PythonScript

SIGNATURE = 'MACS'
TIMEOUT = 10*60*60

PythonScript.PsScript(SIGNATURE, TIMEOUT)
p = PythonScript.PyScript
ev = PythonScript.PsEvents
pc = PythonScript.PsClass
pp = PythonScript.PsProperties

startup = str(p(ev.Get, pc.Desktopobject(1).Startup_disk().Name()))
print 'startup',startup, type(startup)
print p(ev.Get, pc.Disk(startup).Folder(7).File(1).Name())
print p(ev.Get, pc.Disk(1).Name())
print p(ev.Get, pc.Disk('every').Name())
print p(ev.Make, None, New='Alias_file', At=pp.Desktop(''), To=pp.System_folder(1))

sys.exit(1)
	

--- NEW FILE: PythonScript.py ---
"""
Python script a module to comunicate with apple events

v 0.1a2
v.0.2 16 april 1998 


"""
import sys
import getaete
import baetools
import baetypes
from Carbon import AE
import AppleEvents
import macfs
from types import *
#from aetypes import InstanceType
from aepack import AEDescType

ordinal = {
'every': 'all ',
'first' : 'firs',
'last' : 'last',
'any' : 'any ',
'middle' : 'midd'}


Error = 'PythonScript.Error'


class PsEvents:
	pass


class PsClasses:

	
	def __getattr__(self, name):
		try:
			return DCItem(name, self)
		except:
			pass
		
	def __repr__(self):
		if self.form != 'prop':
			t = type(self.seld)
			if t == StringType:
				self.form = 'name'
			elif baetypes.IsRange(self.seld):
				self.form = 'rang'
			elif baetypes.IsComparison(self.seld) or baetypes.IsLogical(self.seld):
				self.form = 'test'
			elif t == TupleType:
			# Breakout: specify both form and seld in a tuple
			# (if you want ID or rele or somesuch)
				self.form, self.seld = self.seld
			elif t == IntType:
				self.form = 'indx'
			else:
				pass

		if self.seld in ordinal.keys():
			self.seld = baetypes.Ordinal(ordinal[self.seld])
			self.form = 'indx'

		s = "baetypes.ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
		if `self.fr`:
			s = s + ", %s)" % `self.fr`
		else:
			s = s + ")"
		return s
		
	def __str__(self):
		return self.want
		
		
def template(self, seld=None, fr=None):
	self.seld = seld
	self.fr = fr
		
def template1(self, which, fr=None):
	self.want = 'prop'
	self.form = 'prop'
	self.fr = fr
	
class DCItem:
	def __init__(self, comp, fr):
		self.compclass = comp
		self.fr = fr
		
	def __call__(self, which=None):
		if which:
			self.compclass = eval('PsClass.%s' % self.compclass)
		else:
			try:
				self.compclass = eval('PsProperties.%s' % self.compclass)
			except AttributeError:
				self.compclass = eval('PsClass.%s' % self.compclass)
		return self.compclass(which, self.fr)

class PsClass:
	pass

class PsProperties:
	pass				


class PsEnumerations:
	pass

			
def PsScript(sig=None, Timeout=0, Ignoring=0):
	elements = {}
	if sig:
		target, sig = Signature(sig)
		pyscript = getaete.Getaete(sig)
	else:
		target, sig = Signature('Pyth')
		pyscript = getaete.Getaete()
	setattr(PyScript, 'timeout', Timeout)
	setattr(PyScript, 'ignoring', Ignoring)
	setattr(PyScript, 'target', target)
	for key, value in pyscript[0].items():
		setattr(PsEvents, key, value)
	for key, value in pyscript[1].items():
		CreateClass(key, 'PsClasses', value)
		for val in value[2]:
			CreateProperty(val[0], 'PsClasses', `val[1]`)
			
		if value[3]:
			for val in value[3]:
				if val[0] not in elements.keys():
					elements[val[0]] = val[1]
				elif len(val[1]) > len(elements[val[0]]):
					elements[val[0]] = val[1]

	for key, value in pyscript[2].items():
		for val in value:
			setattr(PsEnumerations, val[0], val[1])

def CreateClass(newClassName, superClassName, value):
	parentDict = PsClass.__dict__
	exec "class %s(%s): pass" % (newClassName, superClassName) in \
			globals(), parentDict
	newClassObj = parentDict[newClassName]
	newClassObj.__init__ = template
	exec "setattr(newClassObj, 'want', %s)" % `value[0]`
	if value[2] and value[2][0][0] == 'every':
		exec "setattr(newClassObj, 'plur', 1)"

def CreateProperty(newClassName, superClassName, value):
	parentDict = PsProperties.__dict__
	exec "class %s(%s): pass" % (newClassName, superClassName) in \
			globals(), parentDict
	newClassObj = parentDict[newClassName]
	if newClassName == 'Every':
		value = "baetypes.mkOrdinal('every')"
	newClassObj.__init__ = template1
	exec "setattr(newClassObj, 'seld', %s)" % value

def Signature(signature):
	if type(signature) == AEDescType:
		target = signature
	elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
		target = signature.__aepack__()
	elif type(signature) == StringType:
		if len(signature) == 4:
			target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
			target_signature = signature
		else:
			#This should ready be made persistant, so PythonScript 'remembered' where applications were
			fss, ok = macfs.PromptGetFile('Find the aplication %s' % signature, 'APPL')
			if ok:
				target_signature = fss.GetCreatorType()[0]
				target = AE.AECreateDesc(AppleEvents.typeApplSignature, target_signature)
	else:
		raise TypeError, "signature should be 4-char string or AEDesc"
	return target, target_signature

		

		
class PyScript(PsEvents):
	def __init__(self, name, obj=None,  **args):
		desc, code, subcode, rply, message, keywds = name
#		print 'code', code
#		print 'subcode', subcode
#		print 'rply', rply
#		print 'message', message
#		print 'keywds', keywds
#		print 'name', name
#		print 'obj', obj
#		print 'args', args
		self.code = code
		self.subcode = subcode
		self.attributes ={}
		self.arguments = {}
		if keywds:
			self.arguments = self.keyargs(keywds, args)
		self.arguments['----'] = self.keyfms(message[0], obj)

		##XXXX Eudora needs this XXXX##
		if self.arguments['----'] == None:
			del self.arguments['----']
#		print 'arguments', self.arguments
		if self.ignoring or rply[0] == 'null':
			self.send_flags = AppleEvents.kAENoReply
		else:
			self.send_flags = AppleEvents.kAEWaitReply
		self.send_priority = AppleEvents.kAENormalPriority	
		if self.timeout:
			self.send_timeout = self.timeout
		else:
			self.send_timeout = AppleEvents.kAEDefaultTimeout


	def keyargs(self, ats, args):
#		print 'keyargs', ats, args
		output = {}
		for arg in args.keys():
			for at in ats:
				if at[0] == arg:
					output[at[1]] = self.keyfms(at[2][0], args[arg])
		return output
				
	def keyfms(self, key, value):
#		print 'keyfms', 'key', key, `value`
		if key == 'obj ' or key == 'insl':
			return eval(`value`)
		elif key == 'TEXT':
			return value
		elif key == 'null':
			return 
		elif key == 'bool':
			return baetypes.mkboolean(value)
		elif key == 'type':
			try:
				val = eval('PsClass.%s()' % value)
				return baetypes.mktype(str(val))
			except:
				return baetypes.mktype(value)
		else:
			print "I don't know what to put here -- script.keyargs"
			print key, `value`
			sys.exit[1]
		
	def newevent(self, code, subcode, parameters = {}, attributes = {}):
		"""Create a complete structure for an apple event"""
#		print code, subcode, parameters, attributes
		event = AE.AECreateAppleEvent(code, subcode, self.target,
			  	  AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
		baetools.packevent(event, parameters, attributes)
		return event
		
	def sendevent(self, event):
		"""Send a pre-created appleevent, await the reply and unpack it"""
		
		reply = event.AESend(self.send_flags, self.send_priority,
							  self.send_timeout)
		parameters, attributes = baetools.unpackevent(reply)
		return reply, parameters, attributes
		
	def send(self, code, subcode, parameters = {}, attributes = {}):
		"""Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
#		print code, subcode, parameters, attributes
		return self.sendevent(self.newevent(code, subcode, parameters, attributes))
		
	def __str__(self):
		_reply, _arguments, _attributes = self.send(self.code, self.subcode, self.arguments, self.attributes)

		if _arguments.has_key('errn'):
			raise baetools.Error, baetools.decodeerror(_arguments)
		# XXXX Optionally decode result
		if _arguments.has_key('----'):
			return str(_arguments['----'])
		else:
			return 

	
	
def test():
	Simp = 'Hermit:Applications:SimpleText'
	PsScript('MACS', Timeout=60*60*3)
#	PsScript('CSOm', Timeout=60*60*3)
#	PsScript('', Timeout=60*60*3)
#	PyScript('macsoup')
	ev = PsEvents
	ps = PsClass
#	print PsProperties.__dict__
#	y = script(ev.Open, File('Hermit:Desktop Folder:Lincolnshire Imp'), using=Application_file(Simp))
#	print baetypes.NProperty('prop', 'prop', 'pnam',  baetypes.ObjectSpecifier('cdis', 'indx', 1, None))
#	y = PyScript(ev.Get, Disk("Hermit").Folder(7).File(1).Name())
#	y = PyScript(ev.Get, Disk("Hermit").Size(), As='Integer')
#	y = PyScript(ev.Get, ps.Desktopobject(1).Startup_disk())
#	y = PyScript(ev.Get, Mailbox(1).File(), as='TEXT')
#	print 'y', y, type(y)

if __name__ == '__main__':
	test()
#	sys.exit(1)
	

--- NEW FILE: ReadMe.txt ---

PythonScript
------------
v0.5 beta 1 24/04/98

author: Bill Bedford, <billb@mousa.demon.co.uk>

This suite of modules is a first attempt at writing a more user friendly 
python/appleevent interface. The files in the suite are:

PythonScript
------------

Loads three dictionaries generated by getaete into __dict__'s of three 
classes and thus gives us direct assess to all the methods in the aete.  
Each method now contains all the information needed to build apple events.

The general usage is

>>>PythonScript.PsScript(SIGNATURE, TIMEOUT, IGNORING) 

where 
SIGNATURE is the target application
TIMEOUT is in ticks
and IGNORING is a boolean and determines whether the script waits for a reply 
from the target before going on to the next event 

>>>PythonScript.PyScript(Event, Object, keywdarg1..., keywdarg2...etc)

Object is a appleevent object specifier and is of the form

PythonScript.PsClass.Class1(arg).Class2(arg)ƒ.Property()

All applescript event, class and property names are capitalised to 
distinguish them from python methods.

getaete
-------

Reads the aete of the target application and returns it as a list of three 
dictionaries, which represent all the events, properties and enumeration in 
the aete.  (the fourth dictionary, comparisons, has never been implemented 
in applescript so I have not used it) It also reads the applescript aeut 
and adds any suites that are missing (ie where the application author has 
set his suite to inherit from the aeut.)  and the applescript suite, which 
gives the language methods

printaete
---------

Produces a text file with the aete set out in a human readable form, 
similar to the Open Dictionary command in the applescript editor.


baetools, baepack, baetypes
---------------------------

These are direct equivalents of aetools, aepack, aetypes in the standard 
distribution.  Some methods and classes have been enhanced others are 
redundant

PyScriptTest, testeudora
------------------------

A couple of test scripts.  Testeudora is an updated version of the one in 
the standard distribution.
	 
	 
	 
	 
	 
Still To Do (in no particular order)
-----------

These modules are much slower than applescript.  I believe they could be 
made faster by rewriting the aete parser in getaete and/or by putting in 
some form of persistent storage so that the class dictionaries can be cached.
	 
The parsing of the appleevent replies need rewriting.

Support for the use of scripting additions.

A Python aeut needs to be written, much of the applescript one is redundant 
in python.

Probably a few other things I haven't thought of yet.

--- NEW FILE: baepack.py ---
"""Tools for use in AppleEvent clients and servers:
conversion between AE types and python types

pack(x) converts a Python object to an AEDesc object
unpack(desc) does the reverse
coerce(x, wanted_sample) coerces a python object to another python object
"""

#
# This code was originally written by Guido, and modified/extended by Jack
# to include the various types that were missing. The reference used is
# Apple Event Registry, chapter 9.
#

import struct
import string
import types
from string import strip
from types import *
from Carbon import AE
from Carbon.AppleEvents import *
import MacOS
import macfs
import StringIO
import baetypes
from baetypes import mkenum, mktype

import calldll

OSL = calldll.getlibrary('ObjectSupportLib')

# These ones seem to be missing from AppleEvents
# (they're in AERegistry.h)

#typeColorTable = 'clrt'
#typeDrawingArea = 'cdrw'
#typePixelMap = 'cpix'
#typePixelMapMinus = 'tpmm'
#typeRotation = 'trot'
#typeTextStyles = 'tsty'
#typeStyledText = 'STXT'
#typeAEText = 'tTXT'
#typeEnumeration = 'enum'

#
# Some AE types are immedeately coerced into something
# we like better (and which is equivalent)
#
unpacker_coercions = {
	typeComp : typeExtended,
	typeColorTable : typeAEList,
	typeDrawingArea : typeAERecord,
	typeFixed : typeExtended,
	typeFloat : typeExtended,
	typePixelMap : typeAERecord,
	typeRotation : typeAERecord,
	typeStyledText : typeAERecord,
	typeTextStyles : typeAERecord,
};

#
# Some python types we need in the packer:
#
AEDescType = type(AE.AECreateDesc('TEXT', ''))
_sample_fss = macfs.FSSpec(':')
_sample_alias = _sample_fss.NewAliasMinimal()
FSSType = type(_sample_fss)
AliasType = type(_sample_alias)

def pack(x, forcetype = None):
	"""Pack a python object into an AE descriptor"""
#	print 'aepack', x, type(x), forcetype
#	if type(x) == TupleType:
#		forcetype, x = x
	if forcetype:
		print x, 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 == FSSType:
		return AE.AECreateDesc('fss ', x.data)
	if t == AliasType:
		return AE.AECreateDesc('alis', x.data)
	if t == IntType:
		return AE.AECreateDesc('long', struct.pack('l', x))
	if t == FloatType:
		#
		# XXXX (note by Guido) 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
		# XXXX (note by Jack) haven't checked this under MW
		#
#		return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
		return AE.AECreateDesc('exte', struct.pack('d', x))
	if t == StringType:
		return AE.AECreateDesc('TEXT', x)
	if t == ListType:
		list = AE.AECreateList('', 0)
		for item in x:
			list.AEPutDesc(0, pack(item))
		return list
	if t == DictionaryType:
		record = AE.AECreateList('', 1)
		for key, value in x.items():
			record.AEPutParamDesc(key, pack(value))
		return record
	if t == InstanceType and hasattr(x, '__aepack__'):
		return x.__aepack__()
	return AE.AECreateDesc('TEXT', repr(x)) # Copout

def unpack(desc):
	"""Unpack an AE descriptor to a python object"""
	t = desc.type
#	print t
	
	if unpacker_coercions.has_key(t):
		desc = desc.AECoerceDesc(unpacker_coercions[t])
		t = desc.type # This is a guess by Jack....
	
	if t == typeAEList:
		l = []
		for i in range(desc.AECountItems()):
			keyword, item = desc.AEGetNthDesc(i+1, '****')
			l.append(unpack(item))
		return l
	if t == typeAERecord:
		d = {}
		for i in range(desc.AECountItems()):
			keyword, item = desc.AEGetNthDesc(i+1, '****')
			d[keyword] = unpack(item)
		return d
	if t == typeAEText:
		record = desc.AECoerceDesc('reco')
		return mkaetext(unpack(record))
	if t == typeAlias:
		return macfs.RawAlias(desc.data)
	# typeAppleEvent returned as unknown
	if t == typeBoolean:
		return struct.unpack('b', desc.data)[0]
	if t == typeChar:
		return desc.data
	# typeColorTable coerced to typeAEList
	# typeComp coerced to extended
	# typeData returned as unknown
	# typeDrawingArea coerced to typeAERecord
	if t == typeEnumeration:
		return mkenum(desc.data)
	# typeEPS returned as unknown
	if t == typeExtended:
#		print desc, type(desc), len(desc)
		data = desc.data
#		print `data[:8]`, type(data), len(data[:8])
#		print struct.unpack('=d', data[:8])[0]
#		print string.atoi(data), type(data), len(data)
#		print struct.calcsize(data)
		# XXX See corresponding note for pack()
#		return struct.unpack('d', data[:2] + data)[0]
		return struct.unpack('d', data[:8])[0]
	if t == typeFalse:
		return 0
	# typeFixed coerced to extended
	# typeFloat coerced to extended
	if t == typeFSS:
		return macfs.RawFSSpec(desc.data)
	if t == typeInsertionLoc:
		record = desc.AECoerceDesc('reco')
		return mkinsertionloc(unpack(record))
	# typeInteger equal to typeLongInteger
	if t == typeIntlText:
		script, language = struct.unpack('hh', desc.data[:4])
		return baetypes.IntlText(script, language, desc.data[4:])
	if t == typeIntlWritingCode:
		script, language = struct.unpack('hh', desc.data)
		return baetypes.IntlWritingCode(script, language)
	if t == typeKeyword:
		return mkkeyword(desc.data)
	# typeLongFloat is equal to typeFloat
	if t == typeLongInteger:
#		print t, struct.unpack('l', desc.data)
		return struct.unpack('l', desc.data)[0]
	if t == typeNull:
		return None
	if t == typeMagnitude:
		v = struct.unpack('l', desc.data)
		if v < 0:
			v = 0x100000000L + v
		return v
	if t == typeObjectSpecifier:
		from Carbon import Res
#		print desc, type(desc)
#		print desc.__members__
#		print desc.data, desc.type
#		print unpack(desc)
#		getOSL = calldll.newcall(OSL.AEResolve, 'OSErr', 'InHandle', 'InShort')#, 'InString')
#		print 'OSL', getOSL(rdesc, 0)#, desc.data)
		record = desc.AECoerceDesc('reco')
#		print record
		return mkobject(unpack(record))
	# typePict returned as unknown
	# typePixelMap coerced to typeAERecord
	# typePixelMapMinus returned as unknown
	# typeProcessSerialNumber returned as unknown
	if t == typeQDPoint:
		v, h = struct.unpack('hh', desc.data)
		return baetypes.QDPoint(v, h)
	if t == typeQDRectangle:
		v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
		return baetypes.QDRectangle(v0, h0, v1, h1)
	if t == typeRGBColor:
		r, g, b = struct.unpack('hhh', desc.data)
		return baetypes.RGBColor(r, g, b)
	# typeRotation coerced to typeAERecord
	# typeScrapStyles returned as unknown
	# typeSessionID returned as unknown
	if t == typeShortFloat:
		return struct.unpack('f', desc.data)[0]
	if t == typeShortInteger:
#		print t, desc.data
#		print struct.unpack('h', desc.data)[0]
		return struct.unpack('h', desc.data)[0]
	# typeSMFloat identical to typeShortFloat
	# typeSMInt	indetical to typeShortInt
	# typeStyledText coerced to typeAERecord
	if t == typeTargetID:
		return mktargetid(desc.data)
	# typeTextStyles coerced to typeAERecord
	# typeTIFF returned as unknown
	if t == typeTrue:
		return 1
	if t == typeType:
#		print t, desc.data
		return mktype(desc.data)
	#
	# The following are special
	#
	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))
	return mkunknown(desc.type, desc.data)
	
def coerce(data, egdata):
	"""Coerce a python object to another type using the AE coercers"""
	pdata = pack(data)
	pegdata = pack(egdata)
	pdata = pdata.AECoerceDesc(pegdata.type)
	return unpack(pdata)

#
# Helper routines for unpack
#
def mktargetid(data):
	sessionID = getlong(data[:4])
	name = mkppcportrec(data[4:4+72])
	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 mkunknown(type, data):
	return baetypes.Unknown(type, data)

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 mkkeyword(keyword):
	return baetypes.Keyword(keyword)

def mkrange(dict):
	return baetypes.Range(dict['star'], dict['stop'])

def mkcomparison(dict):
	return baetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])

def mklogical(dict):
	return baetypes.Logical(dict['logc'], dict['term'])

def mkstyledtext(dict):
	return baetypes.StyledText(dict['ksty'], dict['ktxt'])
	
def mkaetext(dict):
	return baetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
	
def mkinsertionloc(dict):
	return baetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])

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 baetypes.Text(seld, fr)
		if want == 'cha ': return baetypes.Character(seld, fr)
		if want == 'cwor': return baetypes.Word(seld, fr)
		if want == 'clin': return baetypes.Line(seld, fr)
		if want == 'cpar': return baetypes.Paragraph(seld, fr)
		if want == 'cwin': return baetypes.Window(seld, fr)
		if want == 'docu': return baetypes.Document(seld, fr)
		if want == 'file': return baetypes.File(seld, fr)
		if want == 'cins': return baetypes.InsertionPoint(seld, fr)
	if want == 'prop' and form == 'prop' and baetypes.IsType(seld):
		return baetypes.Property(seld.type, fr)
	return baetypes.ObjectSpecifier(want, form, seld, fr)

def _test():
	"""Test program. Pack and unpack various things"""
	objs = [
		'a string',
		12,
		12.0,
		None,
		['a', 'list', 'of', 'strings'],
		{'key1': 'value1', 'key2':'value2'},
		macfs.FSSpec(':'),
		macfs.FSSpec(':').NewAliasMinimal(),
		baetypes.Enum('enum'),
		baetypes.Type('type'),
		baetypes.Keyword('kwrd'),
		baetypes.Range(1, 10),
		baetypes.Comparison(1, '<   ', 10),
		baetypes.Logical('not ', 1),
		# Cannot do StyledText
		# Cannot do AEText
		baetypes.IntlText(0, 0, 'international text'),
		baetypes.IntlWritingCode(0,0),
		baetypes.QDPoint(50,100),
		baetypes.QDRectangle(50,100,150,200),
		baetypes.RGBColor(0x7000, 0x6000, 0x5000),
		baetypes.Unknown('xxxx', 'unknown type data'),
		baetypes.Character(1),
		baetypes.Character(2, baetypes.Line(2)),
	]
	for o in objs:
		print 'BEFORE', o, `o`
		print type(o)
		packed = pack(o)
		unpacked = unpack(packed)
		print 'AFTER ', unpacked, `unpacked`
	import sys
	sys.exit(1)
	
if __name__ == '__main__':
	_test()
	

--- NEW FILE: baetools.py ---
"""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"

Some of the stuff that appears to be exported from this module comes from other
files: the pack stuff from aepack, the objects from aetypes.

"""

from types import *
from Carbon import AE
from Carbon import AppleEvents
import MacOS
import sys

from baetypes import *
from baepack import pack, unpack, coerce, AEDescType

Error = 'baetools.Error'

# Special code to unpack an AppleEvent (which is *not* a disguised record!)
# Note by Jack: No??!? If I read the docs correctly it *is*....

aekeywords = [
	'tran',
	'rtid',
	'evcl',
	'evid',
	'addr',
	'optk',
	'timo',
	'inte',	# this attribute is read only - will be set in AESend
	'esrc',	# this attribute is read only
	'miss',	# this attribute is read only
	'from'	# new in 1.0.1
]

def missed(ae):
	try:
		desc = ae.AEGetAttributeDesc('miss', 'keyw')
	except AE.Error, msg:
		return None
	return desc.data

def unpackevent(ae):
	parameters = {}
	while 1:
		key = missed(ae)
		if not key: break
		parameters[key] = unpack(ae.AEGetParamDesc(key, '****'))
	attributes = {}
	for key in aekeywords:
		try:
			desc = ae.AEGetAttributeDesc(key, '****')
		except (AE.Error, MacOS.Error), msg:
			if msg[0] != -1701 and msg[0] != -1704:
				raise sys.exc_type, sys.exc_value
			continue
		attributes[key] = unpack(desc)
	return parameters, attributes

def packevent(ae, parameters = {}, attributes = {}):
	for key, value in parameters.items():
		ae.AEPutParamDesc(key, pack(value))
	for key, value in attributes.items():
		ae.AEPutAttributeDesc(key, pack(value))

#
# Support routine for automatically generated Suite interfaces
# These routines are also useable for the reverse function.
#
def keysubst(arguments, keydict):
	"""Replace long name keys by their 4-char counterparts, and check"""
	ok = keydict.values()
	for k in arguments.keys():
		if keydict.has_key(k):
			v = arguments[k]
			del arguments[k]
			arguments[keydict[k]] = v
		elif k != '----' and k not in ok:
			raise TypeError, 'Unknown keyword argument: %s'%k
			
def enumsubst(arguments, key, edict):
	"""Substitute a single enum keyword argument, if it occurs"""
	if not arguments.has_key(key):
		return
	v = arguments[key]
	ok = edict.values()
	if edict.has_key(v):
		arguments[key] = edict[v]
	elif not v in ok:
		raise TypeError, 'Unknown enumerator: %s'%v
		
def decodeerror(arguments):
	"""Create the 'best' argument for a raise MacOS.Error"""
	errn = arguments['errn']
	err_a1 = errn
	if arguments.has_key('errs'):
		err_a2 = arguments['errs']
	else:
		err_a2 = MacOS.GetErrorString(errn)
	if arguments.has_key('erob'):
		err_a3 = arguments['erob']
	else:
		err_a3 = None
	
	return (err_a1, err_a2, err_a3)

class TalkTo:
	"""An AE connection to an application"""
	
	def __init__(self, signature, start=0, timeout=0):
		"""Create a communication channel with a particular application.		
		Addressing the application is done by specifying either a
		4-byte signature, an AEDesc or an object that will __aepack__
		to an AEDesc.
		"""
		self.target_signature = None
		if type(signature) == AEDescType:
			self.target = signature
		elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
			self.target = signature.__aepack__()
		elif type(signature) == StringType and len(signature) == 4:
			self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
			self.target_signature = signature
		else:
			raise TypeError, "signature should be 4-char string or AEDesc"
		self.send_flags = AppleEvents.kAEWaitReply
		self.send_priority = AppleEvents.kAENormalPriority
		if timeout:
			self.send_timeout = timeout
		else:
			self.send_timeout = AppleEvents.kAEDefaultTimeout
		if start:
			self.start()
		
	def start(self):
		"""Start the application, if it is not running yet"""
		self.send_flags = AppleEvents.kAENoReply
		_launch(self.target_signature)
			
	def newevent(self, code, subcode, parameters = {}, attributes = {}):
		"""Create a complete structure for an apple event"""
		event = AE.AECreateAppleEvent(code, subcode, self.target,
		      	  AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
#		print parameters, attributes
		packevent(event, parameters, attributes)
		return event
	
	def sendevent(self, event):
		"""Send a pre-created appleevent, await the reply and unpack it"""
		
		reply = event.AESend(self.send_flags, self.send_priority,
		                          self.send_timeout)
		parameters, attributes = unpackevent(reply)
		return reply, parameters, attributes
		
	def send(self, code, subcode, parameters = {}, attributes = {}):
		"""Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
		return self.sendevent(self.newevent(code, subcode, parameters, attributes))
	
	#
	# The following events are somehow "standard" and don't seem to appear in any
	# suite...
	#
	def activate(self):
		"""Send 'activate' command"""
		self.send('misc', 'actv')

	def _get(self, _object, as=None, _attributes={}):
		"""_get: get data from an object
		Required argument: the object
		Keyword argument _attributes: AppleEvent attribute dictionary
		Returns: the data
		"""
		_code = 'core'
		_subcode = 'getd'

		_arguments = {'----':_object}
		if as:
			_arguments['rtyp'] = mktype(as)

		_reply, _arguments, _attributes = self.send(_code, _subcode,
				_arguments, _attributes)
		if _arguments.has_key('errn'):
			raise Error, decodeerror(_arguments)

		if _arguments.has_key('----'):
			return _arguments['----']

# Tiny Finder class, for local use only

class _miniFinder(TalkTo):
	def open(self, _object, _attributes={}, **_arguments):
		"""open: Open the specified object(s)
		Required argument: list of objects to open
		Keyword argument _attributes: AppleEvent attribute dictionary
		"""
		_code = 'aevt'
		_subcode = 'odoc'

		if _arguments: raise TypeError, 'No optional args expected'
		_arguments['----'] = _object


		_reply, _arguments, _attributes = self.send(_code, _subcode,
				_arguments, _attributes)
		if _arguments.has_key('errn'):
			raise aetools.Error, aetools.decodeerror(_arguments)
		# XXXX Optionally decode result
		if _arguments.has_key('----'):
			return _arguments['----']
#pass
	
_finder = _miniFinder('MACS')

def _launch(appfile):
	"""Open a file thru the finder. Specify file by name or fsspec"""
	_finder.open(_application_file(('ID  ', appfile)))


class _application_file(ComponentItem):
	"""application file - An application's file on disk"""
	want = 'appf'
	
_application_file._propdict = {
}
_application_file._elemdict = {
}
	
# Test program
# XXXX Should test more, really...

def test():
	target = AE.AECreateDesc('sign', 'quil')
	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()
	sys.exit(1)

--- NEW FILE: baetypes.py ---
"""aetypes - Python objects representing various AE types."""

from Carbon.AppleEvents import *
import struct
from types import *
import string

#
# convoluted, since there are cyclic dependencies between this file and
# aetools_convert.
#
def pack(*args):
	from aepack import pack
	return apply(pack, args)
	
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):
	"""'nice' representation of an object"""
	if type(s) is StringType: return repr(s)
	else: return str(s)

class Unknown:
	"""An uninterpreted AE object"""
	
	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)

class Enum:
	"""An AE enumeration value"""
	
	def __init__(self, enum):
		self.enum = "%-4.4s" % str(enum)
	
	def __repr__(self):
		return "Enum(%s)" % `self.enum`
	
	def __str__(self):
		return string.strip(self.enum)
	
	def __aepack__(self):
		return pack(self.enum, typeEnumeration)

def IsEnum(x):
	return IsInstance(x, Enum)

def mkenum(enum):
#	print enum
	if IsEnum(enum): return enum
	return Enum(enum)

class Boolean:
	"""An AE boolean value"""
	
	def __init__(self, bool):
		if bool:
			self.bool = "%-4.4s" % str(typeTrue)
		else:
			self.bool = "%-4.4s" % str(typeFalse)
	
	def __repr__(self):
		return "Boolean(%s)" % self.bool
	
	def __str__(self):
		return self.bool
	
	def __aepack__(self):
		if self.bool == 'true':
			return pack('', typeTrue)
		else:
			return pack('', typeFalse)

def IsBoolean(x):
	return IsInstance(x, Boolean)

def mkboolean(bool):
#	print bool
	if IsBoolean(bool): return bool
	return Boolean(bool)

class Type:
	"""An AE 4-char typename object"""
	
	def __init__(self, _type):
		self.type = "%-4.4s" % str(_type)
		
	def __repr__(self):
		return "Type(%s)" % `self.type`
	
	def __str__(self):
		return string.strip(self.type)
	
	def __aepack__(self):
#		print self.type, typeType
		return pack(self.type, typeType)

def IsType(x):
	return IsInstance(x, Type)

def mktype(_type):
	# Should check for apple ID codes, will allow 
	if IsType(_type): return _type
	if type(_type) <> StringType: return _type
	if len(_type) <> 4: return Type(eval('type' + _type))
	return Type(_type)


class Keyword:
	"""An AE 4-char keyword object"""
	
	def __init__(self, keyword):
		self.keyword = "%-4.4s" % str(keyword)
	
	def __repr__(self):
		return "Keyword(%s)" % `self.keyword`
	
	def __str__(self):
		return string.strip(self.keyword)
	
	def __aepack__(self):
		return pack(self.keyword, typeKeyword)

def IsKeyword(x):
	return IsInstance(x, Keyword)

class Range:
	"""An AE range object"""
	
	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)

class Comparison:
	"""An AE Comparison"""
	
	def __init__(self, obj1, relo, obj2):
		self.obj1 = obj1
		self.relo = "%-4.4s" % str(relo)
		self.obj2 = obj2
	
	def __repr__(self):
		return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`)
	
	def __str__(self):
		return "%s %s %s" % (nice(self.obj1), string.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)
	
class NComparison(Comparison):
	# The class attribute 'relo' must be set in a subclass
	
	def __init__(self, obj1, obj2):
		Comparison.__init__(obj1, self.relo, obj2)

class Ordinal:
	"""An AE Ordinal"""
	
	def __init__(self, ord):
		self.ord = ord
	
	def __repr__(self):
		return  "baetypes.Ordinal(%s)" % `self.ord`
	
	def __str__(self):
		return "%s" % (string.strip(self.ord))
	
	def __aepack__(self):
		return pack(self.ord, typeAbsoluteOrdinal)

def IsOrdinal(x):
#	print 'IsOrdinal', x, IsInstance(x, Ordinal)
	return IsInstance(x, Ordinal)
	
def mkOrdinal(Ord):
	if IsOrdinal(Ord): return Ord
	return Ordinal(Ord)


	
class NOrdinal(Ordinal):
	# The class attribute 'abso' must be set in a subclass
	
	def __init__(self ):
#		print 'NOrdinal', self.abso
		Ordinal.__init__(self, self.abso)

class Logical:
	"""An AE logical expression object"""
	
	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]),
			                     string.strip(self.logc),
			                     nice(self.term[1]))
		else:
			return "%s(%s)" % (string.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 StyledText:
	"""An AE object respresenting text in a certain style"""
	
	def __init__(self, style, text):
		self.style = style
		self.text = text
	
	def __repr__(self):
		return "StyledText(%s, %s)" % (`self.style`, `self.text`)
	
	def __str__(self):
		return self.text
	
	def __aepack__(self):
		return pack({'ksty': self.style, 'ktxt': self.text}, 'STXT')

def IsStyledText(x):
	return IsInstance(x, StyledText)

class AEText:
	"""An AE text object with style, script and language specified"""
	
	def __init__(self, script, style, text):
		self.script = script
		self.style = style
		self.text = text
	
	def __repr__(self):
		return "AEText(%s, %s, %s)" % (`self.script`, `self.style`, `self.text`)
	
	def __str__(self):
		return self.text
	
	def __aepack__(self):
		return pack({keyAEScriptTag: self.script, keyAEStyles: self.style,
				 keyAEText: self.text}, typeAEText)

def IsAEText(x):
	return IsInstance(x, AEText)

class IntlText:
	"""A text object with script and language specified"""
	
	def __init__(self, script, language, text):
		self.script = script
		self.language = language
		self.text = text
	
	def __repr__(self):
		return "IntlText(%s, %s, %s)" % (`self.script`, `self.language`, `self.text`)
	
	def __str__(self):
		return self.text
	
	def __aepack__(self):
		return pack(struct.pack('hh', self.script, self.language)+self.text,
			typeIntlText)

def IsIntlText(x):
	return IsInstance(x, IntlText)

class IntlWritingCode:
	"""An object representing script and language"""
	
	def __init__(self, script, language):
		self.script = script
		self.language = language
	
	def __repr__(self):
		return "IntlWritingCode(%s, %s)" % (`self.script`, `self.language`)
	
	def __str__(self):
		return "script system %d, language %d"%(self.script, self.language)
	
	def __aepack__(self):
		return pack(struct.pack('hh', self.script, self.language),
			typeIntlWritingCode)

def IsIntlWritingCode(x):
	return IsInstance(x, IntlWritingCode)

class QDPoint:
	"""A point"""
	
	def __init__(self, v, h):
		self.v = v
		self.h = h
	
	def __repr__(self):
		return "QDPoint(%s, %s)" % (`self.v`, `self.h`)
	
	def __str__(self):
		return "(%d, %d)"%(self.v, self.h)
	
	def __aepack__(self):
		return pack(struct.pack('hh', self.v, self.h),
			typeQDPoint)

def IsQDPoint(x):
	return IsInstance(x, QDPoint)

class QDRectangle:
	"""A rectangle"""
	
	def __init__(self, v0, h0, v1, h1):
		self.v0 = v0
		self.h0 = h0
		self.v1 = v1
		self.h1 = h1
	
	def __repr__(self):
		return "QDRectangle(%s, %s, %s, %s)" % (`self.v0`, `self.h0`,
				`self.v1`, `self.h1`)
	
	def __str__(self):
		return "(%d, %d)-(%d, %d)"%(self.v0, self.h0, self.v1, self.h1)
	
	def __aepack__(self):
		return pack(struct.pack('hhhh', self.v0, self.h0, self.v1, self.h1),
			typeQDRectangle)

def IsQDRectangle(x):
	return IsInstance(x, QDRectangle)

class RGBColor:
	"""An RGB color"""
	
	def __init__(self, r, g, b):
		self.r = r
		self.g = g
		self.b = b
			
	def __repr__(self):
		return "RGBColor(%s, %s, %s)" % (`self.r`, `self.g`, `self.b`)
	
	def __str__(self):
		return "0x%x red, 0x%x green, 0x%x blue"% (self.r, self.g, self.b)
	
	def __aepack__(self):
		return pack(struct.pack('hhh', self.r, self.g, self.b),
			typeRGBColor)

def IsRGBColor(x):
	return IsInstance(x, RGBColor)

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	4-char class code of thing we want,
			e.g. word, paragraph or property
	
	'form'	enum	how we specify which 'want' 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):
		return pack({'want': mktype(self.want),
			     'form': mkenum(self.form),
			     'seld': self.seld,
			     'from': self.fr},
			    'obj ')

def IsObjectSpecifier(x):
	return IsInstance(x, ObjectSpecifier)


# Backwards compatability, sigh...
class Property(ObjectSpecifier):

	def __init__(self, which, fr = None, want='prop'):
		ObjectSpecifier.__init__(self, want, 'prop', mktype(which), fr)

	def __repr__(self):
		if self.fr:
			return "Property_r(%s, %s)" % (`self.seld.type`, `self.fr`)
		else:
			return "Property_r(%s)" % `self.seld.type`
	
	def __str__(self):
		if self.fr:
			return "Property %s of %s" % (str(self.seld), str(self.fr))
		else:
			return "Property_s %s" % str(self.seld)


class NProperty(ObjectSpecifier):
	# Subclasses *must* self baseclass attributes:
	# want is the type of this property
	# which is the property name of this property

	def __init__(self, want, form, seld, fr = None):
		ObjectSpecifier.__init__(self, want, form, 
					mktype(seld), fr)


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'
		elif t == TupleType:
			# Breakout: specify both form and seld in a tuple
			# (if you want ID or rele or somesuch)
			form, seld = seld
		else:
			form = 'indx'
		ObjectSpecifier.__init__(self, want, form, seld, fr)


class ComponentItem(SelectableItem):
	# Derived classes *must* set the *class attribute* 'want' to some constant
	# Also, dictionaries _propdict and _elemdict must be set to map property
	# and element names to the correct classes
	
	def __init__(self, want, which, fr = None):
		SelectableItem.__init__(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
		
#	def __getattr__(self, name):
#		print name
#		if self._elemdict.has_key(name):
#			cls = self._elemdict[name]
#			return DelayedComponentItem(cls, self)
#	   	if self._propdict.has_key(name):
#	   		cls = self._propdict[name]
#	   		return cls(self)
#		raise AttributeError, name
		
		
class DelayedComponentItem:
	def __init__(self, compclass, fr):
		self.compclass = compclass
		self.fr = fr
		
	def __call__(self, which):
		return self.compclass(which, self.fr)
		
	def __repr__(self):
		return "%s(???, %s)" % (self.__class__.__name__, `self.fr`)
		
	def __str__(self):
		return "selector for element %s of %s"%(self.__class__.__name__, str(self.fr))

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')


--- NEW FILE: getaete.py ---
"""
Produces a 3 dictionaries from application aete's 
to be read by PythonScript

v.02 january 31, 1998 added support for inheriting suites from aeut
v.03 february 16, 1998 changes to identify
v.04 february 26, 1998 simplified decode
v.05 23/04/98 simplified _launch

"""
import baetools
import macpath
import sys
import os
import MacOS
import StringIO
import types
from MACFS import *
import macfs
import string
from Carbon.Res import *
import struct

# for testing only
app ='CSOm' #'ezVu'# 'nwSP'#MACS'#

#Restrict the application suites to the dialect we want to use.
LANG = 0 # 0 = English, 1 = French, 11 = Japanese
lang = {0:'English', 1:'French', 11:'Japanese'}

#The following are neaded to open the application aete
kASAppleScriptSuite	= 'ascr'
kGetAETE			= 'gdte'
attributes = {}
arguments = {}

class AETE(baetools.TalkTo):
	pass
	
def Getaete(app):
	try:
		data = openaete(app)
	except MacOS.Error, msg:
		if msg[0] == -609:
			_launch(app)
			data = openaete(app)
	data = decode(data['----'].data)
	data = compileaete(data)
	return data
	
	
def decode(data):
	"""Decode an aete into a python data structure"""
	f = StringIO.StringIO(data)
	aete = generic(getaete, f)
	return aete

def simplify(item):
	"""Recursively replace singleton tuples by their constituent item"""
	if type(item) is types.ListType:
		return map(simplify, item)
	elif type(item) == types.TupleType and len(item) == 2:
		return simplify(item[1])
	else:
		return item


## Here follows the aete resource decoder.
## It is presented bottom-up instead of top-down because there are  direct
## references to the lower-level part-decoders from the high-level part-decoders.
#
def getflag(f, *args):
	m = ''
	c = f.read(2)
	print `c`
	if not c:
		raise EOFError, 'in getflag' + str(args)
	for n in c:
		m = m + `ord(n)`

def getbyte(f, *args):
	c = f.read(1)
	if not c:
		raise EOFError, 'in getbyte' + str(args)
	return ord(c)

def getword(f, *args):
	getalign(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):
	getalign(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])

def getostype(f, *args):
	getalign(f)
	s = f.read(4)
	if len(s) < 4:
		raise EOFError, 'in getostype' + 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 getalign(f):
	if f.tell() & 1:
		c = f.read(1)
		##if c <> '\0':
		##	print 'align:', `c`

def getlist(f, description, getitem):
	count = getword(f)
	list = []
	for i in range(count):
		list.append(generic(getitem, f))
		getalign(f)
	return list

def alt_generic(what, f, *args):
	print "generic", `what`, args
	res = vageneric(what, f, args)
	print '->', `res`
	return res

def generic(what, f, *args):
	if type(what) == types.FunctionType:
		return apply(what, (f,) + args)
	if type(what) == types.ListType:
		record = []
		for thing in what:
			item = apply(generic, thing[:1] + (f,) + thing[1:])
			record.append(item)
	return record
	return "BAD GENERIC ARGS: %s" % `what`

getdata = [
	(getostype, "type"),
	(getpstr, "description"),
	(getword, "flags")
	]
getargument = [
	(getpstr, "name"),
	(getostype, "keyword"),
	(getdata, "what")
	]
getevent = [
	(getpstr, "name"),
	(getpstr, "description"),
	(getostype, "suite code"),
	(getostype, "event code"),
	(getdata, "returns"),
	(getdata, "accepts"),
	(getlist, "optional arguments", getargument)
	]
getproperty = [
	(getpstr, "name"),
	(getostype, "code"),
	(getdata, "what")
	]
getelement = [
	(getostype, "type"),
	(getlist, "keyform", getostype)
	]
getclass = [
	(getpstr, "name"),
	(getostype, "class code"),
	(getpstr, "description"),
	(getlist, "properties", getproperty),
	(getlist, "elements", getelement)
	]
getcomparison = [
	(getpstr, "operator name"),
	(getostype, "operator ID"),
	(getpstr, "operator comment"),
	]
getenumerator = [
	(getpstr, "enumerator name"),
	(getostype, "enumerator ID"),
	(getpstr, "enumerator comment")
	]
getenumeration = [
	(getostype, "enumeration ID"),
	(getlist, "enumerator", getenumerator)
	]
getsuite = [
	(getpstr, "suite name"),
	(getpstr, "suite description"),
	(getostype, "suite ID"),
	(getword, "suite level"),
	(getword, "suite version"),
	(getlist, "events", getevent),
	(getlist, "classes", getclass),
	(getlist, "comparisons", getcomparison),
	(getlist, "enumerations", getenumeration)
	]
getaete = [
	(getbyte, "major version in BCD"),
	(getbyte, "minor version in BCD"),
	(getword, "language code"),
	(getword, "script code"),
	(getlist, "suites", getsuite)
	]

def compileaete(aete):
	"""Generate dictionary for a full aete resource."""
	[major, minor, language, script, suites] = aete
	suitedict = {}
	gsuites = openaeut()
	for gsuite in gsuites:
		if gsuite[0] == 'AppleScript Suite':
			suite = gsuite
			suite = compilesuite(suite)
			suitedict[identify(suite[0])] = suite[1:]
	for suite in suites:
		if language == LANG:
			suitecode = suite[2]
			if suite[5] == []:
				for gsuite in gsuites:
					if suitecode == gsuite[2]:
						suite = gsuite
			suite = compilesuite(suite)
			suitedict[identify(suite[0])] = suite[1:]
	suitedict = combinesuite(suitedict)
	return suitedict
			
def compilesuite(suite):
	"""Generate dictionary for a single suite"""
	[name, desc, code, level, version, events, classes, comps, enums] = suite
	eventdict ={}
	classdict = {}
	enumdict ={}
	for event in events:
		if event[6]:
			for ev in event[6]:
				ev[0] = identify(ev[:2])
		eventdict[identify(event[:2])] = event[1:]
	for klass in classes:
		if klass[3]:
			for kl in klass[3]:
				kl[0] = identify(kl[:2])
		classdict[identify(klass[:2])] = klass[1:]
	for enum in enums:
		enumdict[enum[0]] = enum[1]
	return name, eventdict, classdict, enumdict
		
def combinesuite(suite):
	"""Combines suite dictionaries to seperate event, class, enumeration dictionaries
	"""
	
	suitelist = []
	eventDict ={}
	classDict ={}
	enumDict ={}
	for value in suite.values():
		for key in value[0].keys():
			val = value[0][key]
			eventDict[key] = val
		for key in value[1].keys():
			val = value[1][key]
			if key in classDict.keys():
				nval = classDict[key][2]
				val[2] = val[2] + nval
			classDict[key] = val
		for key in value[2].keys():
			val = value[2][key]
			enumDict[key] = val
	return 	eventDict, classDict, enumDict
		

illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
	"def", "name", 'data' ]

def identify(str):
	"""Turn any string into an identifier:
	- replace space by _
	- remove ',' and '-'
	capitalise
	"""
	if not str[0]:
		if str[1] == 'c@#!':
			return "Every"
		else:
			return 'Any'
	rv = string.replace(str[0], ' ', '_')
	rv = string.replace(rv, '-', '')
	rv = string.replace(rv, ',', '')
	rv = string.capitalize(rv)
	return rv


def openaete(app):
	"""open and read the aete of the target application"""
	arguments['----'] = LANG
	_aete = AETE(app)
	_reply, _arguments, _attributes = _aete.send(kASAppleScriptSuite, kGetAETE, arguments, attributes)
	if _arguments.has_key('errn'):
		raise baetools.Error, baetools.decodeerror(_arguments)
	return  _arguments

def openaeut():
	"""Open and read a aeut file.
	XXXXX This has been temporarily hard coded until a Python aeut is written XXXX"""

	fullname = dialect
	rf = OpenRFPerm(fullname, 0, 1)
	try:
		UseResFile(rf)
		resources = []
		for i in range(Count1Resources('aeut')):
			res = Get1IndResource('aeut', 1+i)
			resources.append(res)
		for res in resources:
			data = res.data
			data = decode(data)[4]
	finally:
		CloseResFile(rf)
	return data
	
def dialect():
	"""find the correct Dialect file"""

	dialect = lang[LANG] + " Dialect"
	try:
		##System 8
		vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kScriptingAdditionsFolderType, 0)
		fss = macfs.FSSpec((vRefNum, dirID, ''))
		fss = fss.as_pathname()
	except macfs.error:
		##Sytem 7
		vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kExtensionFolderType, 0)
		fss = macfs.FSSpec((vRefNum, dirID, ''))
		fss = fss.as_pathname()
		fss = macpath.join(fss, "Scripting Additions")
	fss = macpath.join(fss, "Dialect")
	fss = macpath.join(fss, dialect)
	return fss
	
	
#def openosax():
#	"""Open and read the aetes of osaxen in the scripting additions folder"""
#
#	# System 7.x
#	aete = []
#	vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kExtensionFolderType, 0)
#	fss = macfs.FSSpec((vRefNum, dirID, ''))
#	fss = fss.as_pathname()
#	osax = macpath.join(fss, "Scripting Additions")
#	for file in os.listdir(osax):
#		fullname = macpath.join(osax, file)
#		print fullname
#		rf = OpenRFPerm(fullname, 0, 1)
#		try:
#			UseResFile(rf)
#			resources = []
#			for i in range(Count1Resources('aete')):
#				res = Get1IndResource('aete', 1+i)
#				resources.append(res)
#			for res in resources:
#				data = res.data
#				data = decode(data)[4]
#		finally:
#			CloseResFile(rf)
#		aete.append(data)
#	print data


#The following should be replaced by direct access to a python 'aeut'

def _launch(appfile):
	"""Open a file thru the finder. Specify file by name or fsspec"""

#	from PythonScript import PyScript
	import baetypes
	_finder = AETE('MACS')
	parameters ={}
	parameters['----'] = eval("baetypes.ObjectSpecifier('%s', '%s', %s)" % ('appf', 'ID  ', `appfile`))
	_reply, _arguments, _attributes = _finder.send( 'aevt', 'odoc', parameters , attributes = {})
	if _arguments.has_key('errn'):
		raise baetools.Error, baetools.decodeerror(_arguments)
	# XXXX Optionally decode result
	if _arguments.has_key('----'):
		return _arguments['----']



if __name__ == '__main__':
#	import profile
#	profile.run('Getaete(app)', 'Getaeteprof')
	Getaete(app)
#	openosax()
#	openaete('ascr')
#	sys.exit(1)

--- NEW FILE: printaete.py ---
"""
Produces a human readable file of an application's aete

v.02 january 29, 1998 bug fix Main()
v.03 january 31, 1998 added support for inheriting suites from aeut
v.04 april 16, 1998 Changed identify to match getaete
"""

import aetools
import sys
import MacOS
import StringIO
import types
import macfs
import string
import macpath
from Carbon.Res import *

# for testing only
app = 'MACS'#CSOm'#'nwSP'#'ezVu'#

#Dialect file hard coded as a tempoary measure
DIALECT = 'Hermit:System Folder:Scripting Additions:Dialects:English Dialect'

#Restrict the application suites to the dialect we want to use.
LANG = 0 # 0 = English, 1 = French, 11 = Japanese

#The following are neaded to open the application aete
kASAppleScriptSuite	= 'ascr'
kGetAETE			= 'gdte'
attributes = {}
arguments = {}

class AETE(aetools.TalkTo):
	pass
	
def Main(appl):
	fss, ok = macfs.PromptGetFile('Application to work on', 'FNDR', 'APPL')#
	if not ok:
		return
	app = fss.GetCreatorType()[0]
	path = macpath.split(sys.argv[0])[0]
	appname =  macpath.split(fss.as_pathname())[1]
	appname = appname + '.aete'
	appname = macpath.join(path, appname)
	try:
		data = Getaete(app)
	except MacOS.Error, msg:
		if msg[0] == -609:
			_launch(app)
			data = Getaete(app)
#	print data
	data = decode(data['----'].data)
	data = compileaete(data, appname)
	
	
def decode(data):
	"""Decode an aete into a python data structure"""
	f = StringIO.StringIO(data)
	aete = generic(getaete, f)
	aete = simplify(aete)
	return aete

def simplify(item):
	"""Recursively replace singleton tuples by their constituent item"""
	if type(item) is types.ListType:
		return map(simplify, item)
	elif type(item) == types.TupleType and len(item) == 2:
		return simplify(item[1])
	else:
		return item


## Here follows the aete resource decoder.
## It is presented bottom-up instead of top-down because there are  direct
## references to the lower-level part-decoders from the high-level part-decoders.
#
def getbyte(f, *args):
	c = f.read(1)
	if not c:
		raise EOFError, 'in getbyte' + str(args)
	return ord(c)

def getword(f, *args):
	getalign(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):
	getalign(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])

def getostype(f, *args):
	getalign(f)
	s = f.read(4)
	if len(s) < 4:
		raise EOFError, 'in getostype' + 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 getalign(f):
	if f.tell() & 1:
		c = f.read(1)
		##if c <> '\0':
		##	print 'align:', `c`

def getlist(f, description, getitem):
	count = getword(f)
	list = []
	for i in range(count):
		list.append(generic(getitem, f))
		getalign(f)
	return list

def alt_generic(what, f, *args):
	print "generic", `what`, args
	res = vageneric(what, f, args)
	print '->', `res`
	return res

def generic(what, f, *args):
	if type(what) == types.FunctionType:
		return apply(what, (f,) + args)
	if type(what) == types.ListType:
		record = []
		for thing in what:
#			print thing
			item = apply(generic, thing[:1] + (f,) + thing[1:])
			record.append((thing[1], item))
		return record
	return "BAD GENERIC ARGS: %s" % `what`

getdata = [
	(getostype, "type"),
	(getpstr, "description"),
	(getword, "flags")
	]
getargument = [
	(getpstr, "name"),
	(getostype, "keyword"),
	(getdata, "what")
	]
getevent = [
	(getpstr, "name"),
	(getpstr, "description"),
	(getostype, "suite code"),
	(getostype, "event code"),
	(getdata, "returns"),
	(getdata, "accepts"),
	(getlist, "optional arguments", getargument)
	]
getproperty = [
	(getpstr, "name"),
	(getostype, "code"),
	(getdata, "what")
	]
getelement = [
	(getostype, "type"),
	(getlist, "keyform", getostype)
	]
getclass = [
	(getpstr, "name"),
	(getostype, "class code"),
	(getpstr, "description"),
	(getlist, "properties", getproperty),
	(getlist, "elements", getelement)
	]
getcomparison = [
	(getpstr, "operator name"),
	(getostype, "operator ID"),
	(getpstr, "operator comment"),
	]
getenumerator = [
	(getpstr, "enumerator name"),
	(getostype, "enumerator ID"),
	(getpstr, "enumerator comment")
	]
getenumeration = [
	(getostype, "enumeration ID"),
	(getlist, "enumerator", getenumerator)
	]
getsuite = [
	(getpstr, "suite name"),
	(getpstr, "suite description"),
	(getostype, "suite ID"),
	(getword, "suite level"),
	(getword, "suite version"),
	(getlist, "events", getevent),
	(getlist, "classes", getclass),
	(getlist, "comparisons", getcomparison),
	(getlist, "enumerations", getenumeration)
	]
getaete = [
	(getword, "major/minor version in BCD"),
	(getword, "language code"),
	(getword, "script code"),
	(getlist, "suites", getsuite)
	]

def compileaete(aete, appname):
	"""Generate dictionary file for a full aete resource."""
	[version, language, script, suites] = aete
	major, minor = divmod(version, 256)
	fp = open(appname, 'w')
	
	fp.write('%s:\n' % (appname))
	fp.write("AETE resource version %d/%d, language %d, script %d\n" % \
		(major, minor, language, script))
	fp.write('\n\n')
	gsuites = openaeut()
	for suite in suites:
		if language == LANG:
			suitecode = suite[2]
			if suite[5] == []:
				for gsuite in gsuites:
					if suitecode == gsuite[2]:
						suite = gsuite
			[name, desc, code, level, version, events, classes, comps, enums] = suite
			fp.write('\n%s Suite: %s\n' % (name, desc))
			fp.write('\n\tEvents:\n')
			for event in events:
				fp.write('\n\t%s: %s\n' % (identify(event[0]), event[1]))
				fp.write('\t\t%s: %s -- %s\n' % (identify(event[0]), event[5][1], event[5][0]))
				fp.write('\t\tResult: %s -- %s\n' % (event[4][1], event[4][0]))
				for ev in event[6]:
					fp.write('\t\t\t%s: %s -- %s\n' % (identify(ev[0]), ev[2][0], ev[2][1]))
			fp.write('\n\tClasses')
			for klass in classes:
				fp.write('\n\t%s: %s\n' % (identify(klass[0]), klass[2]))
				if klass[3]:
					if not klass[3][0][0]: continue
					fp.write('\t\tProperties\n')
					for cl in klass[3]:
						fp.write('\t\t\t%s: %s -- %s\n' % (identify(cl[0]), cl[2][1], cl[2][0]))#,, cl[3][3][1]))
				if klass[4]:
					fp.write('\n\t\t\tElements\n')
					for cl in klass[4]:
						fp.write('\t\t\t\t%s: %s\n' % (identify(cl[0]), cl[1]))
						
				
					
		
	

illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
	"def", "name" ]

def identify(str):
	"""Turn any string into an identifier:
	- replace space by _
	- prepend _ if the result is a python keyword
	"""

	rv = string.replace(str, ' ', '_')
	rv = string.replace(rv, '-', '')
	rv = string.replace(rv, ',', '')
	rv = string.capitalize(rv)
	return rv


def Getaete(app):
	'''Read the target aete'''
	arguments['----'] = LANG
	_aete = AETE(app)
	_reply, _arguments, _attributes = _aete.send('ascr', 'gdte', arguments, attributes)
	if _arguments.has_key('errn'):
		raise aetools.Error, aetools.decodeerror(_arguments)
	return  _arguments

def openaeut():
	"""Open and read a aeut file.
	XXXXX This has been temporarily hard coded until a Python aeut is written XXXX"""

	fullname = DIALECT
	
	rf = OpenRFPerm(fullname, 0, 1)
	try:
		UseResFile(rf)
		resources = []
		for i in range(Count1Resources('aeut')):
			res = Get1IndResource('aeut', 1+i)
			resources.append(res)
		for res in resources:
			data = res.data
			data = decode(data)[3]
	finally:
		CloseResFile(rf)
	return data


#The following should be replaced by direct access to a python 'aeut'

class _miniFinder(aetools.TalkTo):
	def open(self, _object, _attributes={}, **_arguments):
		"""open: Open the specified object(s)
		Required argument: list of objects to open
		Keyword argument _attributes: AppleEvent attribute dictionary
		"""
		_code = 'aevt'
		_subcode = 'odoc'

		if _arguments: raise TypeError, 'No optional args expected'
		_arguments['----'] = _object


		_reply, _arguments, _attributes = self.send(_code, _subcode,
				_arguments, _attributes)
		if _arguments.has_key('errn'):
			raise aetools.Error, aetools.decodeerror(_arguments)
		# XXXX Optionally decode result
		if _arguments.has_key('----'):
			return _arguments['----']
	
_finder = _miniFinder('MACS')

def _launch(appfile):
	"""Open a file thru the finder. Specify file by name or fsspec"""
	_finder.open(_application_file(('ID  ', appfile)))


class _application_file(aetools.ComponentItem):
	"""application file - An application's file on disk"""
	want = 'appf'
	
_application_file._propdict = {
}
_application_file._elemdict = {
}

Main(app)
sys.exit(1)

--- NEW FILE: testeudora.py ---
"""A test program that allows us to control Eudora"""

import sys
import MacOS
import PythonScript
	
# The Creator signature of eudora:
SIGNATURE="CSOm"
TIMEOUT = 10*60*60



def main():
	PythonScript.PsScript(SIGNATURE, TIMEOUT)
	talker = PythonScript.PyScript
	ev = PythonScript.PsEvents
	pc = PythonScript.PsClass
	while 1:
		print 'get, put, name (of first folder), list (foldernames), quit (eudora) or exit (this program) ?'
		line = sys.stdin.readline()
		try:
			if line[0] == 'g':
				print 'check'
				print talker(ev.Activate)
				print talker(ev.Connect, Checking=1)
			elif line[0] == 'p':
				print talker(ev.Connect, Sending=1)
			elif line[0] == 'n':
				id = talker(ev.Get, pc.Mail_folder("").Mailbox(1).Name())
				print "It is called", id, "\n"
			elif line[0] == 'l':
				id = talker(ev.Count, pc.Mail_folder(""), Each='Mailbox')
				print "There are", id, "mailboxes"
			elif line[0] == 'q':
				print talker(ev.Quit)
			elif line[0] == 'e':
				break
		except MacOS.Error, arg:
			if arg[0] == -609:
				print 'Connection invalid, is eudora running?'
			else:
				print 'MacOS Error:', arg[1]
			
main()