blob: bc8619ec7e47d93e70df1490a79144730a337e9d [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 Jansen18c9b132003-04-01 13:32:17 +000039--dump Dump aete resource to stdout in stead of creating module (-d)
40--verbose Tell us what happens (-v)
Jack Jansen714caa02003-03-21 16:07:39 +000041""")
42 sys.exit(1)
43
Jack Jansen5ccd8261995-07-17 11:43:20 +000044def main():
Jack Jansen40926062002-03-30 23:43:04 +000045 if len(sys.argv) > 1:
Jack Jansen18c9b132003-04-01 13:32:17 +000046 SHORTOPTS = "rb:o:e:c:dv"
47 LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=", "dump", "verbose")
Jack Jansen714caa02003-03-21 16:07:39 +000048 try:
49 opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
50 except getopt.GetoptError:
51 usage()
52
53 process_func = processfile
54 basepkgname = 'StdSuites'
55 output = None
56 edit_modnames = []
Jack Jansen59cdbce2003-03-21 16:28:09 +000057 creatorsignature = None
Jack Jansen18c9b132003-04-01 13:32:17 +000058 dump = None
59 verbose = None
Jack Jansen714caa02003-03-21 16:07:39 +000060
61 for o, a in opts:
62 if o in ('-r', '--resource'):
63 process_func = processfile_fromresource
64 if o in ('-b', '--base'):
65 basepkgname = a
66 if o in ('-o', '--output'):
67 output = a
68 if o in ('-e', '--edit'):
69 split = a.split('=')
70 if len(split) != 2:
71 usage()
72 edit_modnames.append(split)
Jack Jansen59cdbce2003-03-21 16:28:09 +000073 if o in ('-c', '--creator'):
74 if len(a) != 4:
75 sys.stderr.write("creator must be 4-char string\n")
76 sys.exit(1)
77 creatorsignature = a
Jack Jansen18c9b132003-04-01 13:32:17 +000078 if o in ('-d', '--dump'):
79 dump = sys.stdout
80 if o in ('-v', '--verbose'):
81 verbose = sys.stderr
Jack Jansen59cdbce2003-03-21 16:28:09 +000082
Jack Jansen714caa02003-03-21 16:07:39 +000083
84 if output and len(args) > 1:
85 sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0])
86 sys.exit(1)
87
88 for filename in args:
89 process_func(filename, output=output, basepkgname=basepkgname,
Jack Jansen18c9b132003-04-01 13:32:17 +000090 edit_modnames=edit_modnames, creatorsignature=creatorsignature,
91 dump=dump, verbose=verbose)
Jack Jansen40926062002-03-30 23:43:04 +000092 else:
Jack Jansen1269be52003-03-29 22:54:00 +000093 main_interactive()
94
95def main_interactive(interact=0, basepkgname='StdSuites'):
96 if interact:
97 # Ask for save-filename for each module
98 edit_modnames = None
99 else:
100 # Use default filenames for each module
101 edit_modnames = []
102 appsfolder = Carbon.Folder.FSFindFolder(-32765, 'apps', 0)
103 filename = EasyDialogs.AskFileForOpen(
104 message='Select scriptable application',
105 dialogOptionFlags=0x1056, # allow selection of .app bundles
106 defaultLocation=appsfolder)
107 if not filename:
108 return
109 if not is_scriptable(filename):
110 if EasyDialogs.AskYesNoCancel(
111 "Warning: application does not seem scriptable",
112 yes="Continue", default=2, no="") <= 0:
113 return
114 try:
Jack Jansen18c9b132003-04-01 13:32:17 +0000115 processfile(filename, edit_modnames=edit_modnames, basepkgname=basepkgname,
116 verbose=sys.stderr)
Jack Jansen1269be52003-03-29 22:54:00 +0000117 except MacOS.Error, arg:
118 print "Error getting terminology:", arg
119 print "Retry, manually parsing resources"
120 processfile_fromresource(filename, edit_modnames=edit_modnames,
Jack Jansen18c9b132003-04-01 13:32:17 +0000121 basepkgname=basepkgname, verbose=sys.stderr)
Jack Jansen1269be52003-03-29 22:54:00 +0000122
123def is_scriptable(application):
124 """Return true if the application is scriptable"""
125 if os.path.isdir(application):
126 plistfile = os.path.join(application, 'Contents', 'Info.plist')
127 if not os.path.exists(plistfile):
128 return False
129 plist = plistlib.Plist.fromFile(plistfile)
130 return plist.get('NSAppleScriptEnabled', False)
131 # If it is a file test for an aete/aeut resource.
132 currf = CurResFile()
133 try:
134 refno = macresource.open_pathname(application)
135 except MacOS.Error:
136 return False
137 UseResFile(refno)
138 n_terminology = Count1Resources('aete') + Count1Resources('aeut') + \
139 Count1Resources('scsz') + Count1Resources('osiz')
140 CloseResFile(refno)
141 UseResFile(currf)
142 return n_terminology > 0
Jack Jansen5ccd8261995-07-17 11:43:20 +0000143
Jack Jansen59cdbce2003-03-21 16:28:09 +0000144def processfile_fromresource(fullname, output=None, basepkgname=None,
Jack Jansen18c9b132003-04-01 13:32:17 +0000145 edit_modnames=None, creatorsignature=None, dump=None, verbose=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000146 """Process all resources in a single file"""
Jack Jansen18c9b132003-04-01 13:32:17 +0000147 if not is_scriptable(fullname) and verbose:
148 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000149 cur = CurResFile()
Jack Jansen18c9b132003-04-01 13:32:17 +0000150 if verbose:
151 print >>verbose, "Processing", fullname
Jack Jansen40926062002-03-30 23:43:04 +0000152 rf = macresource.open_pathname(fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000153 try:
154 UseResFile(rf)
155 resources = []
156 for i in range(Count1Resources('aete')):
157 res = Get1IndResource('aete', 1+i)
158 resources.append(res)
159 for i in range(Count1Resources('aeut')):
160 res = Get1IndResource('aeut', 1+i)
161 resources.append(res)
Jack Jansen18c9b132003-04-01 13:32:17 +0000162 if verbose:
163 print >>verbose, "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +0000164 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000165 for res in resources:
Jack Jansen18c9b132003-04-01 13:32:17 +0000166 if verbose:
167 print >>verbose, "decoding", res.GetResInfo(), "..."
Jack Jansen5ccd8261995-07-17 11:43:20 +0000168 data = res.data
Jack Jansen18c9b132003-04-01 13:32:17 +0000169 aete = decode(data, verbose)
Jack Jansen60762cb2000-08-17 22:11:45 +0000170 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000171 finally:
172 if rf <> cur:
173 CloseResFile(rf)
174 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +0000175 # switch back (needed for dialogs in Python)
176 UseResFile(cur)
Jack Jansen18c9b132003-04-01 13:32:17 +0000177 if dump:
178 dumpaetelist(aetelist, dump)
Jack Jansen714caa02003-03-21 16:07:39 +0000179 compileaetelist(aetelist, fullname, output=output,
Jack Jansen59cdbce2003-03-21 16:28:09 +0000180 basepkgname=basepkgname, edit_modnames=edit_modnames,
Jack Jansen18c9b132003-04-01 13:32:17 +0000181 creatorsignature=creatorsignature, verbose=verbose)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000182
Jack Jansen59cdbce2003-03-21 16:28:09 +0000183def processfile(fullname, output=None, basepkgname=None,
Jack Jansen18c9b132003-04-01 13:32:17 +0000184 edit_modnames=None, creatorsignature=None, dump=None,
185 verbose=None):
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000186 """Ask an application for its terminology and process that"""
Jack Jansen18c9b132003-04-01 13:32:17 +0000187 if not is_scriptable(fullname) and verbose:
188 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
189 if verbose:
190 print >>verbose, "\nASKING FOR aete DICTIONARY IN", `fullname`
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000191 try:
192 aedescobj, launched = OSATerminology.GetAppTerminology(fullname)
193 except MacOS.Error, arg:
Jack Jansen03b9c912003-03-28 22:04:22 +0000194 if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound
Jack Jansen18c9b132003-04-01 13:32:17 +0000195 if verbose:
196 print >>verbose, "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually"
197 aedata, sig = getappterminology(fullname, verbose=verbose)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000198 if not creatorsignature:
199 creatorsignature = sig
200 else:
201 raise
202 else:
203 if launched:
Jack Jansen18c9b132003-04-01 13:32:17 +0000204 if verbose:
205 print >>verbose, "Launched", fullname
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000206 raw = aetools.unpack(aedescobj)
207 if not raw:
Jack Jansen18c9b132003-04-01 13:32:17 +0000208 if verbose:
209 print >>verbose, 'Unpack returned empty value:', raw
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000210 return
Jack Jansen18c9b132003-04-01 13:32:17 +0000211 if not raw[0].data:
212 if verbose:
213 print >>verbose, 'Unpack returned value without data:', raw
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000214 return
215 aedata = raw[0]
Jack Jansen18c9b132003-04-01 13:32:17 +0000216 aete = decode(aedata.data, verbose)
217 if dump:
218 dumpaetelist([aete], dump)
219 return
Jack Jansen59cdbce2003-03-21 16:28:09 +0000220 compileaete(aete, None, fullname, output=output, basepkgname=basepkgname,
Jack Jansen18c9b132003-04-01 13:32:17 +0000221 creatorsignature=creatorsignature, edit_modnames=edit_modnames,
222 verbose=verbose)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000223
Jack Jansen18c9b132003-04-01 13:32:17 +0000224def getappterminology(fullname, verbose=None):
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000225 """Get application terminology by sending an AppleEvent"""
226 # First check that we actually can send AppleEvents
227 if not MacOS.WMAvailable():
228 raise RuntimeError, "Cannot send AppleEvents, no access to window manager"
229 # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless
230 # you have created an event loop first.
231 import Carbon.Evt
232 Carbon.Evt.WaitNextEvent(0,0)
Jack Jansen03b9c912003-03-28 22:04:22 +0000233 if os.path.isdir(fullname):
234 # Now get the signature of the application, hoping it is a bundle
235 pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo')
236 if not os.path.exists(pkginfo):
237 raise RuntimeError, "No PkgInfo file found"
238 tp_cr = open(pkginfo, 'rb').read()
239 cr = tp_cr[4:8]
240 else:
241 # Assume it is a file
242 cr, tp = MacOS.GetCreatorAndType(fullname)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000243 # Let's talk to it and ask for its AETE
244 talker = aetools.TalkTo(cr)
Jack Jansen03b9c912003-03-28 22:04:22 +0000245 try:
246 talker._start()
247 except (MacOS.Error, aetools.Error), arg:
Jack Jansen18c9b132003-04-01 13:32:17 +0000248 if verbose:
249 print >>verbose, 'Warning: start() failed, continuing anyway:', arg
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000250 reply = talker.send("ascr", "gdte")
Jack Jansen03b9c912003-03-28 22:04:22 +0000251 #reply2 = talker.send("ascr", "gdut")
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000252 # Now pick the bits out of the return that we need.
253 return reply[1]['----'], cr
254
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000255
Jack Jansen59cdbce2003-03-21 16:28:09 +0000256def compileaetelist(aetelist, fullname, output=None, basepkgname=None,
Jack Jansen18c9b132003-04-01 13:32:17 +0000257 edit_modnames=None, creatorsignature=None, verbose=None):
Jack Jansen60762cb2000-08-17 22:11:45 +0000258 for aete, resinfo in aetelist:
Jack Jansen714caa02003-03-21 16:07:39 +0000259 compileaete(aete, resinfo, fullname, output=output,
Jack Jansen59cdbce2003-03-21 16:28:09 +0000260 basepkgname=basepkgname, edit_modnames=edit_modnames,
Jack Jansen18c9b132003-04-01 13:32:17 +0000261 creatorsignature=creatorsignature, verbose=verbose)
262
263def dumpaetelist(aetelist, output):
264 import pprint
265 pprint.pprint(aetelist, output)
266
267def decode(data, verbose=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000268 """Decode a resource into a python data structure"""
269 f = StringIO.StringIO(data)
270 aete = generic(getaete, f)
271 aete = simplify(aete)
272 processed = f.tell()
273 unprocessed = len(f.read())
274 total = f.tell()
Jack Jansen18c9b132003-04-01 13:32:17 +0000275 if unprocessed and verbose:
276 verbose.write("%d processed + %d unprocessed = %d total\n" %
Jack Jansen5ccd8261995-07-17 11:43:20 +0000277 (processed, unprocessed, total))
278 return aete
279
280def simplify(item):
281 """Recursively replace singleton tuples by their constituent item"""
282 if type(item) is types.ListType:
283 return map(simplify, item)
284 elif type(item) == types.TupleType and len(item) == 2:
285 return simplify(item[1])
286 else:
287 return item
288
289
290# Here follows the aete resource decoder.
291# It is presented bottom-up instead of top-down because there are direct
292# references to the lower-level part-decoders from the high-level part-decoders.
293
294def getbyte(f, *args):
295 c = f.read(1)
296 if not c:
297 raise EOFError, 'in getbyte' + str(args)
298 return ord(c)
299
300def getword(f, *args):
301 getalign(f)
302 s = f.read(2)
303 if len(s) < 2:
304 raise EOFError, 'in getword' + str(args)
305 return (ord(s[0])<<8) | ord(s[1])
306
307def getlong(f, *args):
308 getalign(f)
309 s = f.read(4)
310 if len(s) < 4:
311 raise EOFError, 'in getlong' + str(args)
312 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
313
314def getostype(f, *args):
315 getalign(f)
316 s = f.read(4)
317 if len(s) < 4:
318 raise EOFError, 'in getostype' + str(args)
319 return s
320
321def getpstr(f, *args):
322 c = f.read(1)
323 if len(c) < 1:
324 raise EOFError, 'in getpstr[1]' + str(args)
325 nbytes = ord(c)
326 if nbytes == 0: return ''
327 s = f.read(nbytes)
328 if len(s) < nbytes:
329 raise EOFError, 'in getpstr[2]' + str(args)
330 return s
331
332def getalign(f):
333 if f.tell() & 1:
334 c = f.read(1)
335 ##if c <> '\0':
336 ## print 'align:', `c`
337
338def getlist(f, description, getitem):
339 count = getword(f)
340 list = []
341 for i in range(count):
342 list.append(generic(getitem, f))
343 getalign(f)
344 return list
345
346def alt_generic(what, f, *args):
347 print "generic", `what`, args
348 res = vageneric(what, f, args)
349 print '->', `res`
350 return res
351
352def generic(what, f, *args):
353 if type(what) == types.FunctionType:
354 return apply(what, (f,) + args)
355 if type(what) == types.ListType:
356 record = []
357 for thing in what:
358 item = apply(generic, thing[:1] + (f,) + thing[1:])
359 record.append((thing[1], item))
360 return record
361 return "BAD GENERIC ARGS: %s" % `what`
362
363getdata = [
364 (getostype, "type"),
365 (getpstr, "description"),
366 (getword, "flags")
367 ]
368getargument = [
369 (getpstr, "name"),
370 (getostype, "keyword"),
371 (getdata, "what")
372 ]
373getevent = [
374 (getpstr, "name"),
375 (getpstr, "description"),
376 (getostype, "suite code"),
377 (getostype, "event code"),
378 (getdata, "returns"),
379 (getdata, "accepts"),
380 (getlist, "optional arguments", getargument)
381 ]
382getproperty = [
383 (getpstr, "name"),
384 (getostype, "code"),
385 (getdata, "what")
386 ]
387getelement = [
388 (getostype, "type"),
389 (getlist, "keyform", getostype)
390 ]
391getclass = [
392 (getpstr, "name"),
393 (getostype, "class code"),
394 (getpstr, "description"),
395 (getlist, "properties", getproperty),
396 (getlist, "elements", getelement)
397 ]
398getcomparison = [
399 (getpstr, "operator name"),
400 (getostype, "operator ID"),
401 (getpstr, "operator comment"),
402 ]
403getenumerator = [
404 (getpstr, "enumerator name"),
405 (getostype, "enumerator ID"),
406 (getpstr, "enumerator comment")
407 ]
408getenumeration = [
409 (getostype, "enumeration ID"),
410 (getlist, "enumerator", getenumerator)
411 ]
412getsuite = [
413 (getpstr, "suite name"),
414 (getpstr, "suite description"),
415 (getostype, "suite ID"),
416 (getword, "suite level"),
417 (getword, "suite version"),
418 (getlist, "events", getevent),
419 (getlist, "classes", getclass),
420 (getlist, "comparisons", getcomparison),
421 (getlist, "enumerations", getenumeration)
422 ]
423getaete = [
424 (getword, "major/minor version in BCD"),
425 (getword, "language code"),
426 (getword, "script code"),
427 (getlist, "suites", getsuite)
428 ]
429
Jack Jansen59cdbce2003-03-21 16:28:09 +0000430def compileaete(aete, resinfo, fname, output=None, basepkgname=None,
Jack Jansen18c9b132003-04-01 13:32:17 +0000431 edit_modnames=None, creatorsignature=None, verbose=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000432 """Generate code for a full aete resource. fname passed for doc purposes"""
433 [version, language, script, suites] = aete
434 major, minor = divmod(version, 256)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000435 if not creatorsignature:
436 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
Jack Jansen40926062002-03-30 23:43:04 +0000437 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000438 if language:
439 packagename = packagename+'_lang%d'%language
440 if script:
441 packagename = packagename+'_script%d'%script
442 if len(packagename) > 27:
443 packagename = packagename[:27]
Jack Jansen714caa02003-03-21 16:07:39 +0000444 if output:
445 # XXXX Put this in site-packages if it isn't a full pathname?
446 if not os.path.exists(output):
447 os.mkdir(output)
448 pathname = output
449 else:
450 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
451 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
Jack Jansen0e85e7a2003-03-26 23:14:44 +0000452 output = pathname
Jack Jansendf976ca2003-01-26 20:35:47 +0000453 if not pathname:
Jack Jansen60762cb2000-08-17 22:11:45 +0000454 return
Jack Jansen60762cb2000-08-17 22:11:45 +0000455 packagename = os.path.split(os.path.normpath(pathname))[1]
Jack Jansen714caa02003-03-21 16:07:39 +0000456 if not basepkgname:
457 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
458 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
Jack Jansendf976ca2003-01-26 20:35:47 +0000459 if basepkgname:
460 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
Jack Jansen714caa02003-03-21 16:07:39 +0000461 if dirname and not dirname in sys.path:
Jack Jansen60762cb2000-08-17 22:11:45 +0000462 sys.path.insert(0, dirname)
463 basepackage = __import__(basepkgname)
464 else:
465 basepackage = None
Jack Jansen60762cb2000-08-17 22:11:45 +0000466 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000467 allprecompinfo = []
468 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000469 for suite in suites:
Jack Jansen714caa02003-03-21 16:07:39 +0000470 code, suite, pathname, modname, precompinfo = precompilesuite(suite, basepackage,
Jack Jansen18c9b132003-04-01 13:32:17 +0000471 output=output, edit_modnames=edit_modnames, verbose=verbose)
Jack Jansen12b2b762000-08-20 19:30:56 +0000472 if not code:
473 continue
474 allprecompinfo = allprecompinfo + precompinfo
Jack Jansendf976ca2003-01-26 20:35:47 +0000475 suiteinfo = suite, pathname, modname
Jack Jansen12b2b762000-08-20 19:30:56 +0000476 suitelist.append((code, modname))
477 allsuites.append(suiteinfo)
478 for suiteinfo in allsuites:
Jack Jansen59cdbce2003-03-21 16:28:09 +0000479 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage,
Jack Jansen18c9b132003-04-01 13:32:17 +0000480 allprecompinfo, interact=(edit_modnames is None), verbose=verbose)
Jack Jansen714caa02003-03-21 16:07:39 +0000481 initfilename = os.path.join(output, '__init__.py')
Jack Jansendf976ca2003-01-26 20:35:47 +0000482 fp = open(initfilename, 'w')
483 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
Jack Jansen60762cb2000-08-17 22:11:45 +0000484 fp.write('"""\n')
Jack Jansen8307e022003-03-29 00:08:24 +0000485 fp.write("Package generated from %s\n"%ascii(fname))
Jack Jansen8b777672002-08-07 14:49:00 +0000486 if resinfo:
487 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000488 fp.write('"""\n')
489 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000490 fp.write('Error = aetools.Error\n')
Jack Jansen03b9c912003-03-28 22:04:22 +0000491 suitelist.sort()
Jack Jansen60762cb2000-08-17 22:11:45 +0000492 for code, modname in suitelist:
493 fp.write("import %s\n" % modname)
494 fp.write("\n\n_code_to_module = {\n")
495 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000496 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000497 fp.write("}\n\n")
498 fp.write("\n\n_code_to_fullname = {\n")
499 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000500 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000501 fp.write("}\n\n")
502 for code, modname in suitelist:
503 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000504
505 # Generate property dicts and element dicts for all types declared in this module
Jack Jansen7e0bc112003-03-21 12:04:19 +0000506 fp.write("\ndef getbaseclasses(v):\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000507 fp.write("\tif not getattr(v, '_propdict', None):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000508 fp.write("\t\tv._propdict = {}\n")
509 fp.write("\t\tv._elemdict = {}\n")
Jack Jansen7e0bc112003-03-21 12:04:19 +0000510 fp.write("\t\tfor superclassname in getattr(v, '_superclassnames', []):\n")
511 fp.write("\t\t\tsuperclass = eval(superclassname)\n")
512 fp.write("\t\t\tgetbaseclasses(superclass)\n")
513 fp.write("\t\t\tv._propdict.update(getattr(superclass, '_propdict', {}))\n")
514 fp.write("\t\t\tv._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000515 fp.write("\t\tv._propdict.update(getattr(v, '_privpropdict', {}))\n")
516 fp.write("\t\tv._elemdict.update(getattr(v, '_privelemdict', {}))\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000517 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000518 fp.write("import StdSuites\n")
Jack Jansen03b9c912003-03-28 22:04:22 +0000519 allprecompinfo.sort()
Jack Jansen8b777672002-08-07 14:49:00 +0000520 if allprecompinfo:
521 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
522 for codenamemapper in allprecompinfo:
523 for k, v in codenamemapper.getall('class'):
524 fp.write("getbaseclasses(%s)\n" % v)
525
526 # Generate a code-to-name mapper for all of the types (classes) declared in this module
527 if allprecompinfo:
528 fp.write("\n#\n# Indices of types declared in this module\n#\n")
529 fp.write("_classdeclarations = {\n")
530 for codenamemapper in allprecompinfo:
531 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000532 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000533 fp.write("}\n")
534
Jack Jansen60762cb2000-08-17 22:11:45 +0000535 if suitelist:
536 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
537 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000538 fp.write(",\n\t\t%s_Events"%modname)
539 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000540 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000541 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000542 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000543
Jack Jansen18c9b132003-04-01 13:32:17 +0000544def precompilesuite(suite, basepackage=None, edit_modnames=None, output=None,
545 verbose=None):
Jack Jansen12b2b762000-08-20 19:30:56 +0000546 """Parse a single suite without generating the output. This step is needed
547 so we can resolve recursive references by suites to enums/comps/etc declared
548 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000549 [name, desc, code, level, version, events, classes, comps, enums] = suite
550
551 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000552 if len(modname) > 28:
553 modname = modname[:27]
Jack Jansen714caa02003-03-21 16:07:39 +0000554 if edit_modnames is None:
555 pathname = EasyDialogs.AskFileForSave(message='Python output file',
556 savedFileName=modname+'.py')
557 else:
558 for old, new in edit_modnames:
559 if old == modname:
560 modname = new
561 if modname:
562 pathname = os.path.join(output, modname + '.py')
563 else:
564 pathname = None
Jack Jansendf976ca2003-01-26 20:35:47 +0000565 if not pathname:
Jack Jansen12b2b762000-08-20 19:30:56 +0000566 return None, None, None, None, None
567
Jack Jansen60762cb2000-08-17 22:11:45 +0000568 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000569
570 if basepackage and basepackage._code_to_module.has_key(code):
571 # We are an extension of a baseclass (usually an application extending
572 # Standard_Suite or so). Import everything from our base module
573 basemodule = basepackage._code_to_module[code]
574 else:
575 # We are not an extension.
576 basemodule = None
577
578 enumsneeded = {}
579 for event in events:
580 findenumsinevent(event, enumsneeded)
581
Jack Jansen18c9b132003-04-01 13:32:17 +0000582 objc = ObjectCompiler(None, basemodule, interact=(edit_modnames is None),
583 verbose=verbose)
Jack Jansen12b2b762000-08-20 19:30:56 +0000584 for cls in classes:
585 objc.compileclass(cls)
586 for cls in classes:
587 objc.fillclasspropsandelems(cls)
588 for comp in comps:
589 objc.compilecomparison(comp)
590 for enum in enums:
591 objc.compileenumeration(enum)
592
593 for enum in enumsneeded.keys():
594 objc.checkforenum(enum)
595
596 objc.dumpindex()
597
598 precompinfo = objc.getprecompinfo(modname)
599
Jack Jansendf976ca2003-01-26 20:35:47 +0000600 return code, suite, pathname, modname, precompinfo
Jack Jansen12b2b762000-08-20 19:30:56 +0000601
Jack Jansen59cdbce2003-03-21 16:28:09 +0000602def compilesuite((suite, pathname, modname), major, minor, language, script,
Jack Jansen18c9b132003-04-01 13:32:17 +0000603 fname, basepackage, precompinfo, interact=1, verbose=None):
Jack Jansen12b2b762000-08-20 19:30:56 +0000604 """Generate code for a single suite"""
605 [name, desc, code, level, version, events, classes, comps, enums] = suite
Jack Jansen03b9c912003-03-28 22:04:22 +0000606 # Sort various lists, so re-generated source is easier compared
Jack Jansen2dc16f22003-03-30 22:39:39 +0000607 def class_sorter(k1, k2):
608 """Sort classes by code, and make sure main class sorts before synonyms"""
609 # [name, code, desc, properties, elements] = cls
610 if k1[1] < k2[1]: return -1
611 if k1[1] > k2[1]: return 1
612 if not k2[3] or k2[3][0][1] == 'c@#!':
613 # This is a synonym, the other one is better
614 return -1
615 if not k1[3] or k1[3][0][1] == 'c@#!':
616 # This is a synonym, the other one is better
617 return 1
618 return 0
619
Jack Jansen03b9c912003-03-28 22:04:22 +0000620 events.sort()
Jack Jansen2dc16f22003-03-30 22:39:39 +0000621 classes.sort(class_sorter)
Jack Jansen03b9c912003-03-28 22:04:22 +0000622 comps.sort()
623 enums.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +0000624
Jack Jansendf976ca2003-01-26 20:35:47 +0000625 fp = open(pathname, 'w')
626 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000627
Jack Jansen8b777672002-08-07 14:49:00 +0000628 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000629 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000630 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000631 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
632 (major, minor, language, script))
633 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000634
635 fp.write('import aetools\n')
636 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000637 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000638 if basepackage and basepackage._code_to_module.has_key(code):
639 # We are an extension of a baseclass (usually an application extending
640 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000641 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000642 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000643 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
644 # This is needed by CodeWarrior and some others.
645 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
646 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000647 else:
648 # We are not an extension.
649 basemodule = None
650 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000651
652 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000653 if events:
654 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000655 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000656 else:
657 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000658
Jack Jansen18c9b132003-04-01 13:32:17 +0000659 objc = ObjectCompiler(fp, basemodule, precompinfo, interact=interact,
660 verbose=verbose)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000661 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000662 objc.compileclass(cls)
663 for cls in classes:
664 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000665 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000666 objc.compilecomparison(comp)
667 for enum in enums:
668 objc.compileenumeration(enum)
669
670 for enum in enumsneeded.keys():
671 objc.checkforenum(enum)
672
673 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000674
675 return code, modname
Jack Jansen2dc16f22003-03-30 22:39:39 +0000676
Jack Jansen60762cb2000-08-17 22:11:45 +0000677def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000678 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000679 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000680 if module:
681 modshortname = string.split(module.__name__, '.')[-1]
682 baseclassname = '%s_Events'%modshortname
683 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000684 else:
685 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000686
Jack Jansen66544221997-08-08 14:49:02 +0000687def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000688 """Generate code for a single event"""
689 [name, desc, code, subcode, returns, accepts, arguments] = event
690 funcname = identify(name)
691 #
692 # generate name->keyword map
693 #
694 if arguments:
695 fp.write("\t_argmap_%s = {\n"%funcname)
696 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000697 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000698 fp.write("\t}\n\n")
699
700 #
701 # Generate function header
702 #
703 has_arg = (not is_null(accepts))
704 opt_arg = (has_arg and is_optional(accepts))
705
Jack Jansen84264771995-10-03 14:35:58 +0000706 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000707 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000708 if not opt_arg:
709 fp.write("_object, ") # Include direct object, if it has one
710 else:
711 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000712 else:
Jack Jansen84264771995-10-03 14:35:58 +0000713 fp.write("_no_object=None, ") # For argument checking
714 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000715 #
716 # Generate doc string (important, since it may be the only
717 # available documentation, due to our name-remaping)
718 #
Jack Jansen21f67582002-08-07 15:44:53 +0000719 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000720 if has_arg:
721 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
722 elif opt_arg:
723 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
724 for arg in arguments:
725 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
726 getdatadoc(arg[2])))
727 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
728 if not is_null(returns):
729 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
730 fp.write('\t\t"""\n')
731 #
732 # Fiddle the args so everything ends up in 'arguments' dictionary
733 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000734 fp.write("\t\t_code = %s\n"% `code`)
735 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000736 #
737 # Do keyword name substitution
738 #
739 if arguments:
740 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
741 else:
742 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
743 #
744 # Stuff required arg (if there is one) into arguments
745 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000746 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000747 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000748 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000749 fp.write("\t\tif _object:\n")
750 fp.write("\t\t\t_arguments['----'] = _object\n")
751 else:
752 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000753 fp.write("\n")
754 #
Jack Jansen73215141995-10-09 23:09:23 +0000755 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000756 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000757 for a in arguments:
758 if is_enum(a[2]):
759 kname = a[1]
760 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000761 if ename <> '****':
762 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000763 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000764 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000765 fp.write("\n")
766 #
767 # Do the transaction
768 #
Jack Jansen84264771995-10-03 14:35:58 +0000769 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
770 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000771 #
772 # Error handling
773 #
Jack Jansen18983532002-04-23 21:03:21 +0000774 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000775 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000776 fp.write("\t\t# XXXX Optionally decode result\n")
777 #
778 # Decode result
779 #
Jack Jansen84264771995-10-03 14:35:58 +0000780 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000781 if is_enum(returns):
782 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000783 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000784 fp.write("\n")
785
786# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
787# print "# returns", compiledata(returns)
788# print "# accepts", compiledata(accepts)
789# for arg in arguments:
790# compileargument(arg)
791
792def compileargument(arg):
793 [name, keyword, what] = arg
794 print "# %s (%s)" % (name, `keyword`), compiledata(what)
795
Jack Jansen12b2b762000-08-20 19:30:56 +0000796def findenumsinevent(event, enumsneeded):
797 """Find all enums for a single event"""
798 [name, desc, code, subcode, returns, accepts, arguments] = event
799 for a in arguments:
800 if is_enum(a[2]):
801 ename = a[2][0]
802 if ename <> '****':
803 enumsneeded[ename] = 1
804
805#
806# This class stores the code<->name translations for a single module. It is used
807# to keep the information while we're compiling the module, but we also keep these objects
808# around so if one suite refers to, say, an enum in another suite we know where to
809# find it. Finally, if we really can't find a code, the user can add modules by
810# hand.
811#
812class CodeNameMapper:
813
Jack Jansen18c9b132003-04-01 13:32:17 +0000814 def __init__(self, interact=1, verbose=None):
Jack Jansen12b2b762000-08-20 19:30:56 +0000815 self.code2name = {
816 "property" : {},
817 "class" : {},
818 "enum" : {},
819 "comparison" : {},
820 }
821 self.name2code = {
822 "property" : {},
823 "class" : {},
824 "enum" : {},
825 "comparison" : {},
826 }
827 self.modulename = None
828 self.star_imported = 0
Jack Jansen59cdbce2003-03-21 16:28:09 +0000829 self.can_interact = interact
Jack Jansen18c9b132003-04-01 13:32:17 +0000830 self.verbose = verbose
Jack Jansen12b2b762000-08-20 19:30:56 +0000831
832 def addnamecode(self, type, name, code):
833 self.name2code[type][name] = code
834 if not self.code2name[type].has_key(code):
835 self.code2name[type][code] = name
836
837 def hasname(self, type, name):
838 return self.name2code[type].has_key(name)
839
840 def hascode(self, type, code):
841 return self.code2name[type].has_key(code)
842
843 def findcodename(self, type, code):
844 if not self.hascode(type, code):
845 return None, None, None
846 name = self.code2name[type][code]
847 if self.modulename and not self.star_imported:
848 qualname = '%s.%s'%(self.modulename, name)
849 else:
850 qualname = name
851 return name, qualname, self.modulename
852
853 def getall(self, type):
854 return self.code2name[type].items()
855
856 def addmodule(self, module, name, star_imported):
857 self.modulename = name
858 self.star_imported = star_imported
859 for code, name in module._propdeclarations.items():
860 self.addnamecode('property', name, code)
861 for code, name in module._classdeclarations.items():
862 self.addnamecode('class', name, code)
863 for code in module._enumdeclarations.keys():
864 self.addnamecode('enum', '_Enum_'+identify(code), code)
865 for code, name in module._compdeclarations.items():
866 self.addnamecode('comparison', name, code)
867
868 def prepareforexport(self, name=None):
869 if not self.modulename:
870 self.modulename = name
871 return self
872
Jack Jansen66544221997-08-08 14:49:02 +0000873class ObjectCompiler:
Jack Jansen18c9b132003-04-01 13:32:17 +0000874 def __init__(self, fp, basesuite=None, othernamemappers=None, interact=1,
875 verbose=None):
Jack Jansen66544221997-08-08 14:49:02 +0000876 self.fp = fp
Jack Jansen18c9b132003-04-01 13:32:17 +0000877 self.verbose = verbose
Jack Jansen60762cb2000-08-17 22:11:45 +0000878 self.basesuite = basesuite
Jack Jansen59cdbce2003-03-21 16:28:09 +0000879 self.can_interact = interact
Jack Jansen18c9b132003-04-01 13:32:17 +0000880 self.namemappers = [CodeNameMapper(self.can_interact, self.verbose)]
Jack Jansen12b2b762000-08-20 19:30:56 +0000881 if othernamemappers:
882 self.othernamemappers = othernamemappers[:]
883 else:
884 self.othernamemappers = []
885 if basesuite:
Jack Jansen18c9b132003-04-01 13:32:17 +0000886 basemapper = CodeNameMapper(self.can_interact, self.verbose)
Jack Jansen12b2b762000-08-20 19:30:56 +0000887 basemapper.addmodule(basesuite, '', 1)
888 self.namemappers.append(basemapper)
889
890 def getprecompinfo(self, modname):
891 list = []
892 for mapper in self.namemappers:
893 emapper = mapper.prepareforexport(modname)
894 if emapper:
895 list.append(emapper)
896 return list
Jack Jansen66544221997-08-08 14:49:02 +0000897
898 def findcodename(self, type, code):
899 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000900 # First try: check whether we already know about this code.
901 for mapper in self.namemappers:
902 if mapper.hascode(type, code):
903 return mapper.findcodename(type, code)
904 # Second try: maybe one of the other modules knows about it.
905 for mapper in self.othernamemappers:
906 if mapper.hascode(type, code):
907 self.othernamemappers.remove(mapper)
908 self.namemappers.append(mapper)
909 if self.fp:
910 self.fp.write("import %s\n"%mapper.modulename)
911 break
912 else:
913 # If all this has failed we ask the user for a guess on where it could
914 # be and retry.
915 if self.fp:
916 m = self.askdefinitionmodule(type, code)
917 else:
918 m = None
919 if not m: return None, None, None
Jack Jansen18c9b132003-04-01 13:32:17 +0000920 mapper = CodeNameMapper(self.can_interact, self.verbose)
Jack Jansen12b2b762000-08-20 19:30:56 +0000921 mapper.addmodule(m, m.__name__, 0)
922 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000923
924 def askdefinitionmodule(self, type, code):
Jack Jansen59cdbce2003-03-21 16:28:09 +0000925 if not self.can_interact:
Jack Jansen18c9b132003-04-01 13:32:17 +0000926 if self.verbose:
927 print >>self.verbose, "** No definition for %s '%s' found" % (type, code)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000928 return None
Jack Jansendf976ca2003-01-26 20:35:47 +0000929 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
930 if not path: return
931 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000932 modname = os.path.splitext(file)[0]
933 if not path in sys.path:
934 sys.path.insert(0, path)
935 m = __import__(modname)
936 self.fp.write("import %s\n"%modname)
937 return m
938
939 def compileclass(self, cls):
940 [name, code, desc, properties, elements] = cls
941 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000942 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000943 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000944 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
945 if self.fp:
946 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000947 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000948 if self.fp:
949 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000950 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000951 self.fp.write('\twant = %s\n' % `code`)
952 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen00c34832003-03-28 23:37:05 +0000953 properties.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000954 for prop in properties:
955 self.compileproperty(prop)
Jack Jansen00c34832003-03-28 23:37:05 +0000956 elements.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000957 for elem in elements:
958 self.compileelement(elem)
959
960 def compileproperty(self, prop):
961 [name, code, what] = prop
962 if code == 'c@#!':
963 # Something silly with plurals. Skip it.
964 return
965 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000966 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000967 # plural forms and such
968 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000969 if pname == othername:
970 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000971 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000972 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000973 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000974 if self.fp:
975 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000976 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000977 self.fp.write("\twhich = %s\n" % `code`)
978 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000979 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000980
981 def compileelement(self, elem):
982 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000983 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000984 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000985
Jack Jansen66544221997-08-08 14:49:02 +0000986 def fillclasspropsandelems(self, cls):
987 [name, code, desc, properties, elements] = cls
988 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000989 if self.namemappers[0].hascode('class', code) and \
990 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000991 # This is an other name (plural or so) for something else. Skip.
Jack Jansen2dc16f22003-03-30 22:39:39 +0000992 if self.fp and (elements or len(properties) > 1 or (len(properties) == 1 and
993 properties[0][1] != 'c@#!')):
Jack Jansen18c9b132003-04-01 13:32:17 +0000994 if self.verbose:
995 print >>self.verbose, '** Skip multiple %s of %s (code %s)' % (cname, self.namemappers[0].findcodename('class', code)[0], `code`)
Jack Jansen2dc16f22003-03-30 22:39:39 +0000996 raise RuntimeError, "About to skip non-empty class"
Jack Jansen66544221997-08-08 14:49:02 +0000997 return
998 plist = []
999 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +00001000 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +00001001 for prop in properties:
1002 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +00001003 if pcode == "c@#^":
1004 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +00001005 if pcode == 'c@#!':
1006 continue
1007 pname = identify(pname)
1008 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +00001009
1010 superclassnames = []
1011 for superclass in superclasses:
1012 superId, superDesc, dummy = superclass
1013 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
Jack Jansen8307e022003-03-29 00:08:24 +00001014 # I don't think this is correct:
1015 if superclassname == cname:
1016 pass # superclassnames.append(fullyqualifiedname)
1017 else:
1018 superclassnames.append(superclassname)
Jack Jansen8b777672002-08-07 14:49:00 +00001019
1020 if self.fp:
1021 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
1022
Jack Jansen66544221997-08-08 14:49:02 +00001023 for elem in elements:
1024 [ecode, keyform] = elem
1025 if ecode == 'c@#!':
1026 continue
1027 name, ename, module = self.findcodename('class', ecode)
1028 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +00001029 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001030 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +00001031 else:
Jack Jansen34d11f02000-03-07 23:40:13 +00001032 elist.append((name, ename))
Jack Jansen00c34832003-03-28 23:37:05 +00001033
1034 plist.sort()
1035 elist.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +00001036
1037 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +00001038 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +00001039 for n in plist:
1040 self.fp.write("\t'%s' : %s,\n"%(n, n))
1041 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +00001042 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +00001043 for n, fulln in elist:
1044 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
1045 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +00001046
1047 def compilecomparison(self, comp):
1048 [name, code, comment] = comp
1049 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +00001050 self.namemappers[0].addnamecode('comparison', iname, code)
1051 if self.fp:
1052 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +00001053 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +00001054
1055 def compileenumeration(self, enum):
1056 [code, items] = enum
1057 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +00001058 if self.fp:
1059 self.fp.write("%s = {\n" % name)
1060 for item in items:
1061 self.compileenumerator(item)
1062 self.fp.write("}\n\n")
1063 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +00001064 return code
1065
1066 def compileenumerator(self, item):
1067 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +00001068 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +00001069
1070 def checkforenum(self, enum):
1071 """This enum code is used by an event. Make sure it's available"""
1072 name, fullname, module = self.findcodename('enum', enum)
1073 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +00001074 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +00001075 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +00001076 return
1077 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +00001078 if self.fp:
1079 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +00001080
1081 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +00001082 if not self.fp:
1083 return
Jack Jansen66544221997-08-08 14:49:02 +00001084 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001085
Jack Jansen66544221997-08-08 14:49:02 +00001086 self.fp.write("_classdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001087 classlist = self.namemappers[0].getall('class')
1088 classlist.sort()
1089 for k, v in classlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001090 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001091 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001092
Jack Jansen66544221997-08-08 14:49:02 +00001093 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001094 proplist = self.namemappers[0].getall('property')
1095 proplist.sort()
1096 for k, v in proplist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001097 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001098 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001099
Jack Jansen66544221997-08-08 14:49:02 +00001100 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001101 complist = self.namemappers[0].getall('comparison')
1102 complist.sort()
1103 for k, v in complist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001104 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001105 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001106
Jack Jansen66544221997-08-08 14:49:02 +00001107 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001108 enumlist = self.namemappers[0].getall('enum')
1109 enumlist.sort()
1110 for k, v in enumlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001111 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001112 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +00001113
1114def compiledata(data):
1115 [type, description, flags] = data
1116 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
1117
1118def is_null(data):
1119 return data[0] == 'null'
1120
1121def is_optional(data):
1122 return (data[2] & 0x8000)
1123
1124def is_enum(data):
1125 return (data[2] & 0x2000)
1126
1127def getdatadoc(data):
1128 [type, descr, flags] = data
1129 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +00001130 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +00001131 if type == '****':
1132 return 'anything'
1133 if type == 'obj ':
1134 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001135 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +00001136
1137dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
1138def compiledataflags(flags):
1139 bits = []
1140 for i in range(16):
1141 if flags & (1<<i):
1142 if i in dataflagdict.keys():
1143 bits.append(dataflagdict[i])
1144 else:
1145 bits.append(`i`)
1146 return '[%s]' % string.join(bits)
1147
Jack Jansen8b777672002-08-07 14:49:00 +00001148def ascii(str):
1149 """Return a string with all non-ascii characters hex-encoded"""
1150 if type(str) != type(''):
1151 return map(ascii, str)
1152 rv = ''
1153 for c in str:
1154 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
1155 rv = rv + c
1156 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001157 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +00001158 return rv
1159
Jack Jansen5ccd8261995-07-17 11:43:20 +00001160def identify(str):
1161 """Turn any string into an identifier:
1162 - replace space by _
1163 - replace other illegal chars by _xx_ (hex code)
1164 - prepend _ if the result is a python keyword
1165 """
Jack Jansen66544221997-08-08 14:49:02 +00001166 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +00001167 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +00001168 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +00001169 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001170 ok2 = ok + string.digits
1171 for c in str:
1172 if c in ok:
1173 rv = rv + c
1174 elif c == ' ':
1175 rv = rv + '_'
1176 else:
1177 rv = rv + '_%02.2x_'%ord(c)
1178 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +00001179 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +00001180 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001181 return rv
1182
1183# Call the main program
1184
1185if __name__ == '__main__':
1186 main()
1187 sys.exit(1)