|
1 """ |
|
2 gensuitemodule - Generate an AE suite module from an aete/aeut resource |
|
3 |
|
4 Based on aete.py. |
|
5 |
|
6 Reading and understanding this code is left as an exercise to the reader. |
|
7 """ |
|
8 |
|
9 from warnings import warnpy3k |
|
10 warnpy3k("In 3.x, the gensuitemodule module is removed.", stacklevel=2) |
|
11 |
|
12 import MacOS |
|
13 import EasyDialogs |
|
14 import os |
|
15 import string |
|
16 import sys |
|
17 import types |
|
18 import StringIO |
|
19 import keyword |
|
20 import macresource |
|
21 import aetools |
|
22 import distutils.sysconfig |
|
23 import OSATerminology |
|
24 from Carbon.Res import * |
|
25 import Carbon.Folder |
|
26 import MacOS |
|
27 import getopt |
|
28 import plistlib |
|
29 |
|
30 _MAC_LIB_FOLDER=os.path.dirname(aetools.__file__) |
|
31 DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages') |
|
32 DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib() |
|
33 |
|
34 def usage(): |
|
35 sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0]) |
|
36 sys.stderr.write("""Options: |
|
37 --output pkgdir Pathname of the output package (short: -o) |
|
38 --resource Parse resource file in stead of launching application (-r) |
|
39 --base package Use another base package in stead of default StdSuites (-b) |
|
40 --edit old=new Edit suite names, use empty new to skip a suite (-e) |
|
41 --creator code Set creator code for package (-c) |
|
42 --dump Dump aete resource to stdout in stead of creating module (-d) |
|
43 --verbose Tell us what happens (-v) |
|
44 """) |
|
45 sys.exit(1) |
|
46 |
|
47 def main(): |
|
48 if len(sys.argv) > 1: |
|
49 SHORTOPTS = "rb:o:e:c:dv" |
|
50 LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=", "dump", "verbose") |
|
51 try: |
|
52 opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS) |
|
53 except getopt.GetoptError: |
|
54 usage() |
|
55 |
|
56 process_func = processfile |
|
57 basepkgname = 'StdSuites' |
|
58 output = None |
|
59 edit_modnames = [] |
|
60 creatorsignature = None |
|
61 dump = None |
|
62 verbose = None |
|
63 |
|
64 for o, a in opts: |
|
65 if o in ('-r', '--resource'): |
|
66 process_func = processfile_fromresource |
|
67 if o in ('-b', '--base'): |
|
68 basepkgname = a |
|
69 if o in ('-o', '--output'): |
|
70 output = a |
|
71 if o in ('-e', '--edit'): |
|
72 split = a.split('=') |
|
73 if len(split) != 2: |
|
74 usage() |
|
75 edit_modnames.append(split) |
|
76 if o in ('-c', '--creator'): |
|
77 if len(a) != 4: |
|
78 sys.stderr.write("creator must be 4-char string\n") |
|
79 sys.exit(1) |
|
80 creatorsignature = a |
|
81 if o in ('-d', '--dump'): |
|
82 dump = sys.stdout |
|
83 if o in ('-v', '--verbose'): |
|
84 verbose = sys.stderr |
|
85 |
|
86 |
|
87 if output and len(args) > 1: |
|
88 sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0]) |
|
89 sys.exit(1) |
|
90 |
|
91 for filename in args: |
|
92 process_func(filename, output=output, basepkgname=basepkgname, |
|
93 edit_modnames=edit_modnames, creatorsignature=creatorsignature, |
|
94 dump=dump, verbose=verbose) |
|
95 else: |
|
96 main_interactive() |
|
97 |
|
98 def main_interactive(interact=0, basepkgname='StdSuites'): |
|
99 if interact: |
|
100 # Ask for save-filename for each module |
|
101 edit_modnames = None |
|
102 else: |
|
103 # Use default filenames for each module |
|
104 edit_modnames = [] |
|
105 appsfolder = Carbon.Folder.FSFindFolder(-32765, 'apps', 0) |
|
106 filename = EasyDialogs.AskFileForOpen( |
|
107 message='Select scriptable application', |
|
108 dialogOptionFlags=0x1056, # allow selection of .app bundles |
|
109 defaultLocation=appsfolder) |
|
110 if not filename: |
|
111 return |
|
112 if not is_scriptable(filename): |
|
113 if EasyDialogs.AskYesNoCancel( |
|
114 "Warning: application does not seem scriptable", |
|
115 yes="Continue", default=2, no="") <= 0: |
|
116 return |
|
117 try: |
|
118 processfile(filename, edit_modnames=edit_modnames, basepkgname=basepkgname, |
|
119 verbose=sys.stderr) |
|
120 except MacOS.Error, arg: |
|
121 print "Error getting terminology:", arg |
|
122 print "Retry, manually parsing resources" |
|
123 processfile_fromresource(filename, edit_modnames=edit_modnames, |
|
124 basepkgname=basepkgname, verbose=sys.stderr) |
|
125 |
|
126 def is_scriptable(application): |
|
127 """Return true if the application is scriptable""" |
|
128 if os.path.isdir(application): |
|
129 plistfile = os.path.join(application, 'Contents', 'Info.plist') |
|
130 if not os.path.exists(plistfile): |
|
131 return False |
|
132 plist = plistlib.Plist.fromFile(plistfile) |
|
133 return plist.get('NSAppleScriptEnabled', False) |
|
134 # If it is a file test for an aete/aeut resource. |
|
135 currf = CurResFile() |
|
136 try: |
|
137 refno = macresource.open_pathname(application) |
|
138 except MacOS.Error: |
|
139 return False |
|
140 UseResFile(refno) |
|
141 n_terminology = Count1Resources('aete') + Count1Resources('aeut') + \ |
|
142 Count1Resources('scsz') + Count1Resources('osiz') |
|
143 CloseResFile(refno) |
|
144 UseResFile(currf) |
|
145 return n_terminology > 0 |
|
146 |
|
147 def processfile_fromresource(fullname, output=None, basepkgname=None, |
|
148 edit_modnames=None, creatorsignature=None, dump=None, verbose=None): |
|
149 """Process all resources in a single file""" |
|
150 if not is_scriptable(fullname) and verbose: |
|
151 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname |
|
152 cur = CurResFile() |
|
153 if verbose: |
|
154 print >>verbose, "Processing", fullname |
|
155 rf = macresource.open_pathname(fullname) |
|
156 try: |
|
157 UseResFile(rf) |
|
158 resources = [] |
|
159 for i in range(Count1Resources('aete')): |
|
160 res = Get1IndResource('aete', 1+i) |
|
161 resources.append(res) |
|
162 for i in range(Count1Resources('aeut')): |
|
163 res = Get1IndResource('aeut', 1+i) |
|
164 resources.append(res) |
|
165 if verbose: |
|
166 print >>verbose, "\nLISTING aete+aeut RESOURCES IN", repr(fullname) |
|
167 aetelist = [] |
|
168 for res in resources: |
|
169 if verbose: |
|
170 print >>verbose, "decoding", res.GetResInfo(), "..." |
|
171 data = res.data |
|
172 aete = decode(data, verbose) |
|
173 aetelist.append((aete, res.GetResInfo())) |
|
174 finally: |
|
175 if rf <> cur: |
|
176 CloseResFile(rf) |
|
177 UseResFile(cur) |
|
178 # switch back (needed for dialogs in Python) |
|
179 UseResFile(cur) |
|
180 if dump: |
|
181 dumpaetelist(aetelist, dump) |
|
182 compileaetelist(aetelist, fullname, output=output, |
|
183 basepkgname=basepkgname, edit_modnames=edit_modnames, |
|
184 creatorsignature=creatorsignature, verbose=verbose) |
|
185 |
|
186 def processfile(fullname, output=None, basepkgname=None, |
|
187 edit_modnames=None, creatorsignature=None, dump=None, |
|
188 verbose=None): |
|
189 """Ask an application for its terminology and process that""" |
|
190 if not is_scriptable(fullname) and verbose: |
|
191 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname |
|
192 if verbose: |
|
193 print >>verbose, "\nASKING FOR aete DICTIONARY IN", repr(fullname) |
|
194 try: |
|
195 aedescobj, launched = OSATerminology.GetAppTerminology(fullname) |
|
196 except MacOS.Error, arg: |
|
197 if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound |
|
198 if verbose: |
|
199 print >>verbose, "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually" |
|
200 aedata, sig = getappterminology(fullname, verbose=verbose) |
|
201 if not creatorsignature: |
|
202 creatorsignature = sig |
|
203 else: |
|
204 raise |
|
205 else: |
|
206 if launched: |
|
207 if verbose: |
|
208 print >>verbose, "Launched", fullname |
|
209 raw = aetools.unpack(aedescobj) |
|
210 if not raw: |
|
211 if verbose: |
|
212 print >>verbose, 'Unpack returned empty value:', raw |
|
213 return |
|
214 if not raw[0].data: |
|
215 if verbose: |
|
216 print >>verbose, 'Unpack returned value without data:', raw |
|
217 return |
|
218 aedata = raw[0] |
|
219 aete = decode(aedata.data, verbose) |
|
220 if dump: |
|
221 dumpaetelist([aete], dump) |
|
222 return |
|
223 compileaete(aete, None, fullname, output=output, basepkgname=basepkgname, |
|
224 creatorsignature=creatorsignature, edit_modnames=edit_modnames, |
|
225 verbose=verbose) |
|
226 |
|
227 def getappterminology(fullname, verbose=None): |
|
228 """Get application terminology by sending an AppleEvent""" |
|
229 # First check that we actually can send AppleEvents |
|
230 if not MacOS.WMAvailable(): |
|
231 raise RuntimeError, "Cannot send AppleEvents, no access to window manager" |
|
232 # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless |
|
233 # you have created an event loop first. |
|
234 import Carbon.Evt |
|
235 Carbon.Evt.WaitNextEvent(0,0) |
|
236 if os.path.isdir(fullname): |
|
237 # Now get the signature of the application, hoping it is a bundle |
|
238 pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo') |
|
239 if not os.path.exists(pkginfo): |
|
240 raise RuntimeError, "No PkgInfo file found" |
|
241 tp_cr = open(pkginfo, 'rb').read() |
|
242 cr = tp_cr[4:8] |
|
243 else: |
|
244 # Assume it is a file |
|
245 cr, tp = MacOS.GetCreatorAndType(fullname) |
|
246 # Let's talk to it and ask for its AETE |
|
247 talker = aetools.TalkTo(cr) |
|
248 try: |
|
249 talker._start() |
|
250 except (MacOS.Error, aetools.Error), arg: |
|
251 if verbose: |
|
252 print >>verbose, 'Warning: start() failed, continuing anyway:', arg |
|
253 reply = talker.send("ascr", "gdte") |
|
254 #reply2 = talker.send("ascr", "gdut") |
|
255 # Now pick the bits out of the return that we need. |
|
256 return reply[1]['----'], cr |
|
257 |
|
258 |
|
259 def compileaetelist(aetelist, fullname, output=None, basepkgname=None, |
|
260 edit_modnames=None, creatorsignature=None, verbose=None): |
|
261 for aete, resinfo in aetelist: |
|
262 compileaete(aete, resinfo, fullname, output=output, |
|
263 basepkgname=basepkgname, edit_modnames=edit_modnames, |
|
264 creatorsignature=creatorsignature, verbose=verbose) |
|
265 |
|
266 def dumpaetelist(aetelist, output): |
|
267 import pprint |
|
268 pprint.pprint(aetelist, output) |
|
269 |
|
270 def decode(data, verbose=None): |
|
271 """Decode a resource into a python data structure""" |
|
272 f = StringIO.StringIO(data) |
|
273 aete = generic(getaete, f) |
|
274 aete = simplify(aete) |
|
275 processed = f.tell() |
|
276 unprocessed = len(f.read()) |
|
277 total = f.tell() |
|
278 if unprocessed and verbose: |
|
279 verbose.write("%d processed + %d unprocessed = %d total\n" % |
|
280 (processed, unprocessed, total)) |
|
281 return aete |
|
282 |
|
283 def simplify(item): |
|
284 """Recursively replace singleton tuples by their constituent item""" |
|
285 if type(item) is types.ListType: |
|
286 return map(simplify, item) |
|
287 elif type(item) == types.TupleType and len(item) == 2: |
|
288 return simplify(item[1]) |
|
289 else: |
|
290 return item |
|
291 |
|
292 |
|
293 # Here follows the aete resource decoder. |
|
294 # It is presented bottom-up instead of top-down because there are direct |
|
295 # references to the lower-level part-decoders from the high-level part-decoders. |
|
296 |
|
297 def getbyte(f, *args): |
|
298 c = f.read(1) |
|
299 if not c: |
|
300 raise EOFError, 'in getbyte' + str(args) |
|
301 return ord(c) |
|
302 |
|
303 def getword(f, *args): |
|
304 getalign(f) |
|
305 s = f.read(2) |
|
306 if len(s) < 2: |
|
307 raise EOFError, 'in getword' + str(args) |
|
308 return (ord(s[0])<<8) | ord(s[1]) |
|
309 |
|
310 def getlong(f, *args): |
|
311 getalign(f) |
|
312 s = f.read(4) |
|
313 if len(s) < 4: |
|
314 raise EOFError, 'in getlong' + str(args) |
|
315 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3]) |
|
316 |
|
317 def getostype(f, *args): |
|
318 getalign(f) |
|
319 s = f.read(4) |
|
320 if len(s) < 4: |
|
321 raise EOFError, 'in getostype' + str(args) |
|
322 return s |
|
323 |
|
324 def getpstr(f, *args): |
|
325 c = f.read(1) |
|
326 if len(c) < 1: |
|
327 raise EOFError, 'in getpstr[1]' + str(args) |
|
328 nbytes = ord(c) |
|
329 if nbytes == 0: return '' |
|
330 s = f.read(nbytes) |
|
331 if len(s) < nbytes: |
|
332 raise EOFError, 'in getpstr[2]' + str(args) |
|
333 return s |
|
334 |
|
335 def getalign(f): |
|
336 if f.tell() & 1: |
|
337 c = f.read(1) |
|
338 ##if c <> '\0': |
|
339 ## print align:', repr(c) |
|
340 |
|
341 def getlist(f, description, getitem): |
|
342 count = getword(f) |
|
343 list = [] |
|
344 for i in range(count): |
|
345 list.append(generic(getitem, f)) |
|
346 getalign(f) |
|
347 return list |
|
348 |
|
349 def alt_generic(what, f, *args): |
|
350 print "generic", repr(what), args |
|
351 res = vageneric(what, f, args) |
|
352 print '->', repr(res) |
|
353 return res |
|
354 |
|
355 def generic(what, f, *args): |
|
356 if type(what) == types.FunctionType: |
|
357 return apply(what, (f,) + args) |
|
358 if type(what) == types.ListType: |
|
359 record = [] |
|
360 for thing in what: |
|
361 item = apply(generic, thing[:1] + (f,) + thing[1:]) |
|
362 record.append((thing[1], item)) |
|
363 return record |
|
364 return "BAD GENERIC ARGS: %r" % (what,) |
|
365 |
|
366 getdata = [ |
|
367 (getostype, "type"), |
|
368 (getpstr, "description"), |
|
369 (getword, "flags") |
|
370 ] |
|
371 getargument = [ |
|
372 (getpstr, "name"), |
|
373 (getostype, "keyword"), |
|
374 (getdata, "what") |
|
375 ] |
|
376 getevent = [ |
|
377 (getpstr, "name"), |
|
378 (getpstr, "description"), |
|
379 (getostype, "suite code"), |
|
380 (getostype, "event code"), |
|
381 (getdata, "returns"), |
|
382 (getdata, "accepts"), |
|
383 (getlist, "optional arguments", getargument) |
|
384 ] |
|
385 getproperty = [ |
|
386 (getpstr, "name"), |
|
387 (getostype, "code"), |
|
388 (getdata, "what") |
|
389 ] |
|
390 getelement = [ |
|
391 (getostype, "type"), |
|
392 (getlist, "keyform", getostype) |
|
393 ] |
|
394 getclass = [ |
|
395 (getpstr, "name"), |
|
396 (getostype, "class code"), |
|
397 (getpstr, "description"), |
|
398 (getlist, "properties", getproperty), |
|
399 (getlist, "elements", getelement) |
|
400 ] |
|
401 getcomparison = [ |
|
402 (getpstr, "operator name"), |
|
403 (getostype, "operator ID"), |
|
404 (getpstr, "operator comment"), |
|
405 ] |
|
406 getenumerator = [ |
|
407 (getpstr, "enumerator name"), |
|
408 (getostype, "enumerator ID"), |
|
409 (getpstr, "enumerator comment") |
|
410 ] |
|
411 getenumeration = [ |
|
412 (getostype, "enumeration ID"), |
|
413 (getlist, "enumerator", getenumerator) |
|
414 ] |
|
415 getsuite = [ |
|
416 (getpstr, "suite name"), |
|
417 (getpstr, "suite description"), |
|
418 (getostype, "suite ID"), |
|
419 (getword, "suite level"), |
|
420 (getword, "suite version"), |
|
421 (getlist, "events", getevent), |
|
422 (getlist, "classes", getclass), |
|
423 (getlist, "comparisons", getcomparison), |
|
424 (getlist, "enumerations", getenumeration) |
|
425 ] |
|
426 getaete = [ |
|
427 (getword, "major/minor version in BCD"), |
|
428 (getword, "language code"), |
|
429 (getword, "script code"), |
|
430 (getlist, "suites", getsuite) |
|
431 ] |
|
432 |
|
433 def compileaete(aete, resinfo, fname, output=None, basepkgname=None, |
|
434 edit_modnames=None, creatorsignature=None, verbose=None): |
|
435 """Generate code for a full aete resource. fname passed for doc purposes""" |
|
436 [version, language, script, suites] = aete |
|
437 major, minor = divmod(version, 256) |
|
438 if not creatorsignature: |
|
439 creatorsignature, dummy = MacOS.GetCreatorAndType(fname) |
|
440 packagename = identify(os.path.splitext(os.path.basename(fname))[0]) |
|
441 if language: |
|
442 packagename = packagename+'_lang%d'%language |
|
443 if script: |
|
444 packagename = packagename+'_script%d'%script |
|
445 if len(packagename) > 27: |
|
446 packagename = packagename[:27] |
|
447 if output: |
|
448 # XXXX Put this in site-packages if it isn't a full pathname? |
|
449 if not os.path.exists(output): |
|
450 os.mkdir(output) |
|
451 pathname = output |
|
452 else: |
|
453 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename, |
|
454 defaultLocation=DEFAULT_USER_PACKAGEFOLDER) |
|
455 output = pathname |
|
456 if not pathname: |
|
457 return |
|
458 packagename = os.path.split(os.path.normpath(pathname))[1] |
|
459 if not basepkgname: |
|
460 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)', |
|
461 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER) |
|
462 if basepkgname: |
|
463 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname)) |
|
464 if dirname and not dirname in sys.path: |
|
465 sys.path.insert(0, dirname) |
|
466 basepackage = __import__(basepkgname) |
|
467 else: |
|
468 basepackage = None |
|
469 suitelist = [] |
|
470 allprecompinfo = [] |
|
471 allsuites = [] |
|
472 for suite in suites: |
|
473 compiler = SuiteCompiler(suite, basepackage, output, edit_modnames, verbose) |
|
474 code, modname, precompinfo = compiler.precompilesuite() |
|
475 if not code: |
|
476 continue |
|
477 allprecompinfo = allprecompinfo + precompinfo |
|
478 suiteinfo = suite, pathname, modname |
|
479 suitelist.append((code, modname)) |
|
480 allsuites.append(compiler) |
|
481 for compiler in allsuites: |
|
482 compiler.compilesuite(major, minor, language, script, fname, allprecompinfo) |
|
483 initfilename = os.path.join(output, '__init__.py') |
|
484 fp = open(initfilename, 'w') |
|
485 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT') |
|
486 fp.write('"""\n') |
|
487 fp.write("Package generated from %s\n"%ascii(fname)) |
|
488 if resinfo: |
|
489 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2]))) |
|
490 fp.write('"""\n') |
|
491 fp.write('import aetools\n') |
|
492 fp.write('Error = aetools.Error\n') |
|
493 suitelist.sort() |
|
494 for code, modname in suitelist: |
|
495 fp.write("import %s\n" % modname) |
|
496 fp.write("\n\n_code_to_module = {\n") |
|
497 for code, modname in suitelist: |
|
498 fp.write(" '%s' : %s,\n"%(ascii(code), modname)) |
|
499 fp.write("}\n\n") |
|
500 fp.write("\n\n_code_to_fullname = {\n") |
|
501 for code, modname in suitelist: |
|
502 fp.write(" '%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname)) |
|
503 fp.write("}\n\n") |
|
504 for code, modname in suitelist: |
|
505 fp.write("from %s import *\n"%modname) |
|
506 |
|
507 # Generate property dicts and element dicts for all types declared in this module |
|
508 fp.write("\ndef getbaseclasses(v):\n") |
|
509 fp.write(" if not getattr(v, '_propdict', None):\n") |
|
510 fp.write(" v._propdict = {}\n") |
|
511 fp.write(" v._elemdict = {}\n") |
|
512 fp.write(" for superclassname in getattr(v, '_superclassnames', []):\n") |
|
513 fp.write(" superclass = eval(superclassname)\n") |
|
514 fp.write(" getbaseclasses(superclass)\n") |
|
515 fp.write(" v._propdict.update(getattr(superclass, '_propdict', {}))\n") |
|
516 fp.write(" v._elemdict.update(getattr(superclass, '_elemdict', {}))\n") |
|
517 fp.write(" v._propdict.update(getattr(v, '_privpropdict', {}))\n") |
|
518 fp.write(" v._elemdict.update(getattr(v, '_privelemdict', {}))\n") |
|
519 fp.write("\n") |
|
520 fp.write("import StdSuites\n") |
|
521 allprecompinfo.sort() |
|
522 if allprecompinfo: |
|
523 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n") |
|
524 for codenamemapper in allprecompinfo: |
|
525 for k, v in codenamemapper.getall('class'): |
|
526 fp.write("getbaseclasses(%s)\n" % v) |
|
527 |
|
528 # Generate a code-to-name mapper for all of the types (classes) declared in this module |
|
529 application_class = None |
|
530 if allprecompinfo: |
|
531 fp.write("\n#\n# Indices of types declared in this module\n#\n") |
|
532 fp.write("_classdeclarations = {\n") |
|
533 for codenamemapper in allprecompinfo: |
|
534 for k, v in codenamemapper.getall('class'): |
|
535 fp.write(" %r : %s,\n" % (k, v)) |
|
536 if k == 'capp': |
|
537 application_class = v |
|
538 fp.write("}\n") |
|
539 |
|
540 |
|
541 if suitelist: |
|
542 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1])) |
|
543 for code, modname in suitelist[1:]: |
|
544 fp.write(",\n %s_Events"%modname) |
|
545 fp.write(",\n aetools.TalkTo):\n") |
|
546 fp.write(" _signature = %r\n\n"%(creatorsignature,)) |
|
547 fp.write(" _moduleName = '%s'\n\n"%packagename) |
|
548 if application_class: |
|
549 fp.write(" _elemdict = %s._elemdict\n" % application_class) |
|
550 fp.write(" _propdict = %s._propdict\n" % application_class) |
|
551 fp.close() |
|
552 |
|
553 class SuiteCompiler: |
|
554 def __init__(self, suite, basepackage, output, edit_modnames, verbose): |
|
555 self.suite = suite |
|
556 self.basepackage = basepackage |
|
557 self.edit_modnames = edit_modnames |
|
558 self.output = output |
|
559 self.verbose = verbose |
|
560 |
|
561 # Set by precompilesuite |
|
562 self.pathname = None |
|
563 self.modname = None |
|
564 |
|
565 # Set by compilesuite |
|
566 self.fp = None |
|
567 self.basemodule = None |
|
568 self.enumsneeded = {} |
|
569 |
|
570 def precompilesuite(self): |
|
571 """Parse a single suite without generating the output. This step is needed |
|
572 so we can resolve recursive references by suites to enums/comps/etc declared |
|
573 in other suites""" |
|
574 [name, desc, code, level, version, events, classes, comps, enums] = self.suite |
|
575 |
|
576 modname = identify(name) |
|
577 if len(modname) > 28: |
|
578 modname = modname[:27] |
|
579 if self.edit_modnames is None: |
|
580 self.pathname = EasyDialogs.AskFileForSave(message='Python output file', |
|
581 savedFileName=modname+'.py') |
|
582 else: |
|
583 for old, new in self.edit_modnames: |
|
584 if old == modname: |
|
585 modname = new |
|
586 if modname: |
|
587 self.pathname = os.path.join(self.output, modname + '.py') |
|
588 else: |
|
589 self.pathname = None |
|
590 if not self.pathname: |
|
591 return None, None, None |
|
592 |
|
593 self.modname = os.path.splitext(os.path.split(self.pathname)[1])[0] |
|
594 |
|
595 if self.basepackage and self.basepackage._code_to_module.has_key(code): |
|
596 # We are an extension of a baseclass (usually an application extending |
|
597 # Standard_Suite or so). Import everything from our base module |
|
598 basemodule = self.basepackage._code_to_module[code] |
|
599 else: |
|
600 # We are not an extension. |
|
601 basemodule = None |
|
602 |
|
603 self.enumsneeded = {} |
|
604 for event in events: |
|
605 self.findenumsinevent(event) |
|
606 |
|
607 objc = ObjectCompiler(None, self.modname, basemodule, interact=(self.edit_modnames is None), |
|
608 verbose=self.verbose) |
|
609 for cls in classes: |
|
610 objc.compileclass(cls) |
|
611 for cls in classes: |
|
612 objc.fillclasspropsandelems(cls) |
|
613 for comp in comps: |
|
614 objc.compilecomparison(comp) |
|
615 for enum in enums: |
|
616 objc.compileenumeration(enum) |
|
617 |
|
618 for enum in self.enumsneeded.keys(): |
|
619 objc.checkforenum(enum) |
|
620 |
|
621 objc.dumpindex() |
|
622 |
|
623 precompinfo = objc.getprecompinfo(self.modname) |
|
624 |
|
625 return code, self.modname, precompinfo |
|
626 |
|
627 def compilesuite(self, major, minor, language, script, fname, precompinfo): |
|
628 """Generate code for a single suite""" |
|
629 [name, desc, code, level, version, events, classes, comps, enums] = self.suite |
|
630 # Sort various lists, so re-generated source is easier compared |
|
631 def class_sorter(k1, k2): |
|
632 """Sort classes by code, and make sure main class sorts before synonyms""" |
|
633 # [name, code, desc, properties, elements] = cls |
|
634 if k1[1] < k2[1]: return -1 |
|
635 if k1[1] > k2[1]: return 1 |
|
636 if not k2[3] or k2[3][0][1] == 'c@#!': |
|
637 # This is a synonym, the other one is better |
|
638 return -1 |
|
639 if not k1[3] or k1[3][0][1] == 'c@#!': |
|
640 # This is a synonym, the other one is better |
|
641 return 1 |
|
642 return 0 |
|
643 |
|
644 events.sort() |
|
645 classes.sort(class_sorter) |
|
646 comps.sort() |
|
647 enums.sort() |
|
648 |
|
649 self.fp = fp = open(self.pathname, 'w') |
|
650 MacOS.SetCreatorAndType(self.pathname, 'Pyth', 'TEXT') |
|
651 |
|
652 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc))) |
|
653 fp.write("Level %d, version %d\n\n" % (level, version)) |
|
654 fp.write("Generated from %s\n"%ascii(fname)) |
|
655 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \ |
|
656 (major, minor, language, script)) |
|
657 fp.write('"""\n\n') |
|
658 |
|
659 fp.write('import aetools\n') |
|
660 fp.write('import MacOS\n\n') |
|
661 fp.write("_code = %r\n\n"% (code,)) |
|
662 if self.basepackage and self.basepackage._code_to_module.has_key(code): |
|
663 # We are an extension of a baseclass (usually an application extending |
|
664 # Standard_Suite or so). Import everything from our base module |
|
665 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code][0]) |
|
666 basemodule = self.basepackage._code_to_module[code] |
|
667 elif self.basepackage and self.basepackage._code_to_module.has_key(code.lower()): |
|
668 # This is needed by CodeWarrior and some others. |
|
669 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code.lower()][0]) |
|
670 basemodule = self.basepackage._code_to_module[code.lower()] |
|
671 else: |
|
672 # We are not an extension. |
|
673 basemodule = None |
|
674 self.basemodule = basemodule |
|
675 self.compileclassheader() |
|
676 |
|
677 self.enumsneeded = {} |
|
678 if events: |
|
679 for event in events: |
|
680 self.compileevent(event) |
|
681 else: |
|
682 fp.write(" pass\n\n") |
|
683 |
|
684 objc = ObjectCompiler(fp, self.modname, basemodule, precompinfo, interact=(self.edit_modnames is None), |
|
685 verbose=self.verbose) |
|
686 for cls in classes: |
|
687 objc.compileclass(cls) |
|
688 for cls in classes: |
|
689 objc.fillclasspropsandelems(cls) |
|
690 for comp in comps: |
|
691 objc.compilecomparison(comp) |
|
692 for enum in enums: |
|
693 objc.compileenumeration(enum) |
|
694 |
|
695 for enum in self.enumsneeded.keys(): |
|
696 objc.checkforenum(enum) |
|
697 |
|
698 objc.dumpindex() |
|
699 |
|
700 def compileclassheader(self): |
|
701 """Generate class boilerplate""" |
|
702 classname = '%s_Events'%self.modname |
|
703 if self.basemodule: |
|
704 modshortname = string.split(self.basemodule.__name__, '.')[-1] |
|
705 baseclassname = '%s_Events'%modshortname |
|
706 self.fp.write("class %s(%s):\n\n"%(classname, baseclassname)) |
|
707 else: |
|
708 self.fp.write("class %s:\n\n"%classname) |
|
709 |
|
710 def compileevent(self, event): |
|
711 """Generate code for a single event""" |
|
712 [name, desc, code, subcode, returns, accepts, arguments] = event |
|
713 fp = self.fp |
|
714 funcname = identify(name) |
|
715 # |
|
716 # generate name->keyword map |
|
717 # |
|
718 if arguments: |
|
719 fp.write(" _argmap_%s = {\n"%funcname) |
|
720 for a in arguments: |
|
721 fp.write(" %r : %r,\n"%(identify(a[0]), a[1])) |
|
722 fp.write(" }\n\n") |
|
723 |
|
724 # |
|
725 # Generate function header |
|
726 # |
|
727 has_arg = (not is_null(accepts)) |
|
728 opt_arg = (has_arg and is_optional(accepts)) |
|
729 |
|
730 fp.write(" def %s(self, "%funcname) |
|
731 if has_arg: |
|
732 if not opt_arg: |
|
733 fp.write("_object, ") # Include direct object, if it has one |
|
734 else: |
|
735 fp.write("_object=None, ") # Also include if it is optional |
|
736 else: |
|
737 fp.write("_no_object=None, ") # For argument checking |
|
738 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args |
|
739 # |
|
740 # Generate doc string (important, since it may be the only |
|
741 # available documentation, due to our name-remaping) |
|
742 # |
|
743 fp.write(' """%s: %s\n'%(ascii(name), ascii(desc))) |
|
744 if has_arg: |
|
745 fp.write(" Required argument: %s\n"%getdatadoc(accepts)) |
|
746 elif opt_arg: |
|
747 fp.write(" Optional argument: %s\n"%getdatadoc(accepts)) |
|
748 for arg in arguments: |
|
749 fp.write(" Keyword argument %s: %s\n"%(identify(arg[0]), |
|
750 getdatadoc(arg[2]))) |
|
751 fp.write(" Keyword argument _attributes: AppleEvent attribute dictionary\n") |
|
752 if not is_null(returns): |
|
753 fp.write(" Returns: %s\n"%getdatadoc(returns)) |
|
754 fp.write(' """\n') |
|
755 # |
|
756 # Fiddle the args so everything ends up in 'arguments' dictionary |
|
757 # |
|
758 fp.write(" _code = %r\n"% (code,)) |
|
759 fp.write(" _subcode = %r\n\n"% (subcode,)) |
|
760 # |
|
761 # Do keyword name substitution |
|
762 # |
|
763 if arguments: |
|
764 fp.write(" aetools.keysubst(_arguments, self._argmap_%s)\n"%funcname) |
|
765 else: |
|
766 fp.write(" if _arguments: raise TypeError, 'No optional args expected'\n") |
|
767 # |
|
768 # Stuff required arg (if there is one) into arguments |
|
769 # |
|
770 if has_arg: |
|
771 fp.write(" _arguments['----'] = _object\n") |
|
772 elif opt_arg: |
|
773 fp.write(" if _object:\n") |
|
774 fp.write(" _arguments['----'] = _object\n") |
|
775 else: |
|
776 fp.write(" if _no_object is not None: raise TypeError, 'No direct arg expected'\n") |
|
777 fp.write("\n") |
|
778 # |
|
779 # Do enum-name substitution |
|
780 # |
|
781 for a in arguments: |
|
782 if is_enum(a[2]): |
|
783 kname = a[1] |
|
784 ename = a[2][0] |
|
785 if ename <> '****': |
|
786 fp.write(" aetools.enumsubst(_arguments, %r, _Enum_%s)\n" % |
|
787 (kname, identify(ename))) |
|
788 self.enumsneeded[ename] = 1 |
|
789 fp.write("\n") |
|
790 # |
|
791 # Do the transaction |
|
792 # |
|
793 fp.write(" _reply, _arguments, _attributes = self.send(_code, _subcode,\n") |
|
794 fp.write(" _arguments, _attributes)\n") |
|
795 # |
|
796 # Error handling |
|
797 # |
|
798 fp.write(" if _arguments.get('errn', 0):\n") |
|
799 fp.write(" raise aetools.Error, aetools.decodeerror(_arguments)\n") |
|
800 fp.write(" # XXXX Optionally decode result\n") |
|
801 # |
|
802 # Decode result |
|
803 # |
|
804 fp.write(" if _arguments.has_key('----'):\n") |
|
805 if is_enum(returns): |
|
806 fp.write(" # XXXX Should do enum remapping here...\n") |
|
807 fp.write(" return _arguments['----']\n") |
|
808 fp.write("\n") |
|
809 |
|
810 def findenumsinevent(self, event): |
|
811 """Find all enums for a single event""" |
|
812 [name, desc, code, subcode, returns, accepts, arguments] = event |
|
813 for a in arguments: |
|
814 if is_enum(a[2]): |
|
815 ename = a[2][0] |
|
816 if ename <> '****': |
|
817 self.enumsneeded[ename] = 1 |
|
818 |
|
819 # |
|
820 # This class stores the code<->name translations for a single module. It is used |
|
821 # to keep the information while we're compiling the module, but we also keep these objects |
|
822 # around so if one suite refers to, say, an enum in another suite we know where to |
|
823 # find it. Finally, if we really can't find a code, the user can add modules by |
|
824 # hand. |
|
825 # |
|
826 class CodeNameMapper: |
|
827 |
|
828 def __init__(self, interact=1, verbose=None): |
|
829 self.code2name = { |
|
830 "property" : {}, |
|
831 "class" : {}, |
|
832 "enum" : {}, |
|
833 "comparison" : {}, |
|
834 } |
|
835 self.name2code = { |
|
836 "property" : {}, |
|
837 "class" : {}, |
|
838 "enum" : {}, |
|
839 "comparison" : {}, |
|
840 } |
|
841 self.modulename = None |
|
842 self.star_imported = 0 |
|
843 self.can_interact = interact |
|
844 self.verbose = verbose |
|
845 |
|
846 def addnamecode(self, type, name, code): |
|
847 self.name2code[type][name] = code |
|
848 if not self.code2name[type].has_key(code): |
|
849 self.code2name[type][code] = name |
|
850 |
|
851 def hasname(self, name): |
|
852 for dict in self.name2code.values(): |
|
853 if dict.has_key(name): |
|
854 return True |
|
855 return False |
|
856 |
|
857 def hascode(self, type, code): |
|
858 return self.code2name[type].has_key(code) |
|
859 |
|
860 def findcodename(self, type, code): |
|
861 if not self.hascode(type, code): |
|
862 return None, None, None |
|
863 name = self.code2name[type][code] |
|
864 if self.modulename and not self.star_imported: |
|
865 qualname = '%s.%s'%(self.modulename, name) |
|
866 else: |
|
867 qualname = name |
|
868 return name, qualname, self.modulename |
|
869 |
|
870 def getall(self, type): |
|
871 return self.code2name[type].items() |
|
872 |
|
873 def addmodule(self, module, name, star_imported): |
|
874 self.modulename = name |
|
875 self.star_imported = star_imported |
|
876 for code, name in module._propdeclarations.items(): |
|
877 self.addnamecode('property', name, code) |
|
878 for code, name in module._classdeclarations.items(): |
|
879 self.addnamecode('class', name, code) |
|
880 for code in module._enumdeclarations.keys(): |
|
881 self.addnamecode('enum', '_Enum_'+identify(code), code) |
|
882 for code, name in module._compdeclarations.items(): |
|
883 self.addnamecode('comparison', name, code) |
|
884 |
|
885 def prepareforexport(self, name=None): |
|
886 if not self.modulename: |
|
887 self.modulename = name |
|
888 return self |
|
889 |
|
890 class ObjectCompiler: |
|
891 def __init__(self, fp, modname, basesuite, othernamemappers=None, interact=1, |
|
892 verbose=None): |
|
893 self.fp = fp |
|
894 self.verbose = verbose |
|
895 self.basesuite = basesuite |
|
896 self.can_interact = interact |
|
897 self.modulename = modname |
|
898 self.namemappers = [CodeNameMapper(self.can_interact, self.verbose)] |
|
899 if othernamemappers: |
|
900 self.othernamemappers = othernamemappers[:] |
|
901 else: |
|
902 self.othernamemappers = [] |
|
903 if basesuite: |
|
904 basemapper = CodeNameMapper(self.can_interact, self.verbose) |
|
905 basemapper.addmodule(basesuite, '', 1) |
|
906 self.namemappers.append(basemapper) |
|
907 |
|
908 def getprecompinfo(self, modname): |
|
909 list = [] |
|
910 for mapper in self.namemappers: |
|
911 emapper = mapper.prepareforexport(modname) |
|
912 if emapper: |
|
913 list.append(emapper) |
|
914 return list |
|
915 |
|
916 def findcodename(self, type, code): |
|
917 while 1: |
|
918 # First try: check whether we already know about this code. |
|
919 for mapper in self.namemappers: |
|
920 if mapper.hascode(type, code): |
|
921 return mapper.findcodename(type, code) |
|
922 # Second try: maybe one of the other modules knows about it. |
|
923 for mapper in self.othernamemappers: |
|
924 if mapper.hascode(type, code): |
|
925 self.othernamemappers.remove(mapper) |
|
926 self.namemappers.append(mapper) |
|
927 if self.fp: |
|
928 self.fp.write("import %s\n"%mapper.modulename) |
|
929 break |
|
930 else: |
|
931 # If all this has failed we ask the user for a guess on where it could |
|
932 # be and retry. |
|
933 if self.fp: |
|
934 m = self.askdefinitionmodule(type, code) |
|
935 else: |
|
936 m = None |
|
937 if not m: return None, None, None |
|
938 mapper = CodeNameMapper(self.can_interact, self.verbose) |
|
939 mapper.addmodule(m, m.__name__, 0) |
|
940 self.namemappers.append(mapper) |
|
941 |
|
942 def hasname(self, name): |
|
943 for mapper in self.othernamemappers: |
|
944 if mapper.hasname(name) and mapper.modulename != self.modulename: |
|
945 if self.verbose: |
|
946 print >>self.verbose, "Duplicate Python identifier:", name, self.modulename, mapper.modulename |
|
947 return True |
|
948 return False |
|
949 |
|
950 def askdefinitionmodule(self, type, code): |
|
951 if not self.can_interact: |
|
952 if self.verbose: |
|
953 print >>self.verbose, "** No definition for %s '%s' found" % (type, code) |
|
954 return None |
|
955 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code)) |
|
956 if not path: return |
|
957 path, file = os.path.split(path) |
|
958 modname = os.path.splitext(file)[0] |
|
959 if not path in sys.path: |
|
960 sys.path.insert(0, path) |
|
961 m = __import__(modname) |
|
962 self.fp.write("import %s\n"%modname) |
|
963 return m |
|
964 |
|
965 def compileclass(self, cls): |
|
966 [name, code, desc, properties, elements] = cls |
|
967 pname = identify(name) |
|
968 if self.namemappers[0].hascode('class', code): |
|
969 # plural forms and such |
|
970 othername, dummy, dummy = self.namemappers[0].findcodename('class', code) |
|
971 if self.fp: |
|
972 self.fp.write("\n%s = %s\n"%(pname, othername)) |
|
973 else: |
|
974 if self.fp: |
|
975 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname) |
|
976 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(desc))) |
|
977 self.fp.write(' want = %r\n' % (code,)) |
|
978 self.namemappers[0].addnamecode('class', pname, code) |
|
979 is_application_class = (code == 'capp') |
|
980 properties.sort() |
|
981 for prop in properties: |
|
982 self.compileproperty(prop, is_application_class) |
|
983 elements.sort() |
|
984 for elem in elements: |
|
985 self.compileelement(elem) |
|
986 |
|
987 def compileproperty(self, prop, is_application_class=False): |
|
988 [name, code, what] = prop |
|
989 if code == 'c@#!': |
|
990 # Something silly with plurals. Skip it. |
|
991 return |
|
992 pname = identify(name) |
|
993 if self.namemappers[0].hascode('property', code): |
|
994 # plural forms and such |
|
995 othername, dummy, dummy = self.namemappers[0].findcodename('property', code) |
|
996 if pname == othername: |
|
997 return |
|
998 if self.fp: |
|
999 self.fp.write("\n_Prop_%s = _Prop_%s\n"%(pname, othername)) |
|
1000 else: |
|
1001 if self.fp: |
|
1002 self.fp.write("class _Prop_%s(aetools.NProperty):\n" % pname) |
|
1003 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(what[1]))) |
|
1004 self.fp.write(" which = %r\n" % (code,)) |
|
1005 self.fp.write(" want = %r\n" % (what[0],)) |
|
1006 self.namemappers[0].addnamecode('property', pname, code) |
|
1007 if is_application_class and self.fp: |
|
1008 self.fp.write("%s = _Prop_%s()\n" % (pname, pname)) |
|
1009 |
|
1010 def compileelement(self, elem): |
|
1011 [code, keyform] = elem |
|
1012 if self.fp: |
|
1013 self.fp.write("# element %r as %s\n" % (code, keyform)) |
|
1014 |
|
1015 def fillclasspropsandelems(self, cls): |
|
1016 [name, code, desc, properties, elements] = cls |
|
1017 cname = identify(name) |
|
1018 if self.namemappers[0].hascode('class', code) and \ |
|
1019 self.namemappers[0].findcodename('class', code)[0] != cname: |
|
1020 # This is an other name (plural or so) for something else. Skip. |
|
1021 if self.fp and (elements or len(properties) > 1 or (len(properties) == 1 and |
|
1022 properties[0][1] != 'c@#!')): |
|
1023 if self.verbose: |
|
1024 print >>self.verbose, '** Skip multiple %s of %s (code %r)' % (cname, self.namemappers[0].findcodename('class', code)[0], code) |
|
1025 raise RuntimeError, "About to skip non-empty class" |
|
1026 return |
|
1027 plist = [] |
|
1028 elist = [] |
|
1029 superclasses = [] |
|
1030 for prop in properties: |
|
1031 [pname, pcode, what] = prop |
|
1032 if pcode == "c@#^": |
|
1033 superclasses.append(what) |
|
1034 if pcode == 'c@#!': |
|
1035 continue |
|
1036 pname = identify(pname) |
|
1037 plist.append(pname) |
|
1038 |
|
1039 superclassnames = [] |
|
1040 for superclass in superclasses: |
|
1041 superId, superDesc, dummy = superclass |
|
1042 superclassname, fullyqualifiedname, module = self.findcodename("class", superId) |
|
1043 # I don't think this is correct: |
|
1044 if superclassname == cname: |
|
1045 pass # superclassnames.append(fullyqualifiedname) |
|
1046 else: |
|
1047 superclassnames.append(superclassname) |
|
1048 |
|
1049 if self.fp: |
|
1050 self.fp.write("%s._superclassnames = %r\n"%(cname, superclassnames)) |
|
1051 |
|
1052 for elem in elements: |
|
1053 [ecode, keyform] = elem |
|
1054 if ecode == 'c@#!': |
|
1055 continue |
|
1056 name, ename, module = self.findcodename('class', ecode) |
|
1057 if not name: |
|
1058 if self.fp: |
|
1059 self.fp.write("# XXXX %s element %r not found!!\n"%(cname, ecode)) |
|
1060 else: |
|
1061 elist.append((name, ename)) |
|
1062 |
|
1063 plist.sort() |
|
1064 elist.sort() |
|
1065 |
|
1066 if self.fp: |
|
1067 self.fp.write("%s._privpropdict = {\n"%cname) |
|
1068 for n in plist: |
|
1069 self.fp.write(" '%s' : _Prop_%s,\n"%(n, n)) |
|
1070 self.fp.write("}\n") |
|
1071 self.fp.write("%s._privelemdict = {\n"%cname) |
|
1072 for n, fulln in elist: |
|
1073 self.fp.write(" '%s' : %s,\n"%(n, fulln)) |
|
1074 self.fp.write("}\n") |
|
1075 |
|
1076 def compilecomparison(self, comp): |
|
1077 [name, code, comment] = comp |
|
1078 iname = identify(name) |
|
1079 self.namemappers[0].addnamecode('comparison', iname, code) |
|
1080 if self.fp: |
|
1081 self.fp.write("class %s(aetools.NComparison):\n" % iname) |
|
1082 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(comment))) |
|
1083 |
|
1084 def compileenumeration(self, enum): |
|
1085 [code, items] = enum |
|
1086 name = "_Enum_%s" % identify(code) |
|
1087 if self.fp: |
|
1088 self.fp.write("%s = {\n" % name) |
|
1089 for item in items: |
|
1090 self.compileenumerator(item) |
|
1091 self.fp.write("}\n\n") |
|
1092 self.namemappers[0].addnamecode('enum', name, code) |
|
1093 return code |
|
1094 |
|
1095 def compileenumerator(self, item): |
|
1096 [name, code, desc] = item |
|
1097 self.fp.write(" %r : %r,\t# %s\n" % (identify(name), code, ascii(desc))) |
|
1098 |
|
1099 def checkforenum(self, enum): |
|
1100 """This enum code is used by an event. Make sure it's available""" |
|
1101 name, fullname, module = self.findcodename('enum', enum) |
|
1102 if not name: |
|
1103 if self.fp: |
|
1104 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum))) |
|
1105 return |
|
1106 if module: |
|
1107 if self.fp: |
|
1108 self.fp.write("from %s import %s\n"%(module, name)) |
|
1109 |
|
1110 def dumpindex(self): |
|
1111 if not self.fp: |
|
1112 return |
|
1113 self.fp.write("\n#\n# Indices of types declared in this module\n#\n") |
|
1114 |
|
1115 self.fp.write("_classdeclarations = {\n") |
|
1116 classlist = self.namemappers[0].getall('class') |
|
1117 classlist.sort() |
|
1118 for k, v in classlist: |
|
1119 self.fp.write(" %r : %s,\n" % (k, v)) |
|
1120 self.fp.write("}\n") |
|
1121 |
|
1122 self.fp.write("\n_propdeclarations = {\n") |
|
1123 proplist = self.namemappers[0].getall('property') |
|
1124 proplist.sort() |
|
1125 for k, v in proplist: |
|
1126 self.fp.write(" %r : _Prop_%s,\n" % (k, v)) |
|
1127 self.fp.write("}\n") |
|
1128 |
|
1129 self.fp.write("\n_compdeclarations = {\n") |
|
1130 complist = self.namemappers[0].getall('comparison') |
|
1131 complist.sort() |
|
1132 for k, v in complist: |
|
1133 self.fp.write(" %r : %s,\n" % (k, v)) |
|
1134 self.fp.write("}\n") |
|
1135 |
|
1136 self.fp.write("\n_enumdeclarations = {\n") |
|
1137 enumlist = self.namemappers[0].getall('enum') |
|
1138 enumlist.sort() |
|
1139 for k, v in enumlist: |
|
1140 self.fp.write(" %r : %s,\n" % (k, v)) |
|
1141 self.fp.write("}\n") |
|
1142 |
|
1143 def compiledata(data): |
|
1144 [type, description, flags] = data |
|
1145 return "%r -- %r %s" % (type, description, compiledataflags(flags)) |
|
1146 |
|
1147 def is_null(data): |
|
1148 return data[0] == 'null' |
|
1149 |
|
1150 def is_optional(data): |
|
1151 return (data[2] & 0x8000) |
|
1152 |
|
1153 def is_enum(data): |
|
1154 return (data[2] & 0x2000) |
|
1155 |
|
1156 def getdatadoc(data): |
|
1157 [type, descr, flags] = data |
|
1158 if descr: |
|
1159 return ascii(descr) |
|
1160 if type == '****': |
|
1161 return 'anything' |
|
1162 if type == 'obj ': |
|
1163 return 'an AE object reference' |
|
1164 return "undocumented, typecode %r"%(type,) |
|
1165 |
|
1166 dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"} |
|
1167 def compiledataflags(flags): |
|
1168 bits = [] |
|
1169 for i in range(16): |
|
1170 if flags & (1<<i): |
|
1171 if i in dataflagdict.keys(): |
|
1172 bits.append(dataflagdict[i]) |
|
1173 else: |
|
1174 bits.append(repr(i)) |
|
1175 return '[%s]' % string.join(bits) |
|
1176 |
|
1177 def ascii(str): |
|
1178 """Return a string with all non-ascii characters hex-encoded""" |
|
1179 if type(str) != type(''): |
|
1180 return map(ascii, str) |
|
1181 rv = '' |
|
1182 for c in str: |
|
1183 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f): |
|
1184 rv = rv + c |
|
1185 else: |
|
1186 rv = rv + '\\' + 'x%02.2x' % ord(c) |
|
1187 return rv |
|
1188 |
|
1189 def identify(str): |
|
1190 """Turn any string into an identifier: |
|
1191 - replace space by _ |
|
1192 - replace other illegal chars by _xx_ (hex code) |
|
1193 - append _ if the result is a python keyword |
|
1194 """ |
|
1195 if not str: |
|
1196 return "empty_ae_name_" |
|
1197 rv = '' |
|
1198 ok = string.ascii_letters + '_' |
|
1199 ok2 = ok + string.digits |
|
1200 for c in str: |
|
1201 if c in ok: |
|
1202 rv = rv + c |
|
1203 elif c == ' ': |
|
1204 rv = rv + '_' |
|
1205 else: |
|
1206 rv = rv + '_%02.2x_'%ord(c) |
|
1207 ok = ok2 |
|
1208 if keyword.iskeyword(rv): |
|
1209 rv = rv + '_' |
|
1210 return rv |
|
1211 |
|
1212 # Call the main program |
|
1213 |
|
1214 if __name__ == '__main__': |
|
1215 main() |
|
1216 sys.exit(1) |