blob: 4eca8af3b6e587c20dde7c4842b0cc82fdb51db3 [file] [log] [blame]
Jack Jansen5ccd8261995-07-17 11:43:20 +00001"""
2gensuitemodule - Generate an AE suite module from an aete/aeut resource
3
Jack Jansen60762cb2000-08-17 22:11:45 +00004Based on aete.py.
5
6Reading and understanding this code is left as an exercise to the reader.
Jack Jansen5ccd8261995-07-17 11:43:20 +00007"""
8
9import MacOS
Jack Jansendf976ca2003-01-26 20:35:47 +000010import EasyDialogs
Jack Jansen5ccd8261995-07-17 11:43:20 +000011import os
12import string
13import sys
14import types
15import StringIO
Jack Jansenb2ecc2c2002-01-24 22:44:07 +000016import keyword
Jack Jansen40926062002-03-30 23:43:04 +000017import macresource
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000018import aetools
19import distutils.sysconfig
20import OSATerminology
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000021from Carbon.Res import *
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000022import MacOS
Jack Jansen714caa02003-03-21 16:07:39 +000023import getopt
Jack Jansen5ccd8261995-07-17 11:43:20 +000024
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000025_MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
26DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
27DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
Jack Jansen40926062002-03-30 23:43:04 +000028
Jack Jansen714caa02003-03-21 16:07:39 +000029def usage():
30 sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0])
31 sys.stderr.write("""Options:
32--output pkgdir Pathname of the output package (short: -o)
33--resource Parse resource file in stead of launching application (-r)
34--base package Use another base package in stead of default StdSuites (-b)
35--edit old=new Edit suite names, use empty new to skip a suite (-e)
Jack Jansen59cdbce2003-03-21 16:28:09 +000036--creator code Set creator code for package (-c)
Jack Jansen714caa02003-03-21 16:07:39 +000037""")
38 sys.exit(1)
39
Jack Jansen5ccd8261995-07-17 11:43:20 +000040def main():
Jack Jansen40926062002-03-30 23:43:04 +000041 if len(sys.argv) > 1:
Jack Jansen59cdbce2003-03-21 16:28:09 +000042 SHORTOPTS = "rb:o:e:c:"
43 LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=")
Jack Jansen714caa02003-03-21 16:07:39 +000044 try:
45 opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
46 except getopt.GetoptError:
47 usage()
48
49 process_func = processfile
50 basepkgname = 'StdSuites'
51 output = None
52 edit_modnames = []
Jack Jansen59cdbce2003-03-21 16:28:09 +000053 creatorsignature = None
Jack Jansen714caa02003-03-21 16:07:39 +000054
55 for o, a in opts:
56 if o in ('-r', '--resource'):
57 process_func = processfile_fromresource
58 if o in ('-b', '--base'):
59 basepkgname = a
60 if o in ('-o', '--output'):
61 output = a
62 if o in ('-e', '--edit'):
63 split = a.split('=')
64 if len(split) != 2:
65 usage()
66 edit_modnames.append(split)
Jack Jansen59cdbce2003-03-21 16:28:09 +000067 if o in ('-c', '--creator'):
68 if len(a) != 4:
69 sys.stderr.write("creator must be 4-char string\n")
70 sys.exit(1)
71 creatorsignature = a
72
Jack Jansen714caa02003-03-21 16:07:39 +000073
74 if output and len(args) > 1:
75 sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0])
76 sys.exit(1)
77
78 for filename in args:
79 process_func(filename, output=output, basepkgname=basepkgname,
Jack Jansen59cdbce2003-03-21 16:28:09 +000080 edit_modnames=edit_modnames, creatorsignature=creatorsignature)
Jack Jansen40926062002-03-30 23:43:04 +000081 else:
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000082 # The dialogOptionFlags below allows selection of .app bundles.
83 filename = EasyDialogs.AskFileForOpen(
84 message='Select scriptable application',
85 dialogOptionFlags=0x1056)
Jack Jansendf976ca2003-01-26 20:35:47 +000086 if not filename:
Jack Jansen40926062002-03-30 23:43:04 +000087 sys.exit(0)
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000088 try:
89 processfile(filename)
90 except MacOS.Error, arg:
91 print "Error getting terminology:", arg
92 print "Retry, manually parsing resources"
93 processfile_fromresource(filename)
Jack Jansen5ccd8261995-07-17 11:43:20 +000094
Jack Jansen59cdbce2003-03-21 16:28:09 +000095def processfile_fromresource(fullname, output=None, basepkgname=None,
96 edit_modnames=None, creatorsignature=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +000097 """Process all resources in a single file"""
98 cur = CurResFile()
Jack Jansen40926062002-03-30 23:43:04 +000099 print "Processing", fullname
100 rf = macresource.open_pathname(fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000101 try:
102 UseResFile(rf)
103 resources = []
104 for i in range(Count1Resources('aete')):
105 res = Get1IndResource('aete', 1+i)
106 resources.append(res)
107 for i in range(Count1Resources('aeut')):
108 res = Get1IndResource('aeut', 1+i)
109 resources.append(res)
110 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +0000111 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000112 for res in resources:
113 print "decoding", res.GetResInfo(), "..."
114 data = res.data
115 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +0000116 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000117 finally:
118 if rf <> cur:
119 CloseResFile(rf)
120 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +0000121 # switch back (needed for dialogs in Python)
122 UseResFile(cur)
Jack Jansen714caa02003-03-21 16:07:39 +0000123 compileaetelist(aetelist, fullname, output=output,
Jack Jansen59cdbce2003-03-21 16:28:09 +0000124 basepkgname=basepkgname, edit_modnames=edit_modnames,
125 creatorsignature=creatorsignature)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000126
Jack Jansen59cdbce2003-03-21 16:28:09 +0000127def processfile(fullname, output=None, basepkgname=None,
128 edit_modnames=None, creatorsignature=None):
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000129 """Ask an application for its terminology and process that"""
Jack Jansen00c34832003-03-28 23:37:05 +0000130 print "\nASKING FOR aete DICTIONARY IN", `fullname`
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000131 try:
132 aedescobj, launched = OSATerminology.GetAppTerminology(fullname)
133 except MacOS.Error, arg:
Jack Jansen03b9c912003-03-28 22:04:22 +0000134 if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound
135 print "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually"
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000136 aedata, sig = getappterminology(fullname)
137 if not creatorsignature:
138 creatorsignature = sig
139 else:
140 raise
141 else:
142 if launched:
143 print "Launched", fullname
144 raw = aetools.unpack(aedescobj)
145 if not raw:
146 print 'Unpack returned empty value:', raw
147 return
148 if not raw[0].data:
149 print 'Unpack returned value without data:', raw
150 return
151 aedata = raw[0]
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000152 aete = decode(aedata.data)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000153 compileaete(aete, None, fullname, output=output, basepkgname=basepkgname,
Jack Jansen03b9c912003-03-28 22:04:22 +0000154 creatorsignature=creatorsignature, edit_modnames=edit_modnames)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000155
156def getappterminology(fullname):
157 """Get application terminology by sending an AppleEvent"""
158 # First check that we actually can send AppleEvents
159 if not MacOS.WMAvailable():
160 raise RuntimeError, "Cannot send AppleEvents, no access to window manager"
161 # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless
162 # you have created an event loop first.
163 import Carbon.Evt
164 Carbon.Evt.WaitNextEvent(0,0)
Jack Jansen03b9c912003-03-28 22:04:22 +0000165 if os.path.isdir(fullname):
166 # Now get the signature of the application, hoping it is a bundle
167 pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo')
168 if not os.path.exists(pkginfo):
169 raise RuntimeError, "No PkgInfo file found"
170 tp_cr = open(pkginfo, 'rb').read()
171 cr = tp_cr[4:8]
172 else:
173 # Assume it is a file
174 cr, tp = MacOS.GetCreatorAndType(fullname)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000175 # Let's talk to it and ask for its AETE
176 talker = aetools.TalkTo(cr)
Jack Jansen03b9c912003-03-28 22:04:22 +0000177 try:
178 talker._start()
179 except (MacOS.Error, aetools.Error), arg:
180 print 'Warning: start() failed, continuing anyway:', arg
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000181 reply = talker.send("ascr", "gdte")
Jack Jansen03b9c912003-03-28 22:04:22 +0000182 #reply2 = talker.send("ascr", "gdut")
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000183 # Now pick the bits out of the return that we need.
184 return reply[1]['----'], cr
185
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000186
Jack Jansen59cdbce2003-03-21 16:28:09 +0000187def compileaetelist(aetelist, fullname, output=None, basepkgname=None,
188 edit_modnames=None, creatorsignature=None):
Jack Jansen60762cb2000-08-17 22:11:45 +0000189 for aete, resinfo in aetelist:
Jack Jansen714caa02003-03-21 16:07:39 +0000190 compileaete(aete, resinfo, fullname, output=output,
Jack Jansen59cdbce2003-03-21 16:28:09 +0000191 basepkgname=basepkgname, edit_modnames=edit_modnames,
192 creatorsignature=creatorsignature)
Jack Jansen60762cb2000-08-17 22:11:45 +0000193
Jack Jansen5ccd8261995-07-17 11:43:20 +0000194def decode(data):
195 """Decode a resource into a python data structure"""
196 f = StringIO.StringIO(data)
197 aete = generic(getaete, f)
198 aete = simplify(aete)
199 processed = f.tell()
200 unprocessed = len(f.read())
201 total = f.tell()
202 if unprocessed:
203 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
204 (processed, unprocessed, total))
205 return aete
206
207def simplify(item):
208 """Recursively replace singleton tuples by their constituent item"""
209 if type(item) is types.ListType:
210 return map(simplify, item)
211 elif type(item) == types.TupleType and len(item) == 2:
212 return simplify(item[1])
213 else:
214 return item
215
216
217# Here follows the aete resource decoder.
218# It is presented bottom-up instead of top-down because there are direct
219# references to the lower-level part-decoders from the high-level part-decoders.
220
221def getbyte(f, *args):
222 c = f.read(1)
223 if not c:
224 raise EOFError, 'in getbyte' + str(args)
225 return ord(c)
226
227def getword(f, *args):
228 getalign(f)
229 s = f.read(2)
230 if len(s) < 2:
231 raise EOFError, 'in getword' + str(args)
232 return (ord(s[0])<<8) | ord(s[1])
233
234def getlong(f, *args):
235 getalign(f)
236 s = f.read(4)
237 if len(s) < 4:
238 raise EOFError, 'in getlong' + str(args)
239 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
240
241def getostype(f, *args):
242 getalign(f)
243 s = f.read(4)
244 if len(s) < 4:
245 raise EOFError, 'in getostype' + str(args)
246 return s
247
248def getpstr(f, *args):
249 c = f.read(1)
250 if len(c) < 1:
251 raise EOFError, 'in getpstr[1]' + str(args)
252 nbytes = ord(c)
253 if nbytes == 0: return ''
254 s = f.read(nbytes)
255 if len(s) < nbytes:
256 raise EOFError, 'in getpstr[2]' + str(args)
257 return s
258
259def getalign(f):
260 if f.tell() & 1:
261 c = f.read(1)
262 ##if c <> '\0':
263 ## print 'align:', `c`
264
265def getlist(f, description, getitem):
266 count = getword(f)
267 list = []
268 for i in range(count):
269 list.append(generic(getitem, f))
270 getalign(f)
271 return list
272
273def alt_generic(what, f, *args):
274 print "generic", `what`, args
275 res = vageneric(what, f, args)
276 print '->', `res`
277 return res
278
279def generic(what, f, *args):
280 if type(what) == types.FunctionType:
281 return apply(what, (f,) + args)
282 if type(what) == types.ListType:
283 record = []
284 for thing in what:
285 item = apply(generic, thing[:1] + (f,) + thing[1:])
286 record.append((thing[1], item))
287 return record
288 return "BAD GENERIC ARGS: %s" % `what`
289
290getdata = [
291 (getostype, "type"),
292 (getpstr, "description"),
293 (getword, "flags")
294 ]
295getargument = [
296 (getpstr, "name"),
297 (getostype, "keyword"),
298 (getdata, "what")
299 ]
300getevent = [
301 (getpstr, "name"),
302 (getpstr, "description"),
303 (getostype, "suite code"),
304 (getostype, "event code"),
305 (getdata, "returns"),
306 (getdata, "accepts"),
307 (getlist, "optional arguments", getargument)
308 ]
309getproperty = [
310 (getpstr, "name"),
311 (getostype, "code"),
312 (getdata, "what")
313 ]
314getelement = [
315 (getostype, "type"),
316 (getlist, "keyform", getostype)
317 ]
318getclass = [
319 (getpstr, "name"),
320 (getostype, "class code"),
321 (getpstr, "description"),
322 (getlist, "properties", getproperty),
323 (getlist, "elements", getelement)
324 ]
325getcomparison = [
326 (getpstr, "operator name"),
327 (getostype, "operator ID"),
328 (getpstr, "operator comment"),
329 ]
330getenumerator = [
331 (getpstr, "enumerator name"),
332 (getostype, "enumerator ID"),
333 (getpstr, "enumerator comment")
334 ]
335getenumeration = [
336 (getostype, "enumeration ID"),
337 (getlist, "enumerator", getenumerator)
338 ]
339getsuite = [
340 (getpstr, "suite name"),
341 (getpstr, "suite description"),
342 (getostype, "suite ID"),
343 (getword, "suite level"),
344 (getword, "suite version"),
345 (getlist, "events", getevent),
346 (getlist, "classes", getclass),
347 (getlist, "comparisons", getcomparison),
348 (getlist, "enumerations", getenumeration)
349 ]
350getaete = [
351 (getword, "major/minor version in BCD"),
352 (getword, "language code"),
353 (getword, "script code"),
354 (getlist, "suites", getsuite)
355 ]
356
Jack Jansen59cdbce2003-03-21 16:28:09 +0000357def compileaete(aete, resinfo, fname, output=None, basepkgname=None,
358 edit_modnames=None, creatorsignature=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000359 """Generate code for a full aete resource. fname passed for doc purposes"""
360 [version, language, script, suites] = aete
361 major, minor = divmod(version, 256)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000362 if not creatorsignature:
363 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
Jack Jansen40926062002-03-30 23:43:04 +0000364 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000365 if language:
366 packagename = packagename+'_lang%d'%language
367 if script:
368 packagename = packagename+'_script%d'%script
369 if len(packagename) > 27:
370 packagename = packagename[:27]
Jack Jansen714caa02003-03-21 16:07:39 +0000371 if output:
372 # XXXX Put this in site-packages if it isn't a full pathname?
373 if not os.path.exists(output):
374 os.mkdir(output)
375 pathname = output
376 else:
377 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
378 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000379 output = pathname
Jack Jansendf976ca2003-01-26 20:35:47 +0000380 if not pathname:
Jack Jansen60762cb2000-08-17 22:11:45 +0000381 return
Jack Jansen60762cb2000-08-17 22:11:45 +0000382 packagename = os.path.split(os.path.normpath(pathname))[1]
Jack Jansen714caa02003-03-21 16:07:39 +0000383 if not basepkgname:
384 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
385 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
Jack Jansendf976ca2003-01-26 20:35:47 +0000386 if basepkgname:
387 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
Jack Jansen714caa02003-03-21 16:07:39 +0000388 if dirname and not dirname in sys.path:
Jack Jansen60762cb2000-08-17 22:11:45 +0000389 sys.path.insert(0, dirname)
390 basepackage = __import__(basepkgname)
391 else:
392 basepackage = None
Jack Jansen60762cb2000-08-17 22:11:45 +0000393 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000394 allprecompinfo = []
395 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000396 for suite in suites:
Jack Jansen714caa02003-03-21 16:07:39 +0000397 code, suite, pathname, modname, precompinfo = precompilesuite(suite, basepackage,
398 output=output, edit_modnames=edit_modnames)
Jack Jansen12b2b762000-08-20 19:30:56 +0000399 if not code:
400 continue
401 allprecompinfo = allprecompinfo + precompinfo
Jack Jansendf976ca2003-01-26 20:35:47 +0000402 suiteinfo = suite, pathname, modname
Jack Jansen12b2b762000-08-20 19:30:56 +0000403 suitelist.append((code, modname))
404 allsuites.append(suiteinfo)
405 for suiteinfo in allsuites:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000406 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage,
407 allprecompinfo, interact=(edit_modnames is None))
Jack Jansen714caa02003-03-21 16:07:39 +0000408 initfilename = os.path.join(output, '__init__.py')
Jack Jansendf976ca2003-01-26 20:35:47 +0000409 fp = open(initfilename, 'w')
410 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
Jack Jansen60762cb2000-08-17 22:11:45 +0000411 fp.write('"""\n')
412 fp.write("Package generated from %s\n"%fname)
Jack Jansen8b777672002-08-07 14:49:00 +0000413 if resinfo:
414 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000415 fp.write('"""\n')
416 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000417 fp.write('Error = aetools.Error\n')
Jack Jansen03b9c912003-03-28 22:04:22 +0000418 suitelist.sort()
Jack Jansen60762cb2000-08-17 22:11:45 +0000419 for code, modname in suitelist:
420 fp.write("import %s\n" % modname)
421 fp.write("\n\n_code_to_module = {\n")
422 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000423 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000424 fp.write("}\n\n")
425 fp.write("\n\n_code_to_fullname = {\n")
426 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000427 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000428 fp.write("}\n\n")
429 for code, modname in suitelist:
430 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000431
432 # Generate property dicts and element dicts for all types declared in this module
Jack Jansen7e0bc112003-03-21 12:04:19 +0000433 fp.write("\ndef getbaseclasses(v):\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000434 fp.write("\tif not getattr(v, '_propdict', None):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000435 fp.write("\t\tv._propdict = {}\n")
436 fp.write("\t\tv._elemdict = {}\n")
Jack Jansen7e0bc112003-03-21 12:04:19 +0000437 fp.write("\t\tfor superclassname in getattr(v, '_superclassnames', []):\n")
438 fp.write("\t\t\tsuperclass = eval(superclassname)\n")
439 fp.write("\t\t\tgetbaseclasses(superclass)\n")
440 fp.write("\t\t\tv._propdict.update(getattr(superclass, '_propdict', {}))\n")
441 fp.write("\t\t\tv._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000442 fp.write("\t\tv._propdict.update(getattr(v, '_privpropdict', {}))\n")
443 fp.write("\t\tv._elemdict.update(getattr(v, '_privelemdict', {}))\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000444 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000445 fp.write("import StdSuites\n")
Jack Jansen03b9c912003-03-28 22:04:22 +0000446 allprecompinfo.sort()
Jack Jansen8b777672002-08-07 14:49:00 +0000447 if allprecompinfo:
448 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
449 for codenamemapper in allprecompinfo:
450 for k, v in codenamemapper.getall('class'):
451 fp.write("getbaseclasses(%s)\n" % v)
452
453 # Generate a code-to-name mapper for all of the types (classes) declared in this module
454 if allprecompinfo:
455 fp.write("\n#\n# Indices of types declared in this module\n#\n")
456 fp.write("_classdeclarations = {\n")
457 for codenamemapper in allprecompinfo:
458 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000459 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000460 fp.write("}\n")
461
Jack Jansen60762cb2000-08-17 22:11:45 +0000462 if suitelist:
463 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
464 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000465 fp.write(",\n\t\t%s_Events"%modname)
466 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000467 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000468 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000469 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000470
Jack Jansen714caa02003-03-21 16:07:39 +0000471def precompilesuite(suite, basepackage=None, edit_modnames=None, output=None):
Jack Jansen12b2b762000-08-20 19:30:56 +0000472 """Parse a single suite without generating the output. This step is needed
473 so we can resolve recursive references by suites to enums/comps/etc declared
474 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000475 [name, desc, code, level, version, events, classes, comps, enums] = suite
476
477 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000478 if len(modname) > 28:
479 modname = modname[:27]
Jack Jansen714caa02003-03-21 16:07:39 +0000480 if edit_modnames is None:
481 pathname = EasyDialogs.AskFileForSave(message='Python output file',
482 savedFileName=modname+'.py')
483 else:
484 for old, new in edit_modnames:
485 if old == modname:
486 modname = new
487 if modname:
488 pathname = os.path.join(output, modname + '.py')
489 else:
490 pathname = None
Jack Jansendf976ca2003-01-26 20:35:47 +0000491 if not pathname:
Jack Jansen12b2b762000-08-20 19:30:56 +0000492 return None, None, None, None, None
493
Jack Jansen60762cb2000-08-17 22:11:45 +0000494 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000495
496 if basepackage and basepackage._code_to_module.has_key(code):
497 # We are an extension of a baseclass (usually an application extending
498 # Standard_Suite or so). Import everything from our base module
499 basemodule = basepackage._code_to_module[code]
500 else:
501 # We are not an extension.
502 basemodule = None
503
504 enumsneeded = {}
505 for event in events:
506 findenumsinevent(event, enumsneeded)
507
Jack Jansen59cdbce2003-03-21 16:28:09 +0000508 objc = ObjectCompiler(None, basemodule, interact=(edit_modnames is None))
Jack Jansen12b2b762000-08-20 19:30:56 +0000509 for cls in classes:
510 objc.compileclass(cls)
511 for cls in classes:
512 objc.fillclasspropsandelems(cls)
513 for comp in comps:
514 objc.compilecomparison(comp)
515 for enum in enums:
516 objc.compileenumeration(enum)
517
518 for enum in enumsneeded.keys():
519 objc.checkforenum(enum)
520
521 objc.dumpindex()
522
523 precompinfo = objc.getprecompinfo(modname)
524
Jack Jansendf976ca2003-01-26 20:35:47 +0000525 return code, suite, pathname, modname, precompinfo
Jack Jansen12b2b762000-08-20 19:30:56 +0000526
Jack Jansen59cdbce2003-03-21 16:28:09 +0000527def compilesuite((suite, pathname, modname), major, minor, language, script,
528 fname, basepackage, precompinfo, interact=1):
Jack Jansen12b2b762000-08-20 19:30:56 +0000529 """Generate code for a single suite"""
530 [name, desc, code, level, version, events, classes, comps, enums] = suite
Jack Jansen03b9c912003-03-28 22:04:22 +0000531 # Sort various lists, so re-generated source is easier compared
532 events.sort()
533 classes.sort()
534 comps.sort()
535 enums.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +0000536
Jack Jansendf976ca2003-01-26 20:35:47 +0000537 fp = open(pathname, 'w')
538 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000539
Jack Jansen8b777672002-08-07 14:49:00 +0000540 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000541 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000542 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000543 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
544 (major, minor, language, script))
545 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000546
547 fp.write('import aetools\n')
548 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000549 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000550 if basepackage and basepackage._code_to_module.has_key(code):
551 # We are an extension of a baseclass (usually an application extending
552 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000553 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000554 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000555 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
556 # This is needed by CodeWarrior and some others.
557 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
558 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000559 else:
560 # We are not an extension.
561 basemodule = None
562 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000563
564 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000565 if events:
566 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000567 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000568 else:
569 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000570
Jack Jansen59cdbce2003-03-21 16:28:09 +0000571 objc = ObjectCompiler(fp, basemodule, precompinfo, interact=interact)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000572 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000573 objc.compileclass(cls)
574 for cls in classes:
575 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000576 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000577 objc.compilecomparison(comp)
578 for enum in enums:
579 objc.compileenumeration(enum)
580
581 for enum in enumsneeded.keys():
582 objc.checkforenum(enum)
583
584 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000585
586 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000587
Jack Jansen60762cb2000-08-17 22:11:45 +0000588def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000589 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000590 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000591 if module:
592 modshortname = string.split(module.__name__, '.')[-1]
593 baseclassname = '%s_Events'%modshortname
594 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000595 else:
596 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000597
Jack Jansen66544221997-08-08 14:49:02 +0000598def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000599 """Generate code for a single event"""
600 [name, desc, code, subcode, returns, accepts, arguments] = event
601 funcname = identify(name)
602 #
603 # generate name->keyword map
604 #
605 if arguments:
606 fp.write("\t_argmap_%s = {\n"%funcname)
607 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000608 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000609 fp.write("\t}\n\n")
610
611 #
612 # Generate function header
613 #
614 has_arg = (not is_null(accepts))
615 opt_arg = (has_arg and is_optional(accepts))
616
Jack Jansen84264771995-10-03 14:35:58 +0000617 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000618 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000619 if not opt_arg:
620 fp.write("_object, ") # Include direct object, if it has one
621 else:
622 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000623 else:
Jack Jansen84264771995-10-03 14:35:58 +0000624 fp.write("_no_object=None, ") # For argument checking
625 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000626 #
627 # Generate doc string (important, since it may be the only
628 # available documentation, due to our name-remaping)
629 #
Jack Jansen21f67582002-08-07 15:44:53 +0000630 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000631 if has_arg:
632 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
633 elif opt_arg:
634 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
635 for arg in arguments:
636 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
637 getdatadoc(arg[2])))
638 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
639 if not is_null(returns):
640 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
641 fp.write('\t\t"""\n')
642 #
643 # Fiddle the args so everything ends up in 'arguments' dictionary
644 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000645 fp.write("\t\t_code = %s\n"% `code`)
646 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000647 #
648 # Do keyword name substitution
649 #
650 if arguments:
651 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
652 else:
653 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
654 #
655 # Stuff required arg (if there is one) into arguments
656 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000657 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000658 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000659 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000660 fp.write("\t\tif _object:\n")
661 fp.write("\t\t\t_arguments['----'] = _object\n")
662 else:
663 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000664 fp.write("\n")
665 #
Jack Jansen73215141995-10-09 23:09:23 +0000666 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000667 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000668 for a in arguments:
669 if is_enum(a[2]):
670 kname = a[1]
671 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000672 if ename <> '****':
673 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000674 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000675 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000676 fp.write("\n")
677 #
678 # Do the transaction
679 #
Jack Jansen84264771995-10-03 14:35:58 +0000680 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
681 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000682 #
683 # Error handling
684 #
Jack Jansen18983532002-04-23 21:03:21 +0000685 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000686 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000687 fp.write("\t\t# XXXX Optionally decode result\n")
688 #
689 # Decode result
690 #
Jack Jansen84264771995-10-03 14:35:58 +0000691 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000692 if is_enum(returns):
693 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000694 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000695 fp.write("\n")
696
697# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
698# print "# returns", compiledata(returns)
699# print "# accepts", compiledata(accepts)
700# for arg in arguments:
701# compileargument(arg)
702
703def compileargument(arg):
704 [name, keyword, what] = arg
705 print "# %s (%s)" % (name, `keyword`), compiledata(what)
706
Jack Jansen12b2b762000-08-20 19:30:56 +0000707def findenumsinevent(event, enumsneeded):
708 """Find all enums for a single event"""
709 [name, desc, code, subcode, returns, accepts, arguments] = event
710 for a in arguments:
711 if is_enum(a[2]):
712 ename = a[2][0]
713 if ename <> '****':
714 enumsneeded[ename] = 1
715
716#
717# This class stores the code<->name translations for a single module. It is used
718# to keep the information while we're compiling the module, but we also keep these objects
719# around so if one suite refers to, say, an enum in another suite we know where to
720# find it. Finally, if we really can't find a code, the user can add modules by
721# hand.
722#
723class CodeNameMapper:
724
Jack Jansen59cdbce2003-03-21 16:28:09 +0000725 def __init__(self, interact=1):
Jack Jansen12b2b762000-08-20 19:30:56 +0000726 self.code2name = {
727 "property" : {},
728 "class" : {},
729 "enum" : {},
730 "comparison" : {},
731 }
732 self.name2code = {
733 "property" : {},
734 "class" : {},
735 "enum" : {},
736 "comparison" : {},
737 }
738 self.modulename = None
739 self.star_imported = 0
Jack Jansen59cdbce2003-03-21 16:28:09 +0000740 self.can_interact = interact
Jack Jansen12b2b762000-08-20 19:30:56 +0000741
742 def addnamecode(self, type, name, code):
743 self.name2code[type][name] = code
744 if not self.code2name[type].has_key(code):
745 self.code2name[type][code] = name
746
747 def hasname(self, type, name):
748 return self.name2code[type].has_key(name)
749
750 def hascode(self, type, code):
751 return self.code2name[type].has_key(code)
752
753 def findcodename(self, type, code):
754 if not self.hascode(type, code):
755 return None, None, None
756 name = self.code2name[type][code]
757 if self.modulename and not self.star_imported:
758 qualname = '%s.%s'%(self.modulename, name)
759 else:
760 qualname = name
761 return name, qualname, self.modulename
762
763 def getall(self, type):
764 return self.code2name[type].items()
765
766 def addmodule(self, module, name, star_imported):
767 self.modulename = name
768 self.star_imported = star_imported
769 for code, name in module._propdeclarations.items():
770 self.addnamecode('property', name, code)
771 for code, name in module._classdeclarations.items():
772 self.addnamecode('class', name, code)
773 for code in module._enumdeclarations.keys():
774 self.addnamecode('enum', '_Enum_'+identify(code), code)
775 for code, name in module._compdeclarations.items():
776 self.addnamecode('comparison', name, code)
777
778 def prepareforexport(self, name=None):
779 if not self.modulename:
780 self.modulename = name
781 return self
782
Jack Jansen66544221997-08-08 14:49:02 +0000783class ObjectCompiler:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000784 def __init__(self, fp, basesuite=None, othernamemappers=None, interact=1):
Jack Jansen66544221997-08-08 14:49:02 +0000785 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000786 self.basesuite = basesuite
Jack Jansen59cdbce2003-03-21 16:28:09 +0000787 self.can_interact = interact
788 self.namemappers = [CodeNameMapper(self.can_interact)]
Jack Jansen12b2b762000-08-20 19:30:56 +0000789 if othernamemappers:
790 self.othernamemappers = othernamemappers[:]
791 else:
792 self.othernamemappers = []
793 if basesuite:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000794 basemapper = CodeNameMapper(self.can_interact)
Jack Jansen12b2b762000-08-20 19:30:56 +0000795 basemapper.addmodule(basesuite, '', 1)
796 self.namemappers.append(basemapper)
797
798 def getprecompinfo(self, modname):
799 list = []
800 for mapper in self.namemappers:
801 emapper = mapper.prepareforexport(modname)
802 if emapper:
803 list.append(emapper)
804 return list
Jack Jansen66544221997-08-08 14:49:02 +0000805
806 def findcodename(self, type, code):
807 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000808 # First try: check whether we already know about this code.
809 for mapper in self.namemappers:
810 if mapper.hascode(type, code):
811 return mapper.findcodename(type, code)
812 # Second try: maybe one of the other modules knows about it.
813 for mapper in self.othernamemappers:
814 if mapper.hascode(type, code):
815 self.othernamemappers.remove(mapper)
816 self.namemappers.append(mapper)
817 if self.fp:
818 self.fp.write("import %s\n"%mapper.modulename)
819 break
820 else:
821 # If all this has failed we ask the user for a guess on where it could
822 # be and retry.
823 if self.fp:
824 m = self.askdefinitionmodule(type, code)
825 else:
826 m = None
827 if not m: return None, None, None
Jack Jansen59cdbce2003-03-21 16:28:09 +0000828 mapper = CodeNameMapper(self.can_interact)
Jack Jansen12b2b762000-08-20 19:30:56 +0000829 mapper.addmodule(m, m.__name__, 0)
830 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000831
832 def askdefinitionmodule(self, type, code):
Jack Jansen59cdbce2003-03-21 16:28:09 +0000833 if not self.can_interact:
834 print "** No definition for %s '%s' found" % (type, code)
835 return None
Jack Jansendf976ca2003-01-26 20:35:47 +0000836 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
837 if not path: return
838 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000839 modname = os.path.splitext(file)[0]
840 if not path in sys.path:
841 sys.path.insert(0, path)
842 m = __import__(modname)
843 self.fp.write("import %s\n"%modname)
844 return m
845
846 def compileclass(self, cls):
847 [name, code, desc, properties, elements] = cls
848 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000849 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000850 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000851 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
852 if self.fp:
853 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000854 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000855 if self.fp:
856 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000857 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000858 self.fp.write('\twant = %s\n' % `code`)
859 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen00c34832003-03-28 23:37:05 +0000860 properties.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000861 for prop in properties:
862 self.compileproperty(prop)
Jack Jansen00c34832003-03-28 23:37:05 +0000863 elements.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000864 for elem in elements:
865 self.compileelement(elem)
866
867 def compileproperty(self, prop):
868 [name, code, what] = prop
869 if code == 'c@#!':
870 # Something silly with plurals. Skip it.
871 return
872 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000873 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000874 # plural forms and such
875 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000876 if pname == othername:
877 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000878 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000879 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000880 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000881 if self.fp:
882 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000883 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000884 self.fp.write("\twhich = %s\n" % `code`)
885 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000886 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000887
888 def compileelement(self, elem):
889 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000890 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000891 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000892
Jack Jansen66544221997-08-08 14:49:02 +0000893 def fillclasspropsandelems(self, cls):
894 [name, code, desc, properties, elements] = cls
895 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000896 if self.namemappers[0].hascode('class', code) and \
897 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000898 # This is an other name (plural or so) for something else. Skip.
899 return
900 plist = []
901 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000902 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000903 for prop in properties:
904 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000905 if pcode == "c@#^":
906 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000907 if pcode == 'c@#!':
908 continue
909 pname = identify(pname)
910 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000911
912 superclassnames = []
913 for superclass in superclasses:
914 superId, superDesc, dummy = superclass
915 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
916 superclassnames.append(superclassname)
917
918 if self.fp:
919 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
920
Jack Jansen66544221997-08-08 14:49:02 +0000921 for elem in elements:
922 [ecode, keyform] = elem
923 if ecode == 'c@#!':
924 continue
925 name, ename, module = self.findcodename('class', ecode)
926 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000927 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000928 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000929 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000930 elist.append((name, ename))
Jack Jansen00c34832003-03-28 23:37:05 +0000931
932 plist.sort()
933 elist.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +0000934
935 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000936 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000937 for n in plist:
938 self.fp.write("\t'%s' : %s,\n"%(n, n))
939 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000940 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000941 for n, fulln in elist:
942 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
943 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000944
945 def compilecomparison(self, comp):
946 [name, code, comment] = comp
947 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000948 self.namemappers[0].addnamecode('comparison', iname, code)
949 if self.fp:
950 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000951 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000952
953 def compileenumeration(self, enum):
954 [code, items] = enum
955 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000956 if self.fp:
957 self.fp.write("%s = {\n" % name)
958 for item in items:
959 self.compileenumerator(item)
960 self.fp.write("}\n\n")
961 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000962 return code
963
964 def compileenumerator(self, item):
965 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +0000966 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +0000967
968 def checkforenum(self, enum):
969 """This enum code is used by an event. Make sure it's available"""
970 name, fullname, module = self.findcodename('enum', enum)
971 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000972 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000973 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000974 return
975 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000976 if self.fp:
977 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000978
979 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000980 if not self.fp:
981 return
Jack Jansen66544221997-08-08 14:49:02 +0000982 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
Jack Jansen00c34832003-03-28 23:37:05 +0000983
Jack Jansen66544221997-08-08 14:49:02 +0000984 self.fp.write("_classdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +0000985 classlist = self.namemappers[0].getall('class')
986 classlist.sort()
987 for k, v in classlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000988 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000989 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +0000990
Jack Jansen66544221997-08-08 14:49:02 +0000991 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +0000992 proplist = self.namemappers[0].getall('property')
993 proplist.sort()
994 for k, v in proplist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000995 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000996 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +0000997
Jack Jansen66544221997-08-08 14:49:02 +0000998 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +0000999 complist = self.namemappers[0].getall('comparison')
1000 complist.sort()
1001 for k, v in complist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001002 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001003 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001004
Jack Jansen66544221997-08-08 14:49:02 +00001005 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001006 enumlist = self.namemappers[0].getall('enum')
1007 enumlist.sort()
1008 for k, v in enumlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001009 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001010 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +00001011
1012def compiledata(data):
1013 [type, description, flags] = data
1014 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
1015
1016def is_null(data):
1017 return data[0] == 'null'
1018
1019def is_optional(data):
1020 return (data[2] & 0x8000)
1021
1022def is_enum(data):
1023 return (data[2] & 0x2000)
1024
1025def getdatadoc(data):
1026 [type, descr, flags] = data
1027 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +00001028 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +00001029 if type == '****':
1030 return 'anything'
1031 if type == 'obj ':
1032 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001033 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +00001034
1035dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
1036def compiledataflags(flags):
1037 bits = []
1038 for i in range(16):
1039 if flags & (1<<i):
1040 if i in dataflagdict.keys():
1041 bits.append(dataflagdict[i])
1042 else:
1043 bits.append(`i`)
1044 return '[%s]' % string.join(bits)
1045
Jack Jansen8b777672002-08-07 14:49:00 +00001046def ascii(str):
1047 """Return a string with all non-ascii characters hex-encoded"""
1048 if type(str) != type(''):
1049 return map(ascii, str)
1050 rv = ''
1051 for c in str:
1052 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
1053 rv = rv + c
1054 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001055 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +00001056 return rv
1057
Jack Jansen5ccd8261995-07-17 11:43:20 +00001058def identify(str):
1059 """Turn any string into an identifier:
1060 - replace space by _
1061 - replace other illegal chars by _xx_ (hex code)
1062 - prepend _ if the result is a python keyword
1063 """
Jack Jansen66544221997-08-08 14:49:02 +00001064 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +00001065 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +00001066 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +00001067 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001068 ok2 = ok + string.digits
1069 for c in str:
1070 if c in ok:
1071 rv = rv + c
1072 elif c == ' ':
1073 rv = rv + '_'
1074 else:
1075 rv = rv + '_%02.2x_'%ord(c)
1076 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +00001077 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +00001078 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001079 return rv
1080
1081# Call the main program
1082
1083if __name__ == '__main__':
1084 main()
1085 sys.exit(1)