test_whitespace_eater_unicode(): Make this test Python 2.1 compatible.
[python/dscho.git] / Mac / scripts / gensuitemodule.py
blob1cd2ef69a5c7e8850257439c86ff6db7529dcae3
1 """
2 gensuitemodule - Generate an AE suite module from an aete/aeut resource
4 Based on aete.py.
6 Reading and understanding this code is left as an exercise to the reader.
7 """
9 import MacOS
10 import EasyDialogs
11 import os
12 import string
13 import sys
14 import types
15 import StringIO
16 import keyword
17 import macresource
18 import aetools
19 import distutils.sysconfig
20 import OSATerminology
21 from Carbon.Res import *
22 import MacOS
24 _MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
25 DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
26 DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
28 def main():
29 if len(sys.argv) > 1:
30 for filename in sys.argv[1:]:
31 processfile(filename)
32 else:
33 # The dialogOptionFlags below allows selection of .app bundles.
34 filename = EasyDialogs.AskFileForOpen(
35 message='Select scriptable application',
36 dialogOptionFlags=0x1056)
37 if not filename:
38 sys.exit(0)
39 try:
40 processfile(filename)
41 except MacOS.Error, arg:
42 print "Error getting terminology:", arg
43 print "Retry, manually parsing resources"
44 processfile_fromresource(filename)
46 def processfile_fromresource(fullname):
47 """Process all resources in a single file"""
48 cur = CurResFile()
49 print "Processing", fullname
50 rf = macresource.open_pathname(fullname)
51 try:
52 UseResFile(rf)
53 resources = []
54 for i in range(Count1Resources('aete')):
55 res = Get1IndResource('aete', 1+i)
56 resources.append(res)
57 for i in range(Count1Resources('aeut')):
58 res = Get1IndResource('aeut', 1+i)
59 resources.append(res)
60 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
61 aetelist = []
62 for res in resources:
63 print "decoding", res.GetResInfo(), "..."
64 data = res.data
65 aete = decode(data)
66 aetelist.append((aete, res.GetResInfo()))
67 finally:
68 if rf <> cur:
69 CloseResFile(rf)
70 UseResFile(cur)
71 # switch back (needed for dialogs in Python)
72 UseResFile(cur)
73 compileaetelist(aetelist, fullname)
75 def processfile(fullname):
76 """Ask an application for its terminology and process that"""
77 aedescobj, launched = OSATerminology.GetSysTerminology(fullname)
78 if launched:
79 print "Launched", fullname
80 raw = aetools.unpack(aedescobj)
81 if not raw:
82 print 'Unpack returned empty value:', raw
83 return
84 if not raw[0].data:
85 print 'Unpack returned value without data:', raw
86 return
87 aedata = raw[0]
88 aete = decode(aedata.data)
89 compileaete(aete, None, fullname)
91 def compileaetelist(aetelist, fullname):
92 for aete, resinfo in aetelist:
93 compileaete(aete, resinfo, fullname)
95 def decode(data):
96 """Decode a resource into a python data structure"""
97 f = StringIO.StringIO(data)
98 aete = generic(getaete, f)
99 aete = simplify(aete)
100 processed = f.tell()
101 unprocessed = len(f.read())
102 total = f.tell()
103 if unprocessed:
104 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
105 (processed, unprocessed, total))
106 return aete
108 def simplify(item):
109 """Recursively replace singleton tuples by their constituent item"""
110 if type(item) is types.ListType:
111 return map(simplify, item)
112 elif type(item) == types.TupleType and len(item) == 2:
113 return simplify(item[1])
114 else:
115 return item
118 # Here follows the aete resource decoder.
119 # It is presented bottom-up instead of top-down because there are direct
120 # references to the lower-level part-decoders from the high-level part-decoders.
122 def getbyte(f, *args):
123 c = f.read(1)
124 if not c:
125 raise EOFError, 'in getbyte' + str(args)
126 return ord(c)
128 def getword(f, *args):
129 getalign(f)
130 s = f.read(2)
131 if len(s) < 2:
132 raise EOFError, 'in getword' + str(args)
133 return (ord(s[0])<<8) | ord(s[1])
135 def getlong(f, *args):
136 getalign(f)
137 s = f.read(4)
138 if len(s) < 4:
139 raise EOFError, 'in getlong' + str(args)
140 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
142 def getostype(f, *args):
143 getalign(f)
144 s = f.read(4)
145 if len(s) < 4:
146 raise EOFError, 'in getostype' + str(args)
147 return s
149 def getpstr(f, *args):
150 c = f.read(1)
151 if len(c) < 1:
152 raise EOFError, 'in getpstr[1]' + str(args)
153 nbytes = ord(c)
154 if nbytes == 0: return ''
155 s = f.read(nbytes)
156 if len(s) < nbytes:
157 raise EOFError, 'in getpstr[2]' + str(args)
158 return s
160 def getalign(f):
161 if f.tell() & 1:
162 c = f.read(1)
163 ##if c <> '\0':
164 ## print 'align:', `c`
166 def getlist(f, description, getitem):
167 count = getword(f)
168 list = []
169 for i in range(count):
170 list.append(generic(getitem, f))
171 getalign(f)
172 return list
174 def alt_generic(what, f, *args):
175 print "generic", `what`, args
176 res = vageneric(what, f, args)
177 print '->', `res`
178 return res
180 def generic(what, f, *args):
181 if type(what) == types.FunctionType:
182 return apply(what, (f,) + args)
183 if type(what) == types.ListType:
184 record = []
185 for thing in what:
186 item = apply(generic, thing[:1] + (f,) + thing[1:])
187 record.append((thing[1], item))
188 return record
189 return "BAD GENERIC ARGS: %s" % `what`
191 getdata = [
192 (getostype, "type"),
193 (getpstr, "description"),
194 (getword, "flags")
196 getargument = [
197 (getpstr, "name"),
198 (getostype, "keyword"),
199 (getdata, "what")
201 getevent = [
202 (getpstr, "name"),
203 (getpstr, "description"),
204 (getostype, "suite code"),
205 (getostype, "event code"),
206 (getdata, "returns"),
207 (getdata, "accepts"),
208 (getlist, "optional arguments", getargument)
210 getproperty = [
211 (getpstr, "name"),
212 (getostype, "code"),
213 (getdata, "what")
215 getelement = [
216 (getostype, "type"),
217 (getlist, "keyform", getostype)
219 getclass = [
220 (getpstr, "name"),
221 (getostype, "class code"),
222 (getpstr, "description"),
223 (getlist, "properties", getproperty),
224 (getlist, "elements", getelement)
226 getcomparison = [
227 (getpstr, "operator name"),
228 (getostype, "operator ID"),
229 (getpstr, "operator comment"),
231 getenumerator = [
232 (getpstr, "enumerator name"),
233 (getostype, "enumerator ID"),
234 (getpstr, "enumerator comment")
236 getenumeration = [
237 (getostype, "enumeration ID"),
238 (getlist, "enumerator", getenumerator)
240 getsuite = [
241 (getpstr, "suite name"),
242 (getpstr, "suite description"),
243 (getostype, "suite ID"),
244 (getword, "suite level"),
245 (getword, "suite version"),
246 (getlist, "events", getevent),
247 (getlist, "classes", getclass),
248 (getlist, "comparisons", getcomparison),
249 (getlist, "enumerations", getenumeration)
251 getaete = [
252 (getword, "major/minor version in BCD"),
253 (getword, "language code"),
254 (getword, "script code"),
255 (getlist, "suites", getsuite)
258 def compileaete(aete, resinfo, fname):
259 """Generate code for a full aete resource. fname passed for doc purposes"""
260 [version, language, script, suites] = aete
261 major, minor = divmod(version, 256)
262 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
263 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
264 if language:
265 packagename = packagename+'_lang%d'%language
266 if script:
267 packagename = packagename+'_script%d'%script
268 if len(packagename) > 27:
269 packagename = packagename[:27]
270 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
271 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
272 if not pathname:
273 return
274 packagename = os.path.split(os.path.normpath(pathname))[1]
275 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
276 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
277 if basepkgname:
278 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
279 if not dirname in sys.path:
280 sys.path.insert(0, dirname)
281 basepackage = __import__(basepkgname)
282 else:
283 basepackage = None
284 suitelist = []
285 allprecompinfo = []
286 allsuites = []
287 for suite in suites:
288 code, suite, pathname, modname, precompinfo = precompilesuite(suite, basepackage)
289 if not code:
290 continue
291 allprecompinfo = allprecompinfo + precompinfo
292 suiteinfo = suite, pathname, modname
293 suitelist.append((code, modname))
294 allsuites.append(suiteinfo)
295 for suiteinfo in allsuites:
296 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
297 initfilename = EasyDialogs.AskFileForSave(message='Package module',
298 savedFileName='__init__.py')
299 if not initfilename:
300 return
301 fp = open(initfilename, 'w')
302 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
303 fp.write('"""\n')
304 fp.write("Package generated from %s\n"%fname)
305 if resinfo:
306 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
307 fp.write('"""\n')
308 fp.write('import aetools\n')
309 fp.write('Error = aetools.Error\n')
310 for code, modname in suitelist:
311 fp.write("import %s\n" % modname)
312 fp.write("\n\n_code_to_module = {\n")
313 for code, modname in suitelist:
314 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
315 fp.write("}\n\n")
316 fp.write("\n\n_code_to_fullname = {\n")
317 for code, modname in suitelist:
318 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
319 fp.write("}\n\n")
320 for code, modname in suitelist:
321 fp.write("from %s import *\n"%modname)
323 # Generate property dicts and element dicts for all types declared in this module
324 fp.write("def getbaseclasses(v):\n")
325 fp.write("\tif hasattr(v, '_superclassnames') and not hasattr(v, '_propdict'):\n")
326 fp.write("\t\tv._propdict = {}\n")
327 fp.write("\t\tv._elemdict = {}\n")
328 fp.write("\t\tfor superclass in v._superclassnames:\n")
329 ## fp.write("\t\t\tgetbaseclasses(superclass)\n")
330 fp.write("\t\t\tv._propdict.update(getattr(eval(superclass), '_privpropdict', {}))\n")
331 fp.write("\t\t\tv._elemdict.update(getattr(eval(superclass), '_privelemdict', {}))\n")
332 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
333 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
334 fp.write("\n")
335 fp.write("import StdSuites\n")
336 if allprecompinfo:
337 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
338 for codenamemapper in allprecompinfo:
339 for k, v in codenamemapper.getall('class'):
340 fp.write("getbaseclasses(%s)\n" % v)
342 # Generate a code-to-name mapper for all of the types (classes) declared in this module
343 if allprecompinfo:
344 fp.write("\n#\n# Indices of types declared in this module\n#\n")
345 fp.write("_classdeclarations = {\n")
346 for codenamemapper in allprecompinfo:
347 for k, v in codenamemapper.getall('class'):
348 fp.write("\t%s : %s,\n" % (`k`, v))
349 fp.write("}\n")
351 if suitelist:
352 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
353 for code, modname in suitelist[1:]:
354 fp.write(",\n\t\t%s_Events"%modname)
355 fp.write(",\n\t\taetools.TalkTo):\n")
356 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
357 fp.write("\t_moduleName = '%s'\n\n"%packagename)
358 fp.close()
360 def precompilesuite(suite, basepackage=None):
361 """Parse a single suite without generating the output. This step is needed
362 so we can resolve recursive references by suites to enums/comps/etc declared
363 in other suites"""
364 [name, desc, code, level, version, events, classes, comps, enums] = suite
366 modname = identify(name)
367 if len(modname) > 28:
368 modname = modname[:27]
369 pathname = EasyDialogs.AskFileForSave(message='Python output file',
370 savedFileName=modname+'.py')
371 if not pathname:
372 return None, None, None, None, None
374 modname = os.path.splitext(os.path.split(pathname)[1])[0]
376 if basepackage and basepackage._code_to_module.has_key(code):
377 # We are an extension of a baseclass (usually an application extending
378 # Standard_Suite or so). Import everything from our base module
379 basemodule = basepackage._code_to_module[code]
380 else:
381 # We are not an extension.
382 basemodule = None
384 enumsneeded = {}
385 for event in events:
386 findenumsinevent(event, enumsneeded)
388 objc = ObjectCompiler(None, basemodule)
389 for cls in classes:
390 objc.compileclass(cls)
391 for cls in classes:
392 objc.fillclasspropsandelems(cls)
393 for comp in comps:
394 objc.compilecomparison(comp)
395 for enum in enums:
396 objc.compileenumeration(enum)
398 for enum in enumsneeded.keys():
399 objc.checkforenum(enum)
401 objc.dumpindex()
403 precompinfo = objc.getprecompinfo(modname)
405 return code, suite, pathname, modname, precompinfo
407 def compilesuite((suite, pathname, modname), major, minor, language, script, fname, basepackage, precompinfo):
408 """Generate code for a single suite"""
409 [name, desc, code, level, version, events, classes, comps, enums] = suite
411 fp = open(pathname, 'w')
412 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
414 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
415 fp.write("Level %d, version %d\n\n" % (level, version))
416 fp.write("Generated from %s\n"%ascii(fname))
417 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
418 (major, minor, language, script))
419 fp.write('"""\n\n')
421 fp.write('import aetools\n')
422 fp.write('import MacOS\n\n')
423 fp.write("_code = %s\n\n"% `code`)
424 if basepackage and basepackage._code_to_module.has_key(code):
425 # We are an extension of a baseclass (usually an application extending
426 # Standard_Suite or so). Import everything from our base module
427 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
428 basemodule = basepackage._code_to_module[code]
429 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
430 # This is needed by CodeWarrior and some others.
431 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
432 basemodule = basepackage._code_to_module[code.lower()]
433 else:
434 # We are not an extension.
435 basemodule = None
436 compileclassheader(fp, modname, basemodule)
438 enumsneeded = {}
439 if events:
440 for event in events:
441 compileevent(fp, event, enumsneeded)
442 else:
443 fp.write("\tpass\n\n")
445 objc = ObjectCompiler(fp, basemodule, precompinfo)
446 for cls in classes:
447 objc.compileclass(cls)
448 for cls in classes:
449 objc.fillclasspropsandelems(cls)
450 for comp in comps:
451 objc.compilecomparison(comp)
452 for enum in enums:
453 objc.compileenumeration(enum)
455 for enum in enumsneeded.keys():
456 objc.checkforenum(enum)
458 objc.dumpindex()
460 return code, modname
462 def compileclassheader(fp, name, module=None):
463 """Generate class boilerplate"""
464 classname = '%s_Events'%name
465 if module:
466 modshortname = string.split(module.__name__, '.')[-1]
467 baseclassname = '%s_Events'%modshortname
468 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
469 else:
470 fp.write("class %s:\n\n"%classname)
472 def compileevent(fp, event, enumsneeded):
473 """Generate code for a single event"""
474 [name, desc, code, subcode, returns, accepts, arguments] = event
475 funcname = identify(name)
477 # generate name->keyword map
479 if arguments:
480 fp.write("\t_argmap_%s = {\n"%funcname)
481 for a in arguments:
482 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
483 fp.write("\t}\n\n")
486 # Generate function header
488 has_arg = (not is_null(accepts))
489 opt_arg = (has_arg and is_optional(accepts))
491 fp.write("\tdef %s(self, "%funcname)
492 if has_arg:
493 if not opt_arg:
494 fp.write("_object, ") # Include direct object, if it has one
495 else:
496 fp.write("_object=None, ") # Also include if it is optional
497 else:
498 fp.write("_no_object=None, ") # For argument checking
499 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
501 # Generate doc string (important, since it may be the only
502 # available documentation, due to our name-remaping)
504 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
505 if has_arg:
506 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
507 elif opt_arg:
508 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
509 for arg in arguments:
510 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
511 getdatadoc(arg[2])))
512 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
513 if not is_null(returns):
514 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
515 fp.write('\t\t"""\n')
517 # Fiddle the args so everything ends up in 'arguments' dictionary
519 fp.write("\t\t_code = %s\n"% `code`)
520 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
522 # Do keyword name substitution
524 if arguments:
525 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
526 else:
527 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
529 # Stuff required arg (if there is one) into arguments
531 if has_arg:
532 fp.write("\t\t_arguments['----'] = _object\n")
533 elif opt_arg:
534 fp.write("\t\tif _object:\n")
535 fp.write("\t\t\t_arguments['----'] = _object\n")
536 else:
537 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
538 fp.write("\n")
540 # Do enum-name substitution
542 for a in arguments:
543 if is_enum(a[2]):
544 kname = a[1]
545 ename = a[2][0]
546 if ename <> '****':
547 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
548 (`kname`, identify(ename)))
549 enumsneeded[ename] = 1
550 fp.write("\n")
552 # Do the transaction
554 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
555 fp.write("\t\t\t\t_arguments, _attributes)\n")
557 # Error handling
559 fp.write("\t\tif _arguments.get('errn', 0):\n")
560 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
561 fp.write("\t\t# XXXX Optionally decode result\n")
563 # Decode result
565 fp.write("\t\tif _arguments.has_key('----'):\n")
566 if is_enum(returns):
567 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
568 fp.write("\t\t\treturn _arguments['----']\n")
569 fp.write("\n")
571 # print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
572 # print "# returns", compiledata(returns)
573 # print "# accepts", compiledata(accepts)
574 # for arg in arguments:
575 # compileargument(arg)
577 def compileargument(arg):
578 [name, keyword, what] = arg
579 print "# %s (%s)" % (name, `keyword`), compiledata(what)
581 def findenumsinevent(event, enumsneeded):
582 """Find all enums for a single event"""
583 [name, desc, code, subcode, returns, accepts, arguments] = event
584 for a in arguments:
585 if is_enum(a[2]):
586 ename = a[2][0]
587 if ename <> '****':
588 enumsneeded[ename] = 1
591 # This class stores the code<->name translations for a single module. It is used
592 # to keep the information while we're compiling the module, but we also keep these objects
593 # around so if one suite refers to, say, an enum in another suite we know where to
594 # find it. Finally, if we really can't find a code, the user can add modules by
595 # hand.
597 class CodeNameMapper:
599 def __init__(self):
600 self.code2name = {
601 "property" : {},
602 "class" : {},
603 "enum" : {},
604 "comparison" : {},
606 self.name2code = {
607 "property" : {},
608 "class" : {},
609 "enum" : {},
610 "comparison" : {},
612 self.modulename = None
613 self.star_imported = 0
615 def addnamecode(self, type, name, code):
616 self.name2code[type][name] = code
617 if not self.code2name[type].has_key(code):
618 self.code2name[type][code] = name
620 def hasname(self, type, name):
621 return self.name2code[type].has_key(name)
623 def hascode(self, type, code):
624 return self.code2name[type].has_key(code)
626 def findcodename(self, type, code):
627 if not self.hascode(type, code):
628 return None, None, None
629 name = self.code2name[type][code]
630 if self.modulename and not self.star_imported:
631 qualname = '%s.%s'%(self.modulename, name)
632 else:
633 qualname = name
634 return name, qualname, self.modulename
636 def getall(self, type):
637 return self.code2name[type].items()
639 def addmodule(self, module, name, star_imported):
640 self.modulename = name
641 self.star_imported = star_imported
642 for code, name in module._propdeclarations.items():
643 self.addnamecode('property', name, code)
644 for code, name in module._classdeclarations.items():
645 self.addnamecode('class', name, code)
646 for code in module._enumdeclarations.keys():
647 self.addnamecode('enum', '_Enum_'+identify(code), code)
648 for code, name in module._compdeclarations.items():
649 self.addnamecode('comparison', name, code)
651 def prepareforexport(self, name=None):
652 if not self.modulename:
653 self.modulename = name
654 return self
656 class ObjectCompiler:
657 def __init__(self, fp, basesuite=None, othernamemappers=None):
658 self.fp = fp
659 self.basesuite = basesuite
660 self.namemappers = [CodeNameMapper()]
661 if othernamemappers:
662 self.othernamemappers = othernamemappers[:]
663 else:
664 self.othernamemappers = []
665 if basesuite:
666 basemapper = CodeNameMapper()
667 basemapper.addmodule(basesuite, '', 1)
668 self.namemappers.append(basemapper)
670 def getprecompinfo(self, modname):
671 list = []
672 for mapper in self.namemappers:
673 emapper = mapper.prepareforexport(modname)
674 if emapper:
675 list.append(emapper)
676 return list
678 def findcodename(self, type, code):
679 while 1:
680 # First try: check whether we already know about this code.
681 for mapper in self.namemappers:
682 if mapper.hascode(type, code):
683 return mapper.findcodename(type, code)
684 # Second try: maybe one of the other modules knows about it.
685 for mapper in self.othernamemappers:
686 if mapper.hascode(type, code):
687 self.othernamemappers.remove(mapper)
688 self.namemappers.append(mapper)
689 if self.fp:
690 self.fp.write("import %s\n"%mapper.modulename)
691 break
692 else:
693 # If all this has failed we ask the user for a guess on where it could
694 # be and retry.
695 if self.fp:
696 m = self.askdefinitionmodule(type, code)
697 else:
698 m = None
699 if not m: return None, None, None
700 mapper = CodeNameMapper()
701 mapper.addmodule(m, m.__name__, 0)
702 self.namemappers.append(mapper)
704 def askdefinitionmodule(self, type, code):
705 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
706 if not path: return
707 path, file = os.path.split(path)
708 modname = os.path.splitext(file)[0]
709 if not path in sys.path:
710 sys.path.insert(0, path)
711 m = __import__(modname)
712 self.fp.write("import %s\n"%modname)
713 return m
715 def compileclass(self, cls):
716 [name, code, desc, properties, elements] = cls
717 pname = identify(name)
718 if self.namemappers[0].hascode('class', code):
719 # plural forms and such
720 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
721 if self.fp:
722 self.fp.write("\n%s = %s\n"%(pname, othername))
723 else:
724 if self.fp:
725 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
726 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
727 self.fp.write('\twant = %s\n' % `code`)
728 self.namemappers[0].addnamecode('class', pname, code)
729 for prop in properties:
730 self.compileproperty(prop)
731 for elem in elements:
732 self.compileelement(elem)
734 def compileproperty(self, prop):
735 [name, code, what] = prop
736 if code == 'c@#!':
737 # Something silly with plurals. Skip it.
738 return
739 pname = identify(name)
740 if self.namemappers[0].hascode('property', code):
741 # plural forms and such
742 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
743 if pname == othername:
744 return
745 if self.fp:
746 self.fp.write("\n%s = %s\n"%(pname, othername))
747 else:
748 if self.fp:
749 self.fp.write("class %s(aetools.NProperty):\n" % pname)
750 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
751 self.fp.write("\twhich = %s\n" % `code`)
752 self.fp.write("\twant = %s\n" % `what[0]`)
753 self.namemappers[0].addnamecode('property', pname, code)
755 def compileelement(self, elem):
756 [code, keyform] = elem
757 if self.fp:
758 self.fp.write("# element %s as %s\n" % (`code`, keyform))
760 def fillclasspropsandelems(self, cls):
761 [name, code, desc, properties, elements] = cls
762 cname = identify(name)
763 if self.namemappers[0].hascode('class', code) and \
764 self.namemappers[0].findcodename('class', code)[0] != cname:
765 # This is an other name (plural or so) for something else. Skip.
766 return
767 plist = []
768 elist = []
769 superclasses = []
770 for prop in properties:
771 [pname, pcode, what] = prop
772 if pcode == "c@#^":
773 superclasses.append(what)
774 if pcode == 'c@#!':
775 continue
776 pname = identify(pname)
777 plist.append(pname)
779 superclassnames = []
780 for superclass in superclasses:
781 superId, superDesc, dummy = superclass
782 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
783 superclassnames.append(superclassname)
785 if self.fp:
786 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
788 for elem in elements:
789 [ecode, keyform] = elem
790 if ecode == 'c@#!':
791 continue
792 name, ename, module = self.findcodename('class', ecode)
793 if not name:
794 if self.fp:
795 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
796 else:
797 elist.append((name, ename))
799 if self.fp:
800 self.fp.write("%s._privpropdict = {\n"%cname)
801 for n in plist:
802 self.fp.write("\t'%s' : %s,\n"%(n, n))
803 self.fp.write("}\n")
804 self.fp.write("%s._privelemdict = {\n"%cname)
805 for n, fulln in elist:
806 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
807 self.fp.write("}\n")
809 def compilecomparison(self, comp):
810 [name, code, comment] = comp
811 iname = identify(name)
812 self.namemappers[0].addnamecode('comparison', iname, code)
813 if self.fp:
814 self.fp.write("class %s(aetools.NComparison):\n" % iname)
815 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
817 def compileenumeration(self, enum):
818 [code, items] = enum
819 name = "_Enum_%s" % identify(code)
820 if self.fp:
821 self.fp.write("%s = {\n" % name)
822 for item in items:
823 self.compileenumerator(item)
824 self.fp.write("}\n\n")
825 self.namemappers[0].addnamecode('enum', name, code)
826 return code
828 def compileenumerator(self, item):
829 [name, code, desc] = item
830 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
832 def checkforenum(self, enum):
833 """This enum code is used by an event. Make sure it's available"""
834 name, fullname, module = self.findcodename('enum', enum)
835 if not name:
836 if self.fp:
837 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
838 return
839 if module:
840 if self.fp:
841 self.fp.write("from %s import %s\n"%(module, name))
843 def dumpindex(self):
844 if not self.fp:
845 return
846 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
847 self.fp.write("_classdeclarations = {\n")
848 for k, v in self.namemappers[0].getall('class'):
849 self.fp.write("\t%s : %s,\n" % (`k`, v))
850 self.fp.write("}\n")
851 self.fp.write("\n_propdeclarations = {\n")
852 for k, v in self.namemappers[0].getall('property'):
853 self.fp.write("\t%s : %s,\n" % (`k`, v))
854 self.fp.write("}\n")
855 self.fp.write("\n_compdeclarations = {\n")
856 for k, v in self.namemappers[0].getall('comparison'):
857 self.fp.write("\t%s : %s,\n" % (`k`, v))
858 self.fp.write("}\n")
859 self.fp.write("\n_enumdeclarations = {\n")
860 for k, v in self.namemappers[0].getall('enum'):
861 self.fp.write("\t%s : %s,\n" % (`k`, v))
862 self.fp.write("}\n")
864 def compiledata(data):
865 [type, description, flags] = data
866 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
868 def is_null(data):
869 return data[0] == 'null'
871 def is_optional(data):
872 return (data[2] & 0x8000)
874 def is_enum(data):
875 return (data[2] & 0x2000)
877 def getdatadoc(data):
878 [type, descr, flags] = data
879 if descr:
880 return ascii(descr)
881 if type == '****':
882 return 'anything'
883 if type == 'obj ':
884 return 'an AE object reference'
885 return "undocumented, typecode %s"%`type`
887 dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
888 def compiledataflags(flags):
889 bits = []
890 for i in range(16):
891 if flags & (1<<i):
892 if i in dataflagdict.keys():
893 bits.append(dataflagdict[i])
894 else:
895 bits.append(`i`)
896 return '[%s]' % string.join(bits)
898 def ascii(str):
899 """Return a string with all non-ascii characters hex-encoded"""
900 if type(str) != type(''):
901 return map(ascii, str)
902 rv = ''
903 for c in str:
904 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
905 rv = rv + c
906 else:
907 rv = rv + '\\' + 'x%02.2x' % ord(c)
908 return rv
910 def identify(str):
911 """Turn any string into an identifier:
912 - replace space by _
913 - replace other illegal chars by _xx_ (hex code)
914 - prepend _ if the result is a python keyword
916 if not str:
917 return "empty_ae_name_"
918 rv = ''
919 ok = string.ascii_letters + '_'
920 ok2 = ok + string.digits
921 for c in str:
922 if c in ok:
923 rv = rv + c
924 elif c == ' ':
925 rv = rv + '_'
926 else:
927 rv = rv + '_%02.2x_'%ord(c)
928 ok = ok2
929 if keyword.iskeyword(rv):
930 rv = rv + '_'
931 return rv
933 # Call the main program
935 if __name__ == '__main__':
936 main()
937 sys.exit(1)