mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1205 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1205 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| gensuitemodule - Generate an AE suite module from an aete/aeut resource
 | |
| 
 | |
| Based on aete.py.
 | |
| 
 | |
| Reading and understanding this code is left as an exercise to the reader.
 | |
| """
 | |
| 
 | |
| import MacOS
 | |
| import EasyDialogs
 | |
| import os
 | |
| import string
 | |
| import sys
 | |
| import types
 | |
| import StringIO
 | |
| import keyword
 | |
| import macresource
 | |
| import aetools
 | |
| import distutils.sysconfig
 | |
| import OSATerminology
 | |
| from Carbon.Res import *
 | |
| import Carbon.Folder
 | |
| import MacOS
 | |
| import getopt
 | |
| import plistlib
 | |
| 
 | |
| _MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
 | |
| DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
 | |
| DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
 | |
| 
 | |
| def usage():
 | |
|     sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0])
 | |
|     sys.stderr.write("""Options:
 | |
| --output pkgdir  Pathname of the output package (short: -o)  
 | |
| --resource       Parse resource file in stead of launching application (-r)
 | |
| --base package   Use another base package in stead of default StdSuites (-b)
 | |
| --edit old=new   Edit suite names, use empty new to skip a suite (-e)
 | |
| --creator code   Set creator code for package (-c)
 | |
| --dump           Dump aete resource to stdout in stead of creating module (-d)
 | |
| --verbose        Tell us what happens (-v)
 | |
