blob: 9e599c89a9e9b467123b86aa058364db6cb357d2 [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 Jansen1269be52003-03-29 22:54:00 +000022import Carbon.Folder
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000023import MacOS
Jack Jansen714caa02003-03-21 16:07:39 +000024import getopt
Jack Jansen1269be52003-03-29 22:54:00 +000025import plistlib
Jack Jansen5ccd8261995-07-17 11:43:20 +000026
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000027_MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
28DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
29DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
Jack Jansen40926062002-03-30 23:43:04 +000030
Jack Jansen714caa02003-03-21 16:07:39 +000031def usage():
32 sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0])
33 sys.stderr.write("""Options:
34--output pkgdir Pathname of the output package (short: -o)
35--resource Parse resource file in stead of launching application (-r)
36--base package Use another base package in stead of default StdSuites (-b)
37--edit old=new Edit suite names, use empty new to skip a suite (-e)
Jack Jansen59cdbce2003-03-21 16:28:09 +000038--creator code Set creator code for package (-c)
Jack Jansen714caa02003-03-21 16:07:39 +000039""")
40 sys.exit(1)
41
Jack Jansen5ccd8261995-07-17 11:43:20 +000042def main():
Jack Jansen40926062002-03-30 23:43:04 +000043 if len(sys.argv) > 1:
Jack Jansen59cdbce2003-03-21 16:28:09 +000044 SHORTOPTS = "rb:o:e:c:"
45 LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=")
Jack Jansen714caa02003-03-21 16:07:39 +000046 try:
47 opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
48 except getopt.GetoptError:
49 usage()
50
51 process_func = processfile
52 basepkgname = 'StdSuites'
53 output = None
54 edit_modnames = []
Jack Jansen59cdbce2003-03-21 16:28:09 +000055 creatorsignature = None
Jack Jansen714caa02003-03-21 16:07:39 +000056
57 for o, a in opts:
58 if o in ('-r', '--resource'):
59 process_func = processfile_fromresource
60 if o in ('-b', '--base'):
61 basepkgname = a
62 if o in ('-o', '--output'):
63 output = a
64 if o in ('-e', '--edit'):
65 split = a.split('=')
66 if len(split) != 2:
67 usage()
68 edit_modnames.append(split)
Jack Jansen59cdbce2003-03-21 16:28:09 +000069 if o in ('-c', '--creator'):
70 if len(a) != 4:
71 sys.stderr.write("creator must be 4-char string\n")
72 sys.exit(1)
73 creatorsignature = a
74
Jack Jansen714caa02003-03-21 16:07:39 +000075
76 if output and len(args) > 1:
77 sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0])
78 sys.exit(1)
79
80 for filename in args:
81 process_func(filename, output=output, basepkgname=basepkgname,
Jack Jansen59cdbce2003-03-21 16:28:09 +000082 edit_modnames=edit_modnames, creatorsignature=creatorsignature)
Jack Jansen40926062002-03-30 23:43:04 +000083 else:
Jack Jansen1269be52003-03-29 22:54:00 +000084 main_interactive()
85
86def main_interactive(interact=0, basepkgname='StdSuites'):
87 if interact:
88 # Ask for save-filename for each module
89 edit_modnames = None
90 else:
91 # Use default filenames for each module
92 edit_modnames = []
93 appsfolder = Carbon.Folder.FSFindFolder(-32765, 'apps', 0)
94 filename = EasyDialogs.AskFileForOpen(
95 message='Select scriptable application',
96 dialogOptionFlags=0x1056, # allow selection of .app bundles
97 defaultLocation=appsfolder)
98 if not filename:
99 return
100 if not is_scriptable(filename):
101 if EasyDialogs.AskYesNoCancel(
102 "Warning: application does not seem scriptable",
103 yes="Continue", default=2, no="") <= 0:
104 return
105 try:
106 processfile(filename, edit_modnames=edit_modnames, basepkgname=basepkgname)
107 except MacOS.Error, arg:
108 print "Error getting terminology:", arg
109 print "Retry, manually parsing resources"
110 processfile_fromresource(filename, edit_modnames=edit_modnames,
111 basepkgname=basepkgname)
112
113def is_scriptable(application):
114 """Return true if the application is scriptable"""
115 if os.path.isdir(application):
116 plistfile = os.path.join(application, 'Contents', 'Info.plist')
117 if not os.path.exists(plistfile):
118 return False
119 plist = plistlib.Plist.fromFile(plistfile)
120 return plist.get('NSAppleScriptEnabled', False)
121 # If it is a file test for an aete/aeut resource.
122 currf = CurResFile()
123 try:
124 refno = macresource.open_pathname(application)
125 except MacOS.Error:
126 return False
127 UseResFile(refno)
128 n_terminology = Count1Resources('aete') + Count1Resources('aeut') + \
129 Count1Resources('scsz') + Count1Resources('osiz')
130 CloseResFile(refno)
131 UseResFile(currf)
132 return n_terminology > 0
Jack Jansen5ccd8261995-07-17 11:43:20 +0000133
Jack Jansen59cdbce2003-03-21 16:28:09 +0000134def processfile_fromresource(fullname, output=None, basepkgname=None,
135 edit_modnames=None, creatorsignature=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000136 """Process all resources in a single file"""
Jack Jansen1269be52003-03-29 22:54:00 +0000137 if not is_scriptable(fullname):
138 print "Warning: app does not seem scriptable: %s" % fullname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000139 cur = CurResFile()
Jack Jansen40926062002-03-30 23:43:04 +0000140 print "Processing", fullname
141 rf = macresource.open_pathname(fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000142 try:
143 UseResFile(rf)
144 resources = []
145 for i in range(Count1Resources('aete')):
146 res = Get1IndResource('aete', 1+i)
147 resources.append(res)
148 for i in range(Count1Resources('aeut')):
149 res = Get1IndResource('aeut', 1+i)
150 resources.append(res)
151 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +0000152 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000153 for res in resources:
154 print "decoding", res.GetResInfo(), "..."
155 data = res.data
156 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +0000157 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000158 finally:
159 if rf <> cur:
160 CloseResFile(rf)
161 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +0000162 # switch back (needed for dialogs in Python)
163 UseResFile(cur)
Jack Jansen714caa02003-03-21 16:07:39 +0000164 compileaetelist(aetelist, fullname, output=output,
Jack Jansen59cdbce2003-03-21 16:28:09 +0000165 basepkgname=basepkgname, edit_modnames=edit_modnames,
166 creatorsignature=creatorsignature)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000167
Jack Jansen59cdbce2003-03-21 16:28:09 +0000168def processfile(fullname, output=None, basepkgname=None,
169 edit_modnames=None, creatorsignature=None):
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000170 """Ask an application for its terminology and process that"""
Jack Jansen1269be52003-03-29 22:54:00 +0000171 if not is_scriptable(fullname):
172 print "Warning: app does not seem scriptable: %s" % fullname
Jack Jansen00c34832003-03-28 23:37:05 +0000173 print "\nASKING FOR aete DICTIONARY IN", `fullname`
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000174 try:
175 aedescobj, launched = OSATerminology.GetAppTerminology(fullname)
176 except MacOS.Error, arg:
Jack Jansen03b9c912003-03-28 22:04:22 +0000177 if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound
178 print "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually"
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000179 aedata, sig = getappterminology(fullname)
180 if not creatorsignature:
181 creatorsignature = sig
182 else:
183 raise
184 else:
185 if launched:
186 print "Launched", fullname
187 raw = aetools.unpack(aedescobj)
188 if not raw:
189 print 'Unpack returned empty value:', raw
190 return
191 if not raw[0].data:
192 print 'Unpack returned value without data:', raw
193 return
194 aedata = raw[0]
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000195 aete = decode(aedata.data)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000196 compileaete(aete, None, fullname, output=output, basepkgname=basepkgname,
Jack Jansen03b9c912003-03-28 22:04:22 +0000197 creatorsignature=creatorsignature, edit_modnames=edit_modnames)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000198
199def getappterminology(fullname):
200 """Get application terminology by sending an AppleEvent"""
201 # First check that we actually can send AppleEvents
202 if not MacOS.WMAvailable():
203 raise RuntimeError, "Cannot send AppleEvents, no access to window manager"
204 # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless
205 # you have created an event loop first.
206 import Carbon.Evt
207 Carbon.Evt.WaitNextEvent(0,0)
Jack Jansen03b9c912003-03-28 22:04:22 +0000208 if os.path.isdir(fullname):
209 # Now get the signature of the application, hoping it is a bundle
210 pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo')
211 if not os.path.exists(pkginfo):
212 raise RuntimeError, "No PkgInfo file found"
213 tp_cr = open(pkginfo, 'rb').read()
214 cr = tp_cr[4:8]
215 else:
216 # Assume it is a file
217 cr, tp = MacOS.GetCreatorAndType(fullname)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000218 # Let's talk to it and ask for its AETE
219 talker = aetools.TalkTo(cr)
Jack Jansen03b9c912003-03-28 22:04:22 +0000220 try:
221 talker._start()
222 except (MacOS.Error, aetools.Error), arg:
223 print 'Warning: start() failed, continuing anyway:', arg
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000224 reply = talker.send("ascr", "gdte")
Jack Jansen03b9c912003-03-28 22:04:22 +0000225 #reply2 = talker.send("ascr", "gdut")
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000226 # Now pick the bits out of the return that we need.
227 return reply[1]['----'], cr
228
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000229
Jack Jansen59cdbce2003-03-21 16:28:09 +0000230def compileaetelist(aetelist, fullname, output=None, basepkgname=None,
231 edit_modnames=None, creatorsignature=None):
Jack Jansen60762cb2000-08-17 22:11:45 +0000232 for aete, resinfo in aetelist:
Jack Jansen714caa02003-03-21 16:07:39 +0000233 compileaete(aete, resinfo, fullname, output=output,
Jack Jansen59cdbce2003-03-21 16:28:09 +0000234 basepkgname=basepkgname, edit_modnames=edit_modnames,
235 creatorsignature=creatorsignature)
Jack Jansen60762cb2000-08-17 22:11:45 +0000236
Jack Jansen5ccd8261995-07-17 11:43:20 +0000237def decode(data):
238 """Decode a resource into a python data structure"""
239 f = StringIO.StringIO(data)
240 aete = generic(getaete, f)
241 aete = simplify(aete)
242 processed = f.tell()
243 unprocessed = len(f.read())
244 total = f.tell()
245 if unprocessed:
246 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
247 (processed, unprocessed, total))
248 return aete
249
250def simplify(item):
251 """Recursively replace singleton tuples by their constituent item"""
252 if type(item) is types.ListType:
253 return map(simplify, item)
254 elif type(item) == types.TupleType and len(item) == 2:
255 return simplify(item[1])
256 else:
257 return item
258
259
260# Here follows the aete resource decoder.
261# It is presented bottom-up instead of top-down because there are direct
262# references to the lower-level part-decoders from the high-level part-decoders.
263
264def getbyte(f, *args):
265 c = f.read(1)
266 if not c:
267 raise EOFError, 'in getbyte' + str(args)
268 return ord(c)
269
270def getword(f, *args):
271 getalign(f)
272 s = f.read(2)
273 if len(s) < 2:
274 raise EOFError, 'in getword' + str(args)
275 return (ord(s[0])<<8) | ord(s[1])
276
277def getlong(f, *args):
278 getalign(f)
279 s = f.read(4)
280 if len(s) < 4:
281 raise EOFError, 'in getlong' + str(args)
282 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
283
284def getostype(f, *args):
285 getalign(f)
286 s = f.read(4)
287 if len(s) < 4:
288 raise EOFError, 'in getostype' + str(args)
289 return s
290
291def getpstr(f, *args):
292 c = f.read(1)
293 if len(c) < 1:
294 raise EOFError, 'in getpstr[1]' + str(args)
295 nbytes = ord(c)
296 if nbytes == 0: return ''
297 s = f.read(nbytes)
298 if len(s) < nbytes:
299 raise EOFError, 'in getpstr[2]' + str(args)
300 return s
301
302def getalign(f):
303 if f.tell() & 1:
304 c = f.read(1)
305 ##if c <> '\0':
306 ## print 'align:', `c`
307
308def getlist(f, description, getitem):
309 count = getword(f)
310 list = []
311 for i in range(count):
312 list.append(generic(getitem, f))
313 getalign(f)
314 return list
315
316def alt_generic(what, f, *args):
317 print "generic", `what`, args
318 res = vageneric(what, f, args)
319 print '->', `res`
320 return res
321
322def generic(what, f, *args):
323 if type(what) == types.FunctionType:
324 return apply(what, (f,) + args)
325 if type(what) == types.ListType:
326 record = []
327 for thing in what:
328 item = apply(generic, thing[:1] + (f,) + thing[1:])
329 record.append((thing[1], item))
330 return record
331 return "BAD GENERIC ARGS: %s" % `what`
332
333getdata = [
334 (getostype, "type"),
335 (getpstr, "description"),
336 (getword, "flags")
337 ]
338getargument = [
339 (getpstr, "name"),
340 (getostype, "keyword"),
341 (getdata, "what")
342 ]
343getevent = [
344 (getpstr, "name"),
345 (getpstr, "description"),
346 (getostype, "suite code"),
347 (getostype, "event code"),
348 (getdata, "returns"),
349 (getdata, "accepts"),
350 (getlist, "optional arguments", getargument)
351 ]
352getproperty = [
353 (getpstr, "name"),
354 (getostype, "code"),
355 (getdata, "what")
356 ]
357getelement = [
358 (getostype, "type"),
359 (getlist, "keyform", getostype)
360 ]
361getclass = [
362 (getpstr, "name"),
363 (getostype, "class code"),
364 (getpstr, "description"),
365 (getlist, "properties", getproperty),
366 (getlist, "elements", getelement)
367 ]
368getcomparison = [
369 (getpstr, "operator name"),
370 (getostype, "operator ID"),
371 (getpstr, "operator comment"),
372 ]
373getenumerator = [
374 (getpstr, "enumerator name"),
375 (getostype, "enumerator ID"),
376 (getpstr, "enumerator comment")
377 ]
378getenumeration = [
379 (getostype, "enumeration ID"),
380 (getlist, "enumerator", getenumerator)
381 ]
382getsuite = [
383 (getpstr, "suite name"),
384 (getpstr, "suite description"),
385 (getostype, "suite ID"),
386 (getword, "suite level"),
387 (getword, "suite version"),
388 (getlist, "events", getevent),
389 (getlist, "classes", getclass),
390 (getlist, "comparisons", getcomparison),
391 (getlist, "enumerations", getenumeration)
392 ]
393getaete = [
394 (getword, "major/minor version in BCD"),
395 (getword, "language code"),
396 (getword, "script code"),
397 (getlist, "suites", getsuite)
398 ]
399
Jack Jansen59cdbce2003-03-21 16:28:09 +0000400def compileaete(aete, resinfo, fname, output=None, basepkgname=None,
401 edit_modnames=None, creatorsignature=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000402 """Generate code for a full aete resource. fname passed for doc purposes"""
403 [version, language, script, suites] = aete
404 major, minor = divmod(version, 256)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000405 if not creatorsignature:
406 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
Jack Jansen40926062002-03-30 23:43:04 +0000407 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000408 if language:
409 packagename = packagename+'_lang%d'%language
410 if script:
411 packagename = packagename+'_script%d'%script
412 if len(packagename) > 27:
413 packagename = packagename[:27]
Jack Jansen714caa02003-03-21 16:07:39 +0000414 if output:
415 # XXXX Put this in site-packages if it isn't a full pathname?
416 if not os.path.exists(output):
417 os.mkdir(output)
418 pathname = output
419 else:
420 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
421 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000422 output = pathname
Jack Jansendf976ca2003-01-26 20:35:47 +0000423 if not pathname:
Jack Jansen60762cb2000-08-17 22:11:45 +0000424 return
Jack Jansen60762cb2000-08-17 22:11:45 +0000425 packagename = os.path.split(os.path.normpath(pathname))[1]
Jack Jansen714caa02003-03-21 16:07:39 +0000426 if not basepkgname:
427 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
428 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
Jack Jansendf976ca2003-01-26 20:35:47 +0000429 if basepkgname:
430 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
Jack Jansen714caa02003-03-21 16:07:39 +0000431 if dirname and not dirname in sys.path:
Jack Jansen60762cb2000-08-17 22:11:45 +0000432 sys.path.insert(0, dirname)
433 basepackage = __import__(basepkgname)
434 else:
435 basepackage = None
Jack Jansen60762cb2000-08-17 22:11:45 +0000436 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000437 allprecompinfo = []
438 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000439 for suite in suites:
Jack Jansen714caa02003-03-21 16:07:39 +0000440 code, suite, pathname, modname, precompinfo = precompilesuite(suite, basepackage,
441 output=output, edit_modnames=edit_modnames)
Jack Jansen12b2b762000-08-20 19:30:56 +0000442 if not code:
443 continue
444 allprecompinfo = allprecompinfo + precompinfo
Jack Jansendf976ca2003-01-26 20:35:47 +0000445 suiteinfo = suite, pathname, modname
Jack Jansen12b2b762000-08-20 19:30:56 +0000446 suitelist.append((code, modname))
447 allsuites.append(suiteinfo)
448 for suiteinfo in allsuites:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000449 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage,
450 allprecompinfo, interact=(edit_modnames is None))
Jack Jansen714caa02003-03-21 16:07:39 +0000451 initfilename = os.path.join(output, '__init__.py')
Jack Jansendf976ca2003-01-26 20:35:47 +0000452 fp = open(initfilename, 'w')
453 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
Jack Jansen60762cb2000-08-17 22:11:45 +0000454 fp.write('"""\n')
Jack Jansen8307e022003-03-29 00:08:24 +0000455 fp.write("Package generated from %s\n"%ascii(fname))
Jack Jansen8b777672002-08-07 14:49:00 +0000456 if resinfo:
457 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000458 fp.write('"""\n')
459 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000460 fp.write('Error = aetools.Error\n')
Jack Jansen03b9c912003-03-28 22:04:22 +0000461 suitelist.sort()
Jack Jansen60762cb2000-08-17 22:11:45 +0000462 for code, modname in suitelist:
463 fp.write("import %s\n" % modname)
464 fp.write("\n\n_code_to_module = {\n")
465 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000466 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000467 fp.write("}\n\n")
468 fp.write("\n\n_code_to_fullname = {\n")
469 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000470 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000471 fp.write("}\n\n")
472 for code, modname in suitelist:
473 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000474
475 # Generate property dicts and element dicts for all types declared in this module
Jack Jansen7e0bc112003-03-21 12:04:19 +0000476 fp.write("\ndef getbaseclasses(v):\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000477 fp.write("\tif not getattr(v, '_propdict', None):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000478 fp.write("\t\tv._propdict = {}\n")
479 fp.write("\t\tv._elemdict = {}\n")
Jack Jansen7e0bc112003-03-21 12:04:19 +0000480 fp.write("\t\tfor superclassname in getattr(v, '_superclassnames', []):\n")
481 fp.write("\t\t\tsuperclass = eval(superclassname)\n")
482 fp.write("\t\t\tgetbaseclasses(superclass)\n")
483 fp.write("\t\t\tv._propdict.update(getattr(superclass, '_propdict', {}))\n")
484 fp.write("\t\t\tv._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000485 fp.write("\t\tv._propdict.update(getattr(v, '_privpropdict', {}))\n")
486 fp.write("\t\tv._elemdict.update(getattr(v, '_privelemdict', {}))\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000487 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000488 fp.write("import StdSuites\n")
Jack Jansen03b9c912003-03-28 22:04:22 +0000489 allprecompinfo.sort()
Jack Jansen8b777672002-08-07 14:49:00 +0000490 if allprecompinfo:
491 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
492 for codenamemapper in allprecompinfo:
493 for k, v in codenamemapper.getall('class'):
494 fp.write("getbaseclasses(%s)\n" % v)
495
496 # Generate a code-to-name mapper for all of the types (classes) declared in this module
497 if allprecompinfo:
498 fp.write("\n#\n# Indices of types declared in this module\n#\n")
499 fp.write("_classdeclarations = {\n")
500 for codenamemapper in allprecompinfo:
501 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000502 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000503 fp.write("}\n")
504
Jack Jansen60762cb2000-08-17 22:11:45 +0000505 if suitelist:
506 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
507 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000508 fp.write(",\n\t\t%s_Events"%modname)
509 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000510 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000511 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000512 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000513
Jack Jansen714caa02003-03-21 16:07:39 +0000514def precompilesuite(suite, basepackage=None, edit_modnames=None, output=None):
Jack Jansen12b2b762000-08-20 19:30:56 +0000515 """Parse a single suite without generating the output. This step is needed
516 so we can resolve recursive references by suites to enums/comps/etc declared
517 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000518 [name, desc, code, level, version, events, classes, comps, enums] = suite
519
520 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000521 if len(modname) > 28:
522 modname = modname[:27]
Jack Jansen714caa02003-03-21 16:07:39 +0000523 if edit_modnames is None:
524 pathname = EasyDialogs.AskFileForSave(message='Python output file',
525 savedFileName=modname+'.py')
526 else:
527 for old, new in edit_modnames:
528 if old == modname:
529 modname = new
530 if modname:
531 pathname = os.path.join(output, modname + '.py')
532 else:
533 pathname = None
Jack Jansendf976ca2003-01-26 20:35:47 +0000534 if not pathname:
Jack Jansen12b2b762000-08-20 19:30:56 +0000535 return None, None, None, None, None
536
Jack Jansen60762cb2000-08-17 22:11:45 +0000537 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000538
539 if basepackage and basepackage._code_to_module.has_key(code):
540 # We are an extension of a baseclass (usually an application extending
541 # Standard_Suite or so). Import everything from our base module
542 basemodule = basepackage._code_to_module[code]
543 else:
544 # We are not an extension.
545 basemodule = None
546
547 enumsneeded = {}
548 for event in events:
549 findenumsinevent(event, enumsneeded)
550
Jack Jansen59cdbce2003-03-21 16:28:09 +0000551 objc = ObjectCompiler(None, basemodule, interact=(edit_modnames is None))
Jack Jansen12b2b762000-08-20 19:30:56 +0000552 for cls in classes:
553 objc.compileclass(cls)
554 for cls in classes:
555 objc.fillclasspropsandelems(cls)
556 for comp in comps:
557 objc.compilecomparison(comp)
558 for enum in enums:
559 objc.compileenumeration(enum)
560
561 for enum in enumsneeded.keys():
562 objc.checkforenum(enum)
563
564 objc.dumpindex()
565
566 precompinfo = objc.getprecompinfo(modname)
567
Jack Jansendf976ca2003-01-26 20:35:47 +0000568 return code, suite, pathname, modname, precompinfo
Jack Jansen12b2b762000-08-20 19:30:56 +0000569
Jack Jansen59cdbce2003-03-21 16:28:09 +0000570def compilesuite((suite, pathname, modname), major, minor, language, script,
571 fname, basepackage, precompinfo, interact=1):
Jack Jansen12b2b762000-08-20 19:30:56 +0000572 """Generate code for a single suite"""
573 [name, desc, code, level, version, events, classes, comps, enums] = suite
Jack Jansen03b9c912003-03-28 22:04:22 +0000574 # Sort various lists, so re-generated source is easier compared
575 events.sort()
576 classes.sort()
577 comps.sort()
578 enums.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +0000579
Jack Jansendf976ca2003-01-26 20:35:47 +0000580 fp = open(pathname, 'w')
581 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000582
Jack Jansen8b777672002-08-07 14:49:00 +0000583 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000584 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000585 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000586 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
587 (major, minor, language, script))
588 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000589
590 fp.write('import aetools\n')
591 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000592 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000593 if basepackage and basepackage._code_to_module.has_key(code):
594 # We are an extension of a baseclass (usually an application extending
595 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000596 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000597 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000598 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
599 # This is needed by CodeWarrior and some others.
600 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
601 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000602 else:
603 # We are not an extension.
604 basemodule = None
605 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000606
607 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000608 if events:
609 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000610 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000611 else:
612 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000613
Jack Jansen59cdbce2003-03-21 16:28:09 +0000614 objc = ObjectCompiler(fp, basemodule, precompinfo, interact=interact)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000615 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000616 objc.compileclass(cls)
617 for cls in classes:
618 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000619 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000620 objc.compilecomparison(comp)
621 for enum in enums:
622 objc.compileenumeration(enum)
623
624 for enum in enumsneeded.keys():
625 objc.checkforenum(enum)
626
627 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000628
629 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000630
Jack Jansen60762cb2000-08-17 22:11:45 +0000631def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000632 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000633 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000634 if module:
635 modshortname = string.split(module.__name__, '.')[-1]
636 baseclassname = '%s_Events'%modshortname
637 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000638 else:
639 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000640
Jack Jansen66544221997-08-08 14:49:02 +0000641def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000642 """Generate code for a single event"""
643 [name, desc, code, subcode, returns, accepts, arguments] = event
644 funcname = identify(name)
645 #
646 # generate name->keyword map
647 #
648 if arguments:
649 fp.write("\t_argmap_%s = {\n"%funcname)
650 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000651 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000652 fp.write("\t}\n\n")
653
654 #
655 # Generate function header
656 #
657 has_arg = (not is_null(accepts))
658 opt_arg = (has_arg and is_optional(accepts))
659
Jack Jansen84264771995-10-03 14:35:58 +0000660 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000661 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000662 if not opt_arg:
663 fp.write("_object, ") # Include direct object, if it has one
664 else:
665 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000666 else:
Jack Jansen84264771995-10-03 14:35:58 +0000667 fp.write("_no_object=None, ") # For argument checking
668 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000669 #
670 # Generate doc string (important, since it may be the only
671 # available documentation, due to our name-remaping)
672 #
Jack Jansen21f67582002-08-07 15:44:53 +0000673 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000674 if has_arg:
675 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
676 elif opt_arg:
677 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
678 for arg in arguments:
679 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
680 getdatadoc(arg[2])))
681 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
682 if not is_null(returns):
683 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
684 fp.write('\t\t"""\n')
685 #
686 # Fiddle the args so everything ends up in 'arguments' dictionary
687 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000688 fp.write("\t\t_code = %s\n"% `code`)
689 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000690 #
691 # Do keyword name substitution
692 #
693 if arguments:
694 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
695 else:
696 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
697 #
698 # Stuff required arg (if there is one) into arguments
699 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000700 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000701 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000702 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000703 fp.write("\t\tif _object:\n")
704 fp.write("\t\t\t_arguments['----'] = _object\n")
705 else:
706 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000707 fp.write("\n")
708 #
Jack Jansen73215141995-10-09 23:09:23 +0000709 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000710 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000711 for a in arguments:
712 if is_enum(a[2]):
713 kname = a[1]
714 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000715 if ename <> '****':
716 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000717 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000718 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000719 fp.write("\n")
720 #
721 # Do the transaction
722 #
Jack Jansen84264771995-10-03 14:35:58 +0000723 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
724 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000725 #
726 # Error handling
727 #
Jack Jansen18983532002-04-23 21:03:21 +0000728 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000729 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000730 fp.write("\t\t# XXXX Optionally decode result\n")
731 #
732 # Decode result
733 #
Jack Jansen84264771995-10-03 14:35:58 +0000734 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000735 if is_enum(returns):
736 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000737 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000738 fp.write("\n")
739
740# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
741# print "# returns", compiledata(returns)
742# print "# accepts", compiledata(accepts)
743# for arg in arguments:
744# compileargument(arg)
745
746def compileargument(arg):
747 [name, keyword, what] = arg
748 print "# %s (%s)" % (name, `keyword`), compiledata(what)
749
Jack Jansen12b2b762000-08-20 19:30:56 +0000750def findenumsinevent(event, enumsneeded):
751 """Find all enums for a single event"""
752 [name, desc, code, subcode, returns, accepts, arguments] = event
753 for a in arguments:
754 if is_enum(a[2]):
755 ename = a[2][0]
756 if ename <> '****':
757 enumsneeded[ename] = 1
758
759#
760# This class stores the code<->name translations for a single module. It is used
761# to keep the information while we're compiling the module, but we also keep these objects
762# around so if one suite refers to, say, an enum in another suite we know where to
763# find it. Finally, if we really can't find a code, the user can add modules by
764# hand.
765#
766class CodeNameMapper:
767
Jack Jansen59cdbce2003-03-21 16:28:09 +0000768 def __init__(self, interact=1):
Jack Jansen12b2b762000-08-20 19:30:56 +0000769 self.code2name = {
770 "property" : {},
771 "class" : {},
772 "enum" : {},
773 "comparison" : {},
774 }
775 self.name2code = {
776 "property" : {},
777 "class" : {},
778 "enum" : {},
779 "comparison" : {},
780 }
781 self.modulename = None
782 self.star_imported = 0
Jack Jansen59cdbce2003-03-21 16:28:09 +0000783 self.can_interact = interact
Jack Jansen12b2b762000-08-20 19:30:56 +0000784
785 def addnamecode(self, type, name, code):
786 self.name2code[type][name] = code
787 if not self.code2name[type].has_key(code):
788 self.code2name[type][code] = name
789
790 def hasname(self, type, name):
791 return self.name2code[type].has_key(name)
792
793 def hascode(self, type, code):
794 return self.code2name[type].has_key(code)
795
796 def findcodename(self, type, code):
797 if not self.hascode(type, code):
798 return None, None, None
799 name = self.code2name[type][code]
800 if self.modulename and not self.star_imported:
801 qualname = '%s.%s'%(self.modulename, name)
802 else:
803 qualname = name
804 return name, qualname, self.modulename
805
806 def getall(self, type):
807 return self.code2name[type].items()
808
809 def addmodule(self, module, name, star_imported):
810 self.modulename = name
811 self.star_imported = star_imported
812 for code, name in module._propdeclarations.items():
813 self.addnamecode('property', name, code)
814 for code, name in module._classdeclarations.items():
815 self.addnamecode('class', name, code)
816 for code in module._enumdeclarations.keys():
817 self.addnamecode('enum', '_Enum_'+identify(code), code)
818 for code, name in module._compdeclarations.items():
819 self.addnamecode('comparison', name, code)
820
821 def prepareforexport(self, name=None):
822 if not self.modulename:
823 self.modulename = name
824 return self
825
Jack Jansen66544221997-08-08 14:49:02 +0000826class ObjectCompiler:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000827 def __init__(self, fp, basesuite=None, othernamemappers=None, interact=1):
Jack Jansen66544221997-08-08 14:49:02 +0000828 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000829 self.basesuite = basesuite
Jack Jansen59cdbce2003-03-21 16:28:09 +0000830 self.can_interact = interact
831 self.namemappers = [CodeNameMapper(self.can_interact)]
Jack Jansen12b2b762000-08-20 19:30:56 +0000832 if othernamemappers:
833 self.othernamemappers = othernamemappers[:]
834 else:
835 self.othernamemappers = []
836 if basesuite:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000837 basemapper = CodeNameMapper(self.can_interact)
Jack Jansen12b2b762000-08-20 19:30:56 +0000838 basemapper.addmodule(basesuite, '', 1)
839 self.namemappers.append(basemapper)
840
841 def getprecompinfo(self, modname):
842 list = []
843 for mapper in self.namemappers:
844 emapper = mapper.prepareforexport(modname)
845 if emapper:
846 list.append(emapper)
847 return list
Jack Jansen66544221997-08-08 14:49:02 +0000848
849 def findcodename(self, type, code):
850 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000851 # First try: check whether we already know about this code.
852 for mapper in self.namemappers:
853 if mapper.hascode(type, code):
854 return mapper.findcodename(type, code)
855 # Second try: maybe one of the other modules knows about it.
856 for mapper in self.othernamemappers:
857 if mapper.hascode(type, code):
858 self.othernamemappers.remove(mapper)
859 self.namemappers.append(mapper)
860 if self.fp:
861 self.fp.write("import %s\n"%mapper.modulename)
862 break
863 else:
864 # If all this has failed we ask the user for a guess on where it could
865 # be and retry.
866 if self.fp:
867 m = self.askdefinitionmodule(type, code)
868 else:
869 m = None
870 if not m: return None, None, None
Jack Jansen59cdbce2003-03-21 16:28:09 +0000871 mapper = CodeNameMapper(self.can_interact)
Jack Jansen12b2b762000-08-20 19:30:56 +0000872 mapper.addmodule(m, m.__name__, 0)
873 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000874
875 def askdefinitionmodule(self, type, code):
Jack Jansen59cdbce2003-03-21 16:28:09 +0000876 if not self.can_interact:
877 print "** No definition for %s '%s' found" % (type, code)
878 return None
Jack Jansendf976ca2003-01-26 20:35:47 +0000879 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
880 if not path: return
881 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000882 modname = os.path.splitext(file)[0]
883 if not path in sys.path:
884 sys.path.insert(0, path)
885 m = __import__(modname)
886 self.fp.write("import %s\n"%modname)
887 return m
888
889 def compileclass(self, cls):
890 [name, code, desc, properties, elements] = cls
891 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000892 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000893 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000894 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
895 if self.fp:
896 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000897 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000898 if self.fp:
899 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000900 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000901 self.fp.write('\twant = %s\n' % `code`)
902 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen00c34832003-03-28 23:37:05 +0000903 properties.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000904 for prop in properties:
905 self.compileproperty(prop)
Jack Jansen00c34832003-03-28 23:37:05 +0000906 elements.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000907 for elem in elements:
908 self.compileelement(elem)
909
910 def compileproperty(self, prop):
911 [name, code, what] = prop
912 if code == 'c@#!':
913 # Something silly with plurals. Skip it.
914 return
915 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000916 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000917 # plural forms and such
918 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000919 if pname == othername:
920 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000921 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000922 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000923 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000924 if self.fp:
925 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000926 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000927 self.fp.write("\twhich = %s\n" % `code`)
928 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000929 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000930
931 def compileelement(self, elem):
932 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000933 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000934 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000935
Jack Jansen66544221997-08-08 14:49:02 +0000936 def fillclasspropsandelems(self, cls):
937 [name, code, desc, properties, elements] = cls
938 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000939 if self.namemappers[0].hascode('class', code) and \
940 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000941 # This is an other name (plural or so) for something else. Skip.
942 return
943 plist = []
944 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000945 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000946 for prop in properties:
947 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000948 if pcode == "c@#^":
949 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000950 if pcode == 'c@#!':
951 continue
952 pname = identify(pname)
953 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000954
955 superclassnames = []
956 for superclass in superclasses:
957 superId, superDesc, dummy = superclass
958 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
Jack Jansen8307e022003-03-29 00:08:24 +0000959 # I don't think this is correct:
960 if superclassname == cname:
961 pass # superclassnames.append(fullyqualifiedname)
962 else:
963 superclassnames.append(superclassname)
Jack Jansen8b777672002-08-07 14:49:00 +0000964
965 if self.fp:
966 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
967
Jack Jansen66544221997-08-08 14:49:02 +0000968 for elem in elements:
969 [ecode, keyform] = elem
970 if ecode == 'c@#!':
971 continue
972 name, ename, module = self.findcodename('class', ecode)
973 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000974 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000975 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000976 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000977 elist.append((name, ename))
Jack Jansen00c34832003-03-28 23:37:05 +0000978
979 plist.sort()
980 elist.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +0000981
982 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000983 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000984 for n in plist:
985 self.fp.write("\t'%s' : %s,\n"%(n, n))
986 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000987 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000988 for n, fulln in elist:
989 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
990 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000991
992 def compilecomparison(self, comp):
993 [name, code, comment] = comp
994 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000995 self.namemappers[0].addnamecode('comparison', iname, code)
996 if self.fp:
997 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000998 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000999
1000 def compileenumeration(self, enum):
1001 [code, items] = enum
1002 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +00001003 if self.fp:
1004 self.fp.write("%s = {\n" % name)
1005 for item in items:
1006 self.compileenumerator(item)
1007 self.fp.write("}\n\n")
1008 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +00001009 return code
1010
1011 def compileenumerator(self, item):
1012 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +00001013 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +00001014
1015 def checkforenum(self, enum):
1016 """This enum code is used by an event. Make sure it's available"""
1017 name, fullname, module = self.findcodename('enum', enum)
1018 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +00001019 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +00001020 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +00001021 return
1022 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +00001023 if self.fp:
1024 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +00001025
1026 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +00001027 if not self.fp:
1028 return
Jack Jansen66544221997-08-08 14:49:02 +00001029 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001030
Jack Jansen66544221997-08-08 14:49:02 +00001031 self.fp.write("_classdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001032 classlist = self.namemappers[0].getall('class')
1033 classlist.sort()
1034 for k, v in classlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001035 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001036 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001037
Jack Jansen66544221997-08-08 14:49:02 +00001038 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001039 proplist = self.namemappers[0].getall('property')
1040 proplist.sort()
1041 for k, v in proplist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001042 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001043 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001044
Jack Jansen66544221997-08-08 14:49:02 +00001045 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001046 complist = self.namemappers[0].getall('comparison')
1047 complist.sort()
1048 for k, v in complist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001049 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001050 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001051
Jack Jansen66544221997-08-08 14:49:02 +00001052 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001053 enumlist = self.namemappers[0].getall('enum')
1054 enumlist.sort()
1055 for k, v in enumlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001056 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001057 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +00001058
1059def compiledata(data):
1060 [type, description, flags] = data
1061 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
1062
1063def is_null(data):
1064 return data[0] == 'null'
1065
1066def is_optional(data):
1067 return (data[2] & 0x8000)
1068
1069def is_enum(data):
1070 return (data[2] & 0x2000)
1071
1072def getdatadoc(data):
1073 [type, descr, flags] = data
1074 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +00001075 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +00001076 if type == '****':
1077 return 'anything'
1078 if type == 'obj ':
1079 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001080 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +00001081
1082dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
1083def compiledataflags(flags):
1084 bits = []
1085 for i in range(16):
1086 if flags & (1<<i):
1087 if i in dataflagdict.keys():
1088 bits.append(dataflagdict[i])
1089 else:
1090 bits.append(`i`)
1091 return '[%s]' % string.join(bits)
1092
Jack Jansen8b777672002-08-07 14:49:00 +00001093def ascii(str):
1094 """Return a string with all non-ascii characters hex-encoded"""
1095 if type(str) != type(''):
1096 return map(ascii, str)
1097 rv = ''
1098 for c in str:
1099 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
1100 rv = rv + c
1101 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001102 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +00001103 return rv
1104
Jack Jansen5ccd8261995-07-17 11:43:20 +00001105def identify(str):
1106 """Turn any string into an identifier:
1107 - replace space by _
1108 - replace other illegal chars by _xx_ (hex code)
1109 - prepend _ if the result is a python keyword
1110 """
Jack Jansen66544221997-08-08 14:49:02 +00001111 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +00001112 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +00001113 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +00001114 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001115 ok2 = ok + string.digits
1116 for c in str:
1117 if c in ok:
1118 rv = rv + c
1119 elif c == ' ':
1120 rv = rv + '_'
1121 else:
1122 rv = rv + '_%02.2x_'%ord(c)
1123 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +00001124 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +00001125 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001126 return rv
1127
1128# Call the main program
1129
1130if __name__ == '__main__':
1131 main()
1132 sys.exit(1)