| """)
 | |
|     sys.exit(1)
 | |
| 
 | |
| def main():
 | |
|     if len(sys.argv) > 1:
 | |
|         SHORTOPTS = "rb:o:e:c:dv"
 | |
|         LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=", "dump", "verbose")
 | |
|         try:
 | |
|             opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
 | |
|         except getopt.GetoptError:
 | |
|             usage()
 | |
|         
 | |
|         process_func = processfile
 | |
|         basepkgname = 'StdSuites'
 | |
|         output = None
 | |
|         edit_modnames = []
 | |
|         creatorsignature = None
 | |
|         dump = None
 | |
|         verbose = None
 | |
|         
 | |
|         for o, a in opts:
 | |
|             if o in ('-r', '--resource'):
 | |
|                 process_func = processfile_fromresource
 | |
|             if o in ('-b', '--base'):
 | |
|                 basepkgname = a
 | |
|             if o in ('-o', '--output'):
 | |
|                 output = a
 | |
|             if o in ('-e', '--edit'):
 | |
|                 split = a.split('=')
 | |
|                 if len(split) != 2:
 | |
|                     usage()
 | |
|                 edit_modnames.append(split)
 | |
|             if o in ('-c', '--creator'):
 | |
|                 if len(a) != 4:
 | |
|                     sys.stderr.write("creator must be 4-char string\n")
 | |
|                     sys.exit(1)
 | |
|                 creatorsignature = a
 | |
|             if o in ('-d', '--dump'):
 | |
|                 dump = sys.stdout
 | |
|             if o in ('-v', '--verbose'):
 | |
|                 verbose = sys.stderr
 | |
|                 
 | |
|                     
 | |
|         if output and len(args) > 1:
 | |
|             sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0])
 | |
|             sys.exit(1)
 | |
|             
 | |
|         for filename in args:
 | |
|             process_func(filename, output=output, basepkgname=basepkgname, 
 | |
|                 edit_modnames=edit_modnames, creatorsignature=creatorsignature,
 | |
|                 dump=dump, verbose=verbose)
 | |
|     else:
 | |
|         main_interactive()
 | |
|         
 | |
| def main_interactive(interact=0, basepkgname='StdSuites'):
 | |
|     if interact:
 | |
|         # Ask for save-filename for each module
 | |
|         edit_modnames = None
 | |
|     else:
 | |
|         # Use default filenames for each module
 | |
|         edit_modnames = []
 | |
|     appsfolder = Carbon.Folder.FSFindFolder(-32765, 'apps', 0)
 | |
|     filename = EasyDialogs.AskFileForOpen(
 | |
|         message='Select scriptable application',
 | |
|         dialogOptionFlags=0x1056,       # allow selection of .app bundles
 | |
|         defaultLocation=appsfolder)
 | |
|     if not filename:
 | |
|         return
 | |
|     if not is_scriptable(filename):
 | |
|         if EasyDialogs.AskYesNoCancel(
 | |
|                 "Warning: application does not seem scriptable",
 | |
|                 yes="Continue", default=2, no="") <= 0:
 | |
|             return
 | |
|     try:
 | |
|         processfile(filename, edit_modnames=edit_modnames, basepkgname=basepkgname,
 | |
|         verbose=sys.stderr)
 | |
|     except MacOS.Error, arg:
 | |
|         print "Error getting terminology:", arg
 | |
|         print "Retry, manually parsing resources"
 | |
|         processfile_fromresource(filename, edit_modnames=edit_modnames,
 | |
|             basepkgname=basepkgname, verbose=sys.stderr)
 | |
|             
 | |
| def is_scriptable(application):
 | |
|     """Return true if the application is scriptable"""
 | |
|     if os.path.isdir(application):
 | |
|         plistfile = os.path.join(application, 'Contents', 'Info.plist')
 | |
|         if not os.path.exists(plistfile):
 | |
|             return False
 | |
|         plist = plistlib.Plist.fromFile(plistfile)
 | |
|         return plist.get('NSAppleScriptEnabled', False)
 | |
|     # If it is a file test for an aete/aeut resource.
 | |
|     currf = CurResFile()
 | |
|     try:
 | |
|         refno = macresource.open_pathname(application)
 | |
|     except MacOS.Error:
 | |
|         return False
 | |
|     UseResFile(refno)
 | |
|     n_terminology = Count1Resources('aete') + Count1Resources('aeut') + \
 | |
|         Count1Resources('scsz') + Count1Resources('osiz')
 | |
|     CloseResFile(refno)
 | |
|     UseResFile(currf)
 | |
|     return n_terminology > 0
 | |
| 
 | |
| def processfile_fromresource(fullname, output=None, basepkgname=None, 
 | |
|         edit_modnames=None, creatorsignature=None, dump=None, verbose=None):
 | |
|     """Process all resources in a single file"""
 | |
|     if not is_scriptable(fullname) and verbose:
 | |
|         print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
 | |
|     cur = CurResFile()
 | |
|     if verbose:
 | |
|         print >>verbose, "Processing", fullname
 | |
|     rf = macresource.open_pathname(fullname)
 | |
|     try:
 | |
|         UseResFile(rf)
 | |
|         resources = []
 | |
|         for i in range(Count1Resources('aete')):
 | |
|             res = Get1IndResource('aete', 1+i)
 | |
|             resources.append(res)
 | |
|         for i in range(Count1Resources('aeut')):
 | |
|             res = Get1IndResource('aeut', 1+i)
 | |
|             resources.append(res)
 | |
|         if verbose: 
 | |
|             print >>verbose, "\nLISTING aete+aeut RESOURCES IN", `fullname`
 | |
|         aetelist = []
 | |
|         for res in resources:
 | |
|             if verbose:
 | |
|                 print >>verbose, "decoding", res.GetResInfo(), "..."
 | |
|             data = res.data
 | |
|             aete = decode(data, verbose)
 | |
|             aetelist.append((aete, res.GetResInfo()))
 | |
|     finally:
 | |
|         if rf <> cur:
 | |
|             CloseResFile(rf)
 | |
|             UseResFile(cur)
 | |
|     # switch back (needed for dialogs in Python)
 | |
|     UseResFile(cur)
 | |
|     if dump:
 | |
|         dumpaetelist(aetelist, dump)
 | |
|     compileaetelist(aetelist, fullname, output=output, 
 | |
|         basepkgname=basepkgname, edit_modnames=edit_modnames,
 | |
|         creatorsignature=creatorsignature, verbose=verbose)
 | |
| 
 | |
| def processfile(fullname, output=None, basepkgname=None, 
 | |
|         edit_modnames=None, creatorsignature=None, dump=None,
 | |
|         verbose=None):
 | |
|     """Ask an application for its terminology and process that"""
 | |
|     if not is_scriptable(fullname) and verbose:
 | |
|         print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
 | |
|     if verbose:
 | |
|         print >>verbose, "\nASKING FOR aete DICTIONARY IN", `fullname`
 | |
|     try:
 | |
|         aedescobj, launched = OSATerminology.GetAppTerminology(fullname)
 | |
|     except MacOS.Error, arg:
 | |
|         if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound
 | |
|             if verbose:
 | |
|                 print >>verbose, "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually"
 | |
|             aedata, sig = getappterminology(fullname, verbose=verbose)
 | |
|             if not creatorsignature:
 | |
|                 creatorsignature = sig
 | |
|         else:
 | |
|             raise
 | |
|     else:
 | |
|         if launched:
 | |
|             if verbose:
 | |
|                 print >>verbose, "Launched", fullname
 | |
|         raw = aetools.unpack(aedescobj)
 | |
|         if not raw: 
 | |
|             if verbose:
 | |
|                 print >>verbose, 'Unpack returned empty value:', raw
 | |
|             return
 | |
|         if not raw[0].data:
 | |
|             if verbose:
 | |
|                 print >>verbose, 'Unpack returned value without data:', raw
 | |
|             return
 | |
|         aedata = raw[0]
 | |
|     aete = decode(aedata.data, verbose)
 | |
|     if dump:
 | |
|         dumpaetelist([aete], dump)
 | |
|         return
 | |
|     compileaete(aete, None, fullname, output=output, basepkgname=basepkgname,
 | |
|         creatorsignature=creatorsignature, edit_modnames=edit_modnames, 
 | |
|         verbose=verbose)
 | |
|         
 | |
| def getappterminology(fullname, verbose=None):
 | |
|     """Get application terminology by sending an AppleEvent"""
 | |
|     # First check that we actually can send AppleEvents
 | |
|     if not MacOS.WMAvailable():
 | |
|         raise RuntimeError, "Cannot send AppleEvents, no access to window manager"
 | |
|     # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless
 | |
|     # you have created an event loop first.
 | |
|     import Carbon.Evt
 | |
|     Carbon.Evt.WaitNextEvent(0,0)
 | |
|     if os.path.isdir(fullname):
 | |
|         # Now get the signature of the application, hoping it is a bundle
 | |
|         pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo')
 | |
|         if not os.path.exists(pkginfo):
 | |
|             raise RuntimeError, "No PkgInfo file found"
 | |
|         tp_cr = open(pkginfo, 'rb').read()
 | |
|         cr = tp_cr[4:8]
 | |
|     else:
 | |
|         # Assume it is a file
 | |
|         cr, tp = MacOS.GetCreatorAndType(fullname)
 | |
|     # Let's talk to it and ask for its AETE
 | |
|     talker = aetools.TalkTo(cr)
 | |
|     try:
 | |
|         talker._start()
 | |
|     except (MacOS.Error, aetools.Error), arg:
 | |
|         if verbose:
 | |
|             print >>verbose, 'Warning: start() failed, continuing anyway:', arg
 | |
|     reply = talker.send("ascr", "gdte")
 | |
|     #reply2 = talker.send("ascr", "gdut")
 | |
|     # Now pick the bits out of the return that we need.
 | |
|     return reply[1]['----'], cr
 | |
|     
 | |
| 
 | |
| def compileaetelist(aetelist, fullname, output=None, basepkgname=None, 
 | |
|             edit_modnames=None, creatorsignature=None, verbose=None):
 | |
|     for aete, resinfo in aetelist:
 | |
|         compileaete(aete, resinfo, fullname, output=output, 
 | |
|             basepkgname=basepkgname, edit_modnames=edit_modnames,
 | |
|             creatorsignature=creatorsignature, verbose=verbose)
 | |
| 
 | |
| def dumpaetelist(aetelist, output):
 | |
|     import pprint
 | |
|     pprint.pprint(aetelist, output)
 | |
|     
 | |
| def decode(data, verbose=None):
 | |
|     """Decode a resource into a python data structure"""
 | |
|     f = StringIO.StringIO(data)
 | |
|     aete = generic(getaete, f)
 | |
|     aete = simplify(aete)
 | |
|     processed = f.tell()
 | |
|     unprocessed = len(f.read())
 | |
|     total = f.tell()
 | |
|     if unprocessed and verbose:
 | |
|         verbose.write("%d processed + %d unprocessed = %d total\n" %
 | |
|                          (processed, unprocessed, total))
 | |
|     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:
 | |
|             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, resinfo, fname, output=None, basepkgname=None, 
 | |
|         edit_modnames=None, creatorsignature=None, verbose=None):
 | |
|     """Generate code for a full aete resource. fname passed for doc purposes"""
 | |
|     [version, language, script, suites] = aete
 | |
|     major, minor = divmod(version, 256)
 | |
|     if not creatorsignature:
 | |
|         creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
 | |
|     packagename = identify(os.path.splitext(os.path.basename(fname))[0])
 | |
|     if language:
 | |
|         packagename = packagename+'_lang%d'%language
 | |
|     if script:
 | |
|         packagename = packagename+'_script%d'%script
 | |
|     if len(packagename) > 27:
 | |
|         packagename = packagename[:27]
 | |
|     if output:
 | |
|         # XXXX Put this in site-packages if it isn't a full pathname?
 | |
|         if not os.path.exists(output):
 | |
|             os.mkdir(output)
 | |
|         pathname = output
 | |
|     else:
 | |
|         pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
 | |
|             defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
 | |
|         output = pathname
 | |
|     if not pathname:
 | |
|         return
 | |
|     packagename = os.path.split(os.path.normpath(pathname))[1]
 | |
|     if not basepkgname:
 | |
|         basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
 | |
|             defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
 | |
|     if basepkgname:
 | |
|         dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
 | |
|         if dirname and not dirname in sys.path:
 | |
|             sys.path.insert(0, dirname)
 | |
|         basepackage = __import__(basepkgname)
 | |
|     else:
 | |
|         basepackage = None
 | |
|     suitelist = []
 | |
|     allprecompinfo = []
 | |
|     allsuites = []
 | |
|     for suite in suites:
 | |
|         compiler = SuiteCompiler(suite, basepackage, output, edit_modnames, verbose)
 | |
|         code, modname, precompinfo = compiler.precompilesuite()
 | |
|         if not code:
 | |
|             continue
 | |
|         allprecompinfo = allprecompinfo + precompinfo
 | |
|         suiteinfo = suite, pathname, modname
 | |
|         suitelist.append((code, modname))
 | |
|         allsuites.append(compiler)
 | |
|     for compiler in allsuites:
 | |
|         compiler.compilesuite(major, minor, language, script, fname, allprecompinfo)
 | |
|     initfilename = os.path.join(output, '__init__.py')
 | |
|     fp = open(initfilename, 'w')
 | |
|     MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
 | |
|     fp.write('"""\n')
 | |
|     fp.write("Package generated from %s\n"%ascii(fname))
 | |
|     if resinfo:
 | |
|         fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
 | |
|     fp.write('"""\n')
 | |
|     fp.write('import aetools\n')
 | |
|     fp.write('Error = aetools.Error\n')
 | |
|     suitelist.sort()
 | |
|     for code, modname in suitelist:
 | |
|         fp.write("import %s\n" % modname)
 | |
|     fp.write("\n\n_code_to_module = {\n")
 | |
|     for code, modname in suitelist:
 | |
|         fp.write("    '%s' : %s,\n"%(ascii(code), modname))
 | |
|     fp.write("}\n\n")
 | |
|     fp.write("\n\n_code_to_fullname = {\n")
 | |
|     for code, modname in suitelist:
 | |
|         fp.write("    '%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
 | |
|     fp.write("}\n\n")
 | |
|     for code, modname in suitelist:
 | |
|         fp.write("from %s import *\n"%modname)
 | |
|     
 | |
|     # Generate property dicts and element dicts for all types declared in this module
 | |
|     fp.write("\ndef getbaseclasses(v):\n")
 | |
|     fp.write("    if not getattr(v, '_propdict', None):\n")
 | |
|     fp.write("        v._propdict = {}\n")
 | |
|     fp.write("        v._elemdict = {}\n")
 | |
|     fp.write("        for superclassname in getattr(v, '_superclassnames', []):\n")
 | |
|     fp.write("            superclass = eval(superclassname)\n")
 | |
|     fp.write("            getbaseclasses(superclass)\n")
 | |
|     fp.write("            v._propdict.update(getattr(superclass, '_propdict', {}))\n")
 | |
|     fp.write("            v._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
 | |
|     fp.write("        v._propdict.update(getattr(v, '_privpropdict', {}))\n")
 | |
|     fp.write("        v._elemdict.update(getattr(v, '_privelemdict', {}))\n")
 | |
|     fp.write("\n")
 | |
|     fp.write("import StdSuites\n")
 | |
|     allprecompinfo.sort()
 | |
|     if allprecompinfo:
 | |
|         fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
 | |
|         for codenamemapper in allprecompinfo:
 | |
|             for k, v in codenamemapper.getall('class'):
 | |
|                 fp.write("getbaseclasses(%s)\n" % v)
 | |
| 
 | |
|     # Generate a code-to-name mapper for all of the types (classes) declared in this module
 | |
|     if allprecompinfo:
 | |
|         fp.write("\n#\n# Indices of types declared in this module\n#\n")
 | |
|         fp.write("_classdeclarations = {\n")
 | |
|         for codenamemapper in allprecompinfo:
 | |
|             for k, v in codenamemapper.getall('class'):
 | |
|                 fp.write("    %s : %s,\n" % (`k`, v))
 | |
|         fp.write("}\n")
 | |
| 
 | |
|     if suitelist:
 | |
|         fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
 | |
|         for code, modname in suitelist[1:]:
 | |
|             fp.write(",\n        %s_Events"%modname)
 | |
|         fp.write(",\n        aetools.TalkTo):\n")
 | |
|         fp.write("    _signature = %s\n\n"%`creatorsignature`)
 | |
|         fp.write("    _moduleName = '%s'\n\n"%packagename)
 | |
|     fp.close()
 | |
| 
 | |
| class SuiteCompiler:
 | |
|     def __init__(self, suite, basepackage, output, edit_modnames, verbose):
 | |
|         self.suite = suite
 | |
|         self.basepackage = basepackage
 | |
|         self.edit_modnames = edit_modnames
 | |
|         self.output = output
 | |
|         self.verbose = verbose
 | |
|         
 | |
|         # Set by precompilesuite
 | |
|         self.pathname = None
 | |
|         self.modname = None
 | |
|         
 | |
|         # Set by compilesuite
 | |
|         self.fp = None
 | |
|         self.basemodule = None
 | |
|         self.enumsneeded = {}
 | |
|             
 | |
|     def precompilesuite(self):
 | |
|         """Parse a single suite without generating the output. This step is needed
 | |
|         so we can resolve recursive references by suites to enums/comps/etc declared
 | |
|         in other suites"""
 | |
|         [name, desc, code, level, version, events, classes, comps, enums] = self.suite
 | |
|         
 | |
|         modname = identify(name)
 | |
|         if len(modname) > 28:
 | |
|             modname = modname[:27]
 | |
|         if self.edit_modnames is None:
 | |
|             self.pathname = EasyDialogs.AskFileForSave(message='Python output file',
 | |
|                 savedFileName=modname+'.py')
 | |
|         else:
 | |
|             for old, new in self.edit_modnames:
 | |
|                 if old == modname:
 | |
|                     modname = new
 | |
|             if modname:
 | |
|                 self.pathname = os.path.join(self.output, modname + '.py')
 | |
|             else:
 | |
|                 self.pathname = None
 | |
|         if not self.pathname:
 | |
|             return None, None, None
 | |
|     
 | |
|         self.modname = os.path.splitext(os.path.split(self.pathname)[1])[0]
 | |
|         
 | |
|         if self.basepackage and self.basepackage._code_to_module.has_key(code):
 | |
|             # We are an extension of a baseclass (usually an application extending
 | |
|             # Standard_Suite or so). Import everything from our base module
 | |
|             basemodule = self.basepackage._code_to_module[code]
 | |
|         else:
 | |
|             # We are not an extension.
 | |
|             basemodule = None
 | |
|     
 | |
|         self.enumsneeded = {}
 | |
|         for event in events:
 | |
|             self.findenumsinevent(event)
 | |
|     
 | |
|         objc = ObjectCompiler(None, self.modname, basemodule, interact=(self.edit_modnames is None),
 | |
|             verbose=self.verbose)
 | |
|         for cls in classes:
 | |
|             objc.compileclass(cls)
 | |
|         for cls in classes:
 | |
|             objc.fillclasspropsandelems(cls)
 | |
|         for comp in comps:
 | |
|             objc.compilecomparison(comp)
 | |
|         for enum in enums:
 | |
|             objc.compileenumeration(enum)
 | |
|         
 | |
|         for enum in self.enumsneeded.keys():
 | |
|             objc.checkforenum(enum)
 | |
|             
 | |
|         objc.dumpindex()
 | |
|         
 | |
|         precompinfo = objc.getprecompinfo(self.modname)
 | |
|         
 | |
|         return code, self.modname, precompinfo
 | |
|     
 | |
|     def compilesuite(self, major, minor, language, script, fname, precompinfo):
 | |
|         """Generate code for a single suite"""
 | |
|         [name, desc, code, level, version, events, classes, comps, enums] = self.suite
 | |
|         # Sort various lists, so re-generated source is easier compared
 | |
|         def class_sorter(k1, k2):
 | |
|             """Sort classes by code, and make sure main class sorts before synonyms"""
 | |
|             # [name, code, desc, properties, elements] = cls
 | |
|             if k1[1] < k2[1]: return -1
 | |
|             if k1[1] > k2[1]: return 1
 | |
|             if not k2[3] or k2[3][0][1] == 'c@#!':
 | |
|                 # This is a synonym, the other one is better
 | |
|                 return -1
 | |
|             if not k1[3] or k1[3][0][1] == 'c@#!':
 | |
|                 # This is a synonym, the other one is better
 | |
|                 return 1
 | |
|             return 0
 | |
|             
 | |
|         events.sort()
 | |
|         classes.sort(class_sorter)
 | |
|         comps.sort()
 | |
|         enums.sort()
 | |
|         
 | |
|         self.fp = fp = open(self.pathname, 'w')
 | |
|         MacOS.SetCreatorAndType(self.pathname, 'Pyth', 'TEXT')
 | |
|         
 | |
|         fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
 | |
|         fp.write("Level %d, version %d\n\n" % (level, version))
 | |
|         fp.write("Generated from %s\n"%ascii(fname))
 | |
|         fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
 | |
|             (major, minor, language, script))
 | |
|         fp.write('"""\n\n')
 | |
|         
 | |
|         fp.write('import aetools\n')
 | |
|         fp.write('import MacOS\n\n')
 | |
|         fp.write("_code = %s\n\n"% `code`)
 | |
|         if self.basepackage and self.basepackage._code_to_module.has_key(code):
 | |
|             # We are an extension of a baseclass (usually an application extending
 | |
|             # Standard_Suite or so). Import everything from our base module
 | |
|             fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code][0])
 | |
|             basemodule = self.basepackage._code_to_module[code]
 | |
|         elif self.basepackage and self.basepackage._code_to_module.has_key(code.lower()):
 | |
|             # This is needed by CodeWarrior and some others.
 | |
|             fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code.lower()][0])
 | |
|             basemodule = self.basepackage._code_to_module[code.lower()]
 | |
|         else:
 | |
|             # We are not an extension.
 | |
|             basemodule = None
 | |
|         self.basemodule = basemodule
 | |
|         self.compileclassheader()
 | |
|     
 | |
|         self.enumsneeded = {}
 | |
|         if events:
 | |
|             for event in events:
 | |
|                 self.compileevent(event)
 | |
|         else:
 | |
|             fp.write("    pass\n\n")
 | |
|     
 | |
|         objc = ObjectCompiler(fp, self.modname, basemodule, precompinfo, interact=(self.edit_modnames is None),
 | |
|             verbose=self.verbose)
 | |
|         for cls in classes:
 | |
|             objc.compileclass(cls)
 | |
|         for cls in classes:
 | |
|             objc.fillclasspropsandelems(cls)
 | |
|         for comp in comps:
 | |
|             objc.compilecomparison(comp)
 | |
|         for enum in enums:
 | |
|             objc.compileenumeration(enum)
 | |
|         
 | |
|         for enum in self.enumsneeded.keys():
 | |
|             objc.checkforenum(enum)
 | |
|             
 | |
|         objc.dumpindex()
 | |
|         
 | |
|     def compileclassheader(self):
 | |
|         """Generate class boilerplate"""
 | |
|         classname = '%s_Events'%self.modname
 | |
|         if self.basemodule:
 | |
|             modshortname = string.split(self.basemodule.__name__, '.')[-1]
 | |
|             baseclassname = '%s_Events'%modshortname
 | |
|             self.fp.write("class %s(%s):\n\n"%(classname, baseclassname))
 | |
|         else:
 | |
|             self.fp.write("class %s:\n\n"%classname)
 | |
|         
 | |
|     def compileevent(self, event):
 | |
|         """Generate code for a single event"""
 | |
|         [name, desc, code, subcode, returns, accepts, arguments] = event
 | |
|         fp = self.fp
 | |
|         funcname = identify(name)
 | |
|         #
 | |
|         # generate name->keyword map
 | |
|         #
 | |
|         if arguments:
 | |
|             fp.write("    _argmap_%s = {\n"%funcname)
 | |
|             for a in arguments:
 | |
|                 fp.write("        %s : %s,\n"%(`identify(a[0])`, `a[1]`))
 | |
|             fp.write("    }\n\n")
 | |
|             
 | |
|         #
 | |
|         # Generate function header
 | |
|         #
 | |
|         has_arg = (not is_null(accepts))
 | |
|         opt_arg = (has_arg and is_optional(accepts))
 | |
|         
 | |
|         fp.write("    def %s(self, "%funcname)
 | |
|         if has_arg:
 | |
|             if not opt_arg:
 | |
|                 fp.write("_object, ")       # Include direct object, if it has one
 | |
|             else:
 | |
|                 fp.write("_object=None, ")  # Also include if it is optional
 | |
|         else:
 | |
|             fp.write("_no_object=None, ")   # For argument checking
 | |
|         fp.write("_attributes={}, **_arguments):\n")    # include attribute dict and args
 | |
|         #
 | |
|         # Generate doc string (important, since it may be the only
 | |
|         # available documentation, due to our name-remaping)
 | |
|         #
 | |
|         fp.write('        """%s: %s\n'%(ascii(name), ascii(desc)))
 | |
|         if has_arg:
 | |
|             fp.write("        Required argument: %s\n"%getdatadoc(accepts))
 | |
|         elif opt_arg:
 | |
|             fp.write("        Optional argument: %s\n"%getdatadoc(accepts))
 | |
|         for arg in arguments:
 | |
|             fp.write("        Keyword argument %s: %s\n"%(identify(arg[0]),
 | |
|                     getdatadoc(arg[2])))
 | |
|         fp.write("        Keyword argument _attributes: AppleEvent attribute dictionary\n")
 | |
|         if not is_null(returns):
 | |
|             fp.write("        Returns: %s\n"%getdatadoc(returns))
 | |
|         fp.write('        """\n')
 | |
|         #
 | |
|         # Fiddle the args so everything ends up in 'arguments' dictionary
 | |
|         #
 | |
|         fp.write("        _code = %s\n"% `code`)
 | |
|         fp.write("        _subcode = %s\n\n"% `subcode`)
 | |
|         #
 | |
|         # Do keyword name substitution
 | |
|         #
 | |
|         if arguments:
 | |
|             fp.write("        aetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
 | |
|         else:
 | |
|             fp.write("        if _arguments: raise TypeError, 'No optional args expected'\n")
 | |
|         #
 | |
|         # Stuff required arg (if there is one) into arguments
 | |
|         #
 | |
|         if has_arg:
 | |
|             fp.write("        _arguments['----'] = _object\n")
 | |
|         elif opt_arg:
 | |
|             fp.write("        if _object:\n")
 | |
|             fp.write("            _arguments['----'] = _object\n")
 | |
|         else:
 | |
|             fp.write("        if _no_object != None: raise TypeError, 'No direct arg expected'\n")
 | |
|         fp.write("\n")
 | |
|         #
 | |
|         # Do enum-name substitution
 | |
|         #
 | |
|         for a in arguments:
 | |
|             if is_enum(a[2]):
 | |
|                 kname = a[1]
 | |
|                 ename = a[2][0]
 | |
|                 if ename <> '****':
 | |
|                     fp.write("        aetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
 | |
|                         (`kname`, identify(ename)))
 | |
|                     self.enumsneeded[ename] = 1
 | |
|         fp.write("\n")
 | |
|         #
 | |
|         # Do the transaction
 | |
|         #
 | |
|         fp.write("        _reply, _arguments, _attributes = self.send(_code, _subcode,\n")
 | |
|         fp.write("                _arguments, _attributes)\n")
 | |
|         #
 | |
|         # Error handling
 | |
|         #
 | |
|         fp.write("        if _arguments.get('errn', 0):\n")
 | |
|         fp.write("            raise aetools.Error, aetools.decodeerror(_arguments)\n")
 | |
|         fp.write("        # XXXX Optionally decode result\n")
 | |
|         #
 | |
|         # Decode result
 | |
|         #
 | |
|         fp.write("        if _arguments.has_key('----'):\n")
 | |
|         if is_enum(returns):
 | |
|             fp.write("            # XXXX Should do enum remapping here...\n")
 | |
|         fp.write("            return _arguments['----']\n")
 | |
|         fp.write("\n")
 | |
|             
 | |
|     def findenumsinevent(self, event):
 | |
|         """Find all enums for a single event"""
 | |
|         [name, desc, code, subcode, returns, accepts, arguments] = event
 | |
|         for a in arguments:
 | |
|             if is_enum(a[2]):
 | |
|                 ename = a[2][0]
 | |
|                 if ename <> '****':
 | |
|                     self.enumsneeded[ename] = 1
 | |
|     
 | |
| #
 | |
| # This class stores the code<->name translations for a single module. It is used
 | |
| # to keep the information while we're compiling the module, but we also keep these objects
 | |
| # around so if one suite refers to, say, an enum in another suite we know where to
 | |
| # find it. Finally, if we really can't find a code, the user can add modules by
 | |
| # hand.
 | |
| #
 | |
| class CodeNameMapper:
 | |
|     
 | |
|     def __init__(self, interact=1, verbose=None):
 | |
|         self.code2name = {
 | |
|             "property" : {},
 | |
|             "class" : {},
 | |
|             "enum" : {},
 | |
|             "comparison" : {},
 | |
|         }
 | |
|         self.name2code =  {
 | |
|             "property" : {},
 | |
|             "class" : {},
 | |
|             "enum" : {},
 | |
|             "comparison" : {},
 | |
|         }
 | |
|         self.modulename = None
 | |
|         self.star_imported = 0
 | |
|         self.can_interact = interact
 | |
|         self.verbose = verbose
 | |
|         
 | |
|     def addnamecode(self, type, name, code):
 | |
|         self.name2code[type][name] = code
 | |
|         if not self.code2name[type].has_key(code):
 | |
|             self.code2name[type][code] = name
 | |
|         
 | |
|     def hasname(self, name):
 | |
|         for dict in self.name2code.values():
 | |
|             if dict.has_key(name):
 | |
|                 return True
 | |
|         return False
 | |
|         
 | |
|     def hascode(self, type, code):
 | |
|         return self.code2name[type].has_key(code)
 | |
|         
 | |
|     def findcodename(self, type, code):
 | |
|         if not self.hascode(type, code):
 | |
|             return None, None, None
 | |
|         name = self.code2name[type][code]
 | |
|         if self.modulename and not self.star_imported:
 | |
|             qualname = '%s.%s'%(self.modulename, name)
 | |
|         else:
 | |
|             qualname = name
 | |
|         return name, qualname, self.modulename
 | |
|         
 | |
|     def getall(self, type):
 | |
|         return self.code2name[type].items()
 | |
|             
 | |
|     def addmodule(self, module, name, star_imported):
 | |
|         self.modulename = name
 | |
|         self.star_imported = star_imported
 | |
|         for code, name in module._propdeclarations.items():
 | |
|             self.addnamecode('property', name, code)
 | |
|         for code, name in module._classdeclarations.items():
 | |
|             self.addnamecode('class', name, code)
 | |
|         for code in module._enumdeclarations.keys():
 | |
|             self.addnamecode('enum', '_Enum_'+identify(code), code)
 | |
|         for code, name in module._compdeclarations.items():
 | |
|             self.addnamecode('comparison', name, code)
 | |
|         
 | |
|     def prepareforexport(self, name=None):
 | |
|         if not self.modulename:
 | |
|             self.modulename = name
 | |
|         return self
 | |
|             
 | |
| class ObjectCompiler:
 | |
|     def __init__(self, fp, modname, basesuite, othernamemappers=None, interact=1, 
 | |
|             verbose=None):
 | |
|         self.fp = fp
 | |
|         self.verbose = verbose
 | |
|         self.basesuite = basesuite
 | |
|         self.can_interact = interact
 | |
|         self.modulename = modname
 | |
|         self.namemappers = [CodeNameMapper(self.can_interact, self.verbose)]
 | |
|         if othernamemappers:
 | |
|             self.othernamemappers = othernamemappers[:]
 | |
|         else:
 | |
|             self.othernamemappers = []
 | |
|         if basesuite:
 | |
|             basemapper = CodeNameMapper(self.can_interact, self.verbose)
 | |
|             basemapper.addmodule(basesuite, '', 1)
 | |
|             self.namemappers.append(basemapper)
 | |
|         
 | |
|     def getprecompinfo(self, modname):
 | |
|         list = []
 | |
|         for mapper in self.namemappers:
 | |
|             emapper = mapper.prepareforexport(modname)
 | |
|             if emapper:
 | |
|                 list.append(emapper)
 | |
|         return list
 | |
|         
 | |
|     def findcodename(self, type, code):
 | |
|         while 1:
 | |
|             # First try: check whether we already know about this code.
 | |
|             for mapper in self.namemappers:
 | |
|                 if mapper.hascode(type, code):
 | |
|                     return mapper.findcodename(type, code)
 | |
|             # Second try: maybe one of the other modules knows about it.
 | |
|             for mapper in self.othernamemappers:
 | |
|                 if mapper.hascode(type, code):
 | |
|                     self.othernamemappers.remove(mapper)
 | |
|                     self.namemappers.append(mapper)
 | |
|                     if self.fp:
 | |
|                         self.fp.write("import %s\n"%mapper.modulename)
 | |
|                     break
 | |
|             else:
 | |
|                 # If all this has failed we ask the user for a guess on where it could
 | |
|                 # be and retry.
 | |
|                 if self.fp:
 | |
|                     m = self.askdefinitionmodule(type, code)
 | |
|                 else:
 | |
|                     m = None
 | |
|                 if not m: return None, None, None
 | |
|                 mapper = CodeNameMapper(self.can_interact, self.verbose)
 | |
|                 mapper.addmodule(m, m.__name__, 0)
 | |
|                 self.namemappers.append(mapper)
 | |
|                 
 | |
|     def hasname(self, name):
 | |
|         for mapper in self.othernamemappers:
 | |
|             if mapper.hasname(name) and mapper.modulename != self.modulename:
 | |
|                 if self.verbose:
 | |
|                     print >>self.verbose, "Duplicate Python identifier:", name, self.modulename, mapper.modulename
 | |
|                 return True
 | |
|         return False
 | |
|     
 | |
|     def askdefinitionmodule(self, type, code):
 | |
|         if not self.can_interact:
 | |
|             if self.verbose:
 | |
|                 print >>self.verbose, "** No definition for %s '%s' found" % (type, code)
 | |
|             return None
 | |
|         path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
 | |
|         if not path: return
 | |
|         path, file = os.path.split(path)
 | |
|         modname = os.path.splitext(file)[0]
 | |
|         if not path in sys.path:
 | |
|             sys.path.insert(0, path)
 | |
|         m = __import__(modname)
 | |
|         self.fp.write("import %s\n"%modname)
 | |
|         return m
 | |
|         
 | |
|     def compileclass(self, cls):
 | |
|         [name, code, desc, properties, elements] = cls
 | |
|         pname = identify(name)
 | |
|         if self.namemappers[0].hascode('class', code):
 | |
|             # plural forms and such
 | |
|             othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
 | |
|             if self.fp:
 | |
|                 self.fp.write("\n%s = %s\n"%(pname, othername))
 | |
|         else:
 | |
|             if self.fp:
 | |
|                 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
 | |
|                 self.fp.write('    """%s - %s """\n' % (ascii(name), ascii(desc)))
 | |
|                 self.fp.write('    want = %s\n' % `code`)
 | |
|         self.namemappers[0].addnamecode('class', pname, code)
 | |
|         properties.sort()
 | |
|         for prop in properties:
 | |
|             self.compileproperty(prop)
 | |
|         elements.sort()
 | |
|         for elem in elements:
 | |
|             self.compileelement(elem)
 | |
|     
 | |
|     def compileproperty(self, prop):
 | |
|         [name, code, what] = prop
 | |
|         if code == 'c@#!':
 | |
|             # Something silly with plurals. Skip it.
 | |
|             return
 | |
|         pname = identify(name)
 | |
|         if self.namemappers[0].hascode('property', code):
 | |
|             # plural forms and such
 | |
|             othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
 | |
|             if pname == othername:
 | |
|                 return
 | |
|             if self.fp:
 | |
|                 self.fp.write("\n_Prop_%s = _Prop_%s\n"%(pname, othername))
 | |
|         else:
 | |
|             if self.hasname(pname):
 | |
|                 pass
 | |
|             if self.fp:
 | |
|                 self.fp.write("class _Prop_%s(aetools.NProperty):\n" % pname)
 | |
|                 self.fp.write('    """%s - %s """\n' % (ascii(name), ascii(what[1])))
 | |
|                 self.fp.write("    which = %s\n" % `code`)
 | |
|                 self.fp.write("    want = %s\n" % `what[0]`)
 | |
|         self.namemappers[0].addnamecode('property', pname, code)
 | |
|     
 | |
|     def compileelement(self, elem):
 | |
|         [code, keyform] = elem
 | |
|         if self.fp:
 | |
|             self.fp.write("#        element %s as %s\n" % (`code`, keyform))
 | |
| 
 | |
|     def fillclasspropsandelems(self, cls):
 | |
|         [name, code, desc, properties, elements] = cls
 | |
|         cname = identify(name)
 | |
|         if self.namemappers[0].hascode('class', code) and \
 | |
|                 self.namemappers[0].findcodename('class', code)[0] != cname:
 | |
|             # This is an other name (plural or so) for something else. Skip.
 | |
|             if self.fp and (elements or len(properties) > 1 or (len(properties) == 1 and
 | |
|                 properties[0][1] != 'c@#!')):
 | |
|                 if self.verbose:
 | |
|                     print >>self.verbose, '** Skip multiple %s of %s (code %s)' % (cname, self.namemappers[0].findcodename('class', code)[0], `code`)
 | |
|                 raise RuntimeError, "About to skip non-empty class"
 | |
|             return
 | |
|         plist = []
 | |
|         elist = []
 | |
|         superclasses = []
 | |
|         for prop in properties:
 | |
|             [pname, pcode, what] = prop
 | |
|             if pcode == "c@#^":
 | |
|                 superclasses.append(what)
 | |
|             if pcode == 'c@#!':
 | |
|                 continue
 | |
|             pname = identify(pname)
 | |
|             plist.append(pname)
 | |
| 
 | |
|         superclassnames = []
 | |
|         for superclass in superclasses:
 | |
|             superId, superDesc, dummy = superclass
 | |
|             superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
 | |
|             # I don't think this is correct:
 | |
|             if superclassname == cname:
 | |
|                 pass # superclassnames.append(fullyqualifiedname)
 | |
|             else:
 | |
|                 superclassnames.append(superclassname)
 | |
| 
 | |
|         if self.fp:
 | |
|             self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
 | |
| 
 | |
|         for elem in elements:
 | |
|             [ecode, keyform] = elem
 | |
|             if ecode == 'c@#!':
 | |
|                 continue
 | |
|             name, ename, module = self.findcodename('class', ecode)
 | |
|             if not name:
 | |
|                 if self.fp:
 | |
|                     self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
 | |
|             else:
 | |
|                 elist.append((name, ename))
 | |
|                 
 | |
|         plist.sort()
 | |
|         elist.sort()
 | |
|         
 | |
|         if self.fp:
 | |
|             self.fp.write("%s._privpropdict = {\n"%cname)
 | |
|             for n in plist:
 | |
|                 self.fp.write("    '%s' : _Prop_%s,\n"%(n, n))
 | |
|             self.fp.write("}\n")
 | |
|             self.fp.write("%s._privelemdict = {\n"%cname)
 | |
|             for n, fulln in elist:
 | |
|                 self.fp.write("    '%s' : %s,\n"%(n, fulln))
 | |
|             self.fp.write("}\n")
 | |
|     
 | |
|     def compilecomparison(self, comp):
 | |
|         [name, code, comment] = comp
 | |
|         iname = identify(name)
 | |
|         self.namemappers[0].addnamecode('comparison', iname, code)
 | |
|         if self.fp:
 | |
|             self.fp.write("class %s(aetools.NComparison):\n" % iname)
 | |
|             self.fp.write('    """%s - %s """\n' % (ascii(name), ascii(comment)))
 | |
|         
 | |
|     def compileenumeration(self, enum):
 | |
|         [code, items] = enum
 | |
|         name = "_Enum_%s" % identify(code)
 | |
|         if self.fp:
 | |
|             self.fp.write("%s = {\n" % name)
 | |
|             for item in items:
 | |
|                 self.compileenumerator(item)
 | |
|             self.fp.write("}\n\n")
 | |
|         self.namemappers[0].addnamecode('enum', name, code)
 | |
|         return code
 | |
|     
 | |
|     def compileenumerator(self, item):
 | |
|         [name, code, desc] = item
 | |
|         self.fp.write("    %s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
 | |
|         
 | |
|     def checkforenum(self, enum):
 | |
|         """This enum code is used by an event. Make sure it's available"""
 | |
|         name, fullname, module = self.findcodename('enum', enum)
 | |
|         if not name:
 | |
|             if self.fp:
 | |
|                 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
 | |
|             return
 | |
|         if module:
 | |
|             if self.fp:
 | |
|                 self.fp.write("from %s import %s\n"%(module, name))
 | |
|         
 | |
|     def dumpindex(self):
 | |
|         if not self.fp:
 | |
|             return
 | |
|         self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
 | |
|         
 | |
|         self.fp.write("_classdeclarations = {\n")
 | |
|         classlist = self.namemappers[0].getall('class')
 | |
|         classlist.sort()
 | |
|         for k, v in classlist:
 | |
|             self.fp.write("    %s : %s,\n" % (`k`, v))
 | |
|         self.fp.write("}\n")
 | |
|         
 | |
| ##      self.fp.write("\n_propdeclarations = {\n")
 | |
| ##      proplist = self.namemappers[0].getall('property')
 | |
| ##      proplist.sort()
 | |
| ##      for k, v in proplist:
 | |
| ##          self.fp.write("    %s : _Prop_%s,\n" % (`k`, v))
 | |
| ##      self.fp.write("}\n")
 | |
| ##      
 | |
| ##      self.fp.write("\n_compdeclarations = {\n")
 | |
| ##      complist = self.namemappers[0].getall('comparison')
 | |
| ##      complist.sort()
 | |
| ##      for k, v in complist:
 | |
| ##          self.fp.write("    %s : %s,\n" % (`k`, v))
 | |
| ##      self.fp.write("}\n")
 | |
| ##      
 | |
| ##      self.fp.write("\n_enumdeclarations = {\n")
 | |
| ##      enumlist = self.namemappers[0].getall('enum')
 | |
| ##      enumlist.sort()
 | |
| ##      for k, v in enumlist:
 | |
| ##          self.fp.write("    %s : %s,\n" % (`k`, v))
 | |
| ##      self.fp.write("}\n")
 | |
| 
 | |
| def compiledata(data):
 | |
|     [type, description, flags] = data
 | |
|     return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
 | |
|     
 | |
| def is_null(data):
 | |
|     return data[0] == 'null'
 | |
|     
 | |
| def is_optional(data):
 | |
|     return (data[2] & 0x8000)
 | |
|     
 | |
| def is_enum(data):
 | |
|     return (data[2] & 0x2000)
 | |
|     
 | |
| def getdatadoc(data):
 | |
|     [type, descr, flags] = data
 | |
|     if descr:
 | |
|         return ascii(descr)
 | |
|     if type == '****':
 | |
|         return 'anything'
 | |
|     if type == 'obj ':
 | |
|         return 'an AE object reference'
 | |
|     return "undocumented, typecode %s"%`type`
 | |
| 
 | |
| dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
 | |
| def compiledataflags(flags):
 | |
|     bits = []
 | |
|     for i in range(16):
 | |
|         if flags & (1<<i):
 | |
|             if i in dataflagdict.keys():
 | |
|                 bits.append(dataflagdict[i])
 | |
|             else:
 | |
|                 bits.append(`i`)
 | |
|     return '[%s]' % string.join(bits)
 | |
|     
 | |
| def ascii(str):
 | |
|     """Return a string with all non-ascii characters hex-encoded"""
 | |
|     if type(str) != type(''):
 | |
|         return map(ascii, str)
 | |
|     rv = ''
 | |
|     for c in str:
 | |
|         if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
 | |
|             rv = rv + c
 | |
|         else:
 | |
|             rv = rv + '\\' + 'x%02.2x' % ord(c)
 | |
|     return rv
 | |
|     
 | |
| def identify(str):
 | |
|     """Turn any string into an identifier:
 | |
|     - replace space by _
 | |
|     - replace other illegal chars by _xx_ (hex code)
 | |
|     - prepend _ if the result is a python keyword
 | |
|     """
 | |
|     if not str:
 | |
|         return "empty_ae_name_"
 | |
|     rv = ''
 | |
|     ok = string.ascii_letters + '_'
 | |
|     ok2 = ok + string.digits
 | |
|     for c in str:
 | |
|         if c in ok:
 | |
|             rv = rv + c
 | |
|         elif c == ' ':
 | |
|             rv = rv + '_'
 | |
|         else:
 | |
|             rv = rv + '_%02.2x_'%ord(c)
 | |
|         ok = ok2
 | |
|     if keyword.iskeyword(rv):
 | |
|         rv = rv + '_'
 | |
|     return rv
 | |
| 
 | |
| # Call the main program
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 | |
|     sys.exit(1)
 | 
