blob: 0fcd1c410e68d51fc9936fa68a64032c5e908ac4 [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 Jansen3279cb02003-04-01 14:25:49 +0000470 compiler = SuiteCompiler(suite, basepackage, output, edit_modnames, verbose)
471 code, modname, precompinfo = compiler.precompilesuite()
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))
Jack Jansen3279cb02003-04-01 14:25:49 +0000477 allsuites.append(compiler)
478 for compiler in allsuites:
479 compiler.compilesuite(major, minor, language, script, fname, allprecompinfo)
Jack Jansen714caa02003-03-21 16:07:39 +0000480 initfilename = os.path.join(output, '__init__.py')
Jack Jansendf976ca2003-01-26 20:35:47 +0000481 fp = open(initfilename, 'w')
482 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
Jack Jansen60762cb2000-08-17 22:11:45 +0000483 fp.write('"""\n')
Jack Jansen8307e022003-03-29 00:08:24 +0000484 fp.write("Package generated from %s\n"%ascii(fname))
Jack Jansen8b777672002-08-07 14:49:00 +0000485 if resinfo:
486 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000487 fp.write('"""\n')
488 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000489 fp.write('Error = aetools.Error\n')
Jack Jansen03b9c912003-03-28 22:04:22 +0000490 suitelist.sort()
Jack Jansen60762cb2000-08-17 22:11:45 +0000491 for code, modname in suitelist:
492 fp.write("import %s\n" % modname)
493 fp.write("\n\n_code_to_module = {\n")
494 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000495 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000496 fp.write("}\n\n")
497 fp.write("\n\n_code_to_fullname = {\n")
498 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000499 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000500 fp.write("}\n\n")
501 for code, modname in suitelist:
502 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000503
504 # Generate property dicts and element dicts for all types declared in this module
Jack Jansen7e0bc112003-03-21 12:04:19 +0000505 fp.write("\ndef getbaseclasses(v):\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000506 fp.write("\tif not getattr(v, '_propdict', None):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000507 fp.write("\t\tv._propdict = {}\n")
508 fp.write("\t\tv._elemdict = {}\n")
Jack Jansen7e0bc112003-03-21 12:04:19 +0000509 fp.write("\t\tfor superclassname in getattr(v, '_superclassnames', []):\n")
510 fp.write("\t\t\tsuperclass = eval(superclassname)\n")
511 fp.write("\t\t\tgetbaseclasses(superclass)\n")
512 fp.write("\t\t\tv._propdict.update(getattr(superclass, '_propdict', {}))\n")
513 fp.write("\t\t\tv._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
Jack Jansen7cb016d2003-03-23 22:05:53 +0000514 fp.write("\t\tv._propdict.update(getattr(v, '_privpropdict', {}))\n")
515 fp.write("\t\tv._elemdict.update(getattr(v, '_privelemdict', {}))\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000516 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000517 fp.write("import StdSuites\n")
Jack Jansen03b9c912003-03-28 22:04:22 +0000518 allprecompinfo.sort()
Jack Jansen8b777672002-08-07 14:49:00 +0000519 if allprecompinfo:
520 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
521 for codenamemapper in allprecompinfo:
522 for k, v in codenamemapper.getall('class'):
523 fp.write("getbaseclasses(%s)\n" % v)
524
525 # Generate a code-to-name mapper for all of the types (classes) declared in this module
526 if allprecompinfo:
527 fp.write("\n#\n# Indices of types declared in this module\n#\n")
528 fp.write("_classdeclarations = {\n")
529 for codenamemapper in allprecompinfo:
530 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000531 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000532 fp.write("}\n")
533
Jack Jansen60762cb2000-08-17 22:11:45 +0000534 if suitelist:
535 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
536 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000537 fp.write(",\n\t\t%s_Events"%modname)
538 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000539 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000540 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000541 fp.close()
Jack Jansen3279cb02003-04-01 14:25:49 +0000542
543class SuiteCompiler:
544 def __init__(self, suite, basepackage, output, edit_modnames, verbose):
545 self.suite = suite
546 self.basepackage = basepackage
547 self.edit_modnames = edit_modnames
548 self.output = output
549 self.verbose = verbose
550
551 # Set by precompilesuite
552 self.pathname = None
553 self.modname = None
554
555 # Set by compilesuite
556 self.fp = None
557 self.basemodule = None
558 self.enumsneeded = {}
559
560 def precompilesuite(self):
561 """Parse a single suite without generating the output. This step is needed
562 so we can resolve recursive references by suites to enums/comps/etc declared
563 in other suites"""
564 [name, desc, code, level, version, events, classes, comps, enums] = self.suite
565
566 modname = identify(name)
567 if len(modname) > 28:
568 modname = modname[:27]
569 if self.edit_modnames is None:
570 self.pathname = EasyDialogs.AskFileForSave(message='Python output file',
571 savedFileName=modname+'.py')
Jack Jansen714caa02003-03-21 16:07:39 +0000572 else:
Jack Jansen3279cb02003-04-01 14:25:49 +0000573 for old, new in self.edit_modnames:
574 if old == modname:
575 modname = new
576 if modname:
577 self.pathname = os.path.join(self.output, modname + '.py')
578 else:
579 self.pathname = None
580 if not self.pathname:
581 return None, None, None
Jack Jansen12b2b762000-08-20 19:30:56 +0000582
Jack Jansen3279cb02003-04-01 14:25:49 +0000583 self.modname = os.path.splitext(os.path.split(self.pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000584
Jack Jansen3279cb02003-04-01 14:25:49 +0000585 if self.basepackage and self.basepackage._code_to_module.has_key(code):
586 # We are an extension of a baseclass (usually an application extending
587 # Standard_Suite or so). Import everything from our base module
588 basemodule = self.basepackage._code_to_module[code]
589 else:
590 # We are not an extension.
591 basemodule = None
Jack Jansen12b2b762000-08-20 19:30:56 +0000592
Jack Jansen3279cb02003-04-01 14:25:49 +0000593 self.enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000594 for event in events:
Jack Jansen3279cb02003-04-01 14:25:49 +0000595 self.findenumsinevent(event)
Jack Jansen66544221997-08-08 14:49:02 +0000596
Jack Jansen3279cb02003-04-01 14:25:49 +0000597 objc = ObjectCompiler(None, basemodule, interact=(self.edit_modnames is None),
598 verbose=self.verbose)
599 for cls in classes:
600 objc.compileclass(cls)
601 for cls in classes:
602 objc.fillclasspropsandelems(cls)
603 for comp in comps:
604 objc.compilecomparison(comp)
605 for enum in enums:
606 objc.compileenumeration(enum)
Jack Jansen66544221997-08-08 14:49:02 +0000607
Jack Jansen3279cb02003-04-01 14:25:49 +0000608 for enum in self.enumsneeded.keys():
609 objc.checkforenum(enum)
610
611 objc.dumpindex()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000612
Jack Jansen3279cb02003-04-01 14:25:49 +0000613 precompinfo = objc.getprecompinfo(self.modname)
614
615 return code, self.modname, precompinfo
Jack Jansen5ccd8261995-07-17 11:43:20 +0000616
Jack Jansen3279cb02003-04-01 14:25:49 +0000617 def compilesuite(self, major, minor, language, script, fname, precompinfo):
618 """Generate code for a single suite"""
619 [name, desc, code, level, version, events, classes, comps, enums] = self.suite
620 # Sort various lists, so re-generated source is easier compared
621 def class_sorter(k1, k2):
622 """Sort classes by code, and make sure main class sorts before synonyms"""
623 # [name, code, desc, properties, elements] = cls
624 if k1[1] < k2[1]: return -1
625 if k1[1] > k2[1]: return 1
626 if not k2[3] or k2[3][0][1] == 'c@#!':
627 # This is a synonym, the other one is better
628 return -1
629 if not k1[3] or k1[3][0][1] == 'c@#!':
630 # This is a synonym, the other one is better
631 return 1
632 return 0
633
634 events.sort()
635 classes.sort(class_sorter)
636 comps.sort()
637 enums.sort()
638
639 self.fp = fp = open(self.pathname, 'w')
640 MacOS.SetCreatorAndType(self.pathname, 'Pyth', 'TEXT')
641
642 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
643 fp.write("Level %d, version %d\n\n" % (level, version))
644 fp.write("Generated from %s\n"%ascii(fname))
645 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
646 (major, minor, language, script))
647 fp.write('"""\n\n')
648
649 fp.write('import aetools\n')
650 fp.write('import MacOS\n\n')
651 fp.write("_code = %s\n\n"% `code`)
652 if self.basepackage and self.basepackage._code_to_module.has_key(code):
653 # We are an extension of a baseclass (usually an application extending
654 # Standard_Suite or so). Import everything from our base module
655 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code][0])
656 basemodule = self.basepackage._code_to_module[code]
657 elif self.basepackage and self.basepackage._code_to_module.has_key(code.lower()):
658 # This is needed by CodeWarrior and some others.
659 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code.lower()][0])
660 basemodule = self.basepackage._code_to_module[code.lower()]
Jack Jansen84264771995-10-03 14:35:58 +0000661 else:
Jack Jansen3279cb02003-04-01 14:25:49 +0000662 # We are not an extension.
663 basemodule = None
664 self.basemodule = basemodule
665 self.compileclassheader()
Jack Jansen5ccd8261995-07-17 11:43:20 +0000666
Jack Jansen3279cb02003-04-01 14:25:49 +0000667 self.enumsneeded = {}
668 if events:
669 for event in events:
670 self.compileevent(event)
671 else:
672 fp.write("\tpass\n\n")
673
674 objc = ObjectCompiler(fp, basemodule, precompinfo, interact=(self.edit_modnames is None),
675 verbose=self.verbose)
676 for cls in classes:
677 objc.compileclass(cls)
678 for cls in classes:
679 objc.fillclasspropsandelems(cls)
680 for comp in comps:
681 objc.compilecomparison(comp)
682 for enum in enums:
683 objc.compileenumeration(enum)
684
685 for enum in self.enumsneeded.keys():
686 objc.checkforenum(enum)
687
688 objc.dumpindex()
689
690 def compileclassheader(self):
691 """Generate class boilerplate"""
692 classname = '%s_Events'%self.modname
693 if self.basemodule:
694 modshortname = string.split(self.basemodule.__name__, '.')[-1]
695 baseclassname = '%s_Events'%modshortname
696 self.fp.write("class %s(%s):\n\n"%(classname, baseclassname))
697 else:
698 self.fp.write("class %s:\n\n"%classname)
699
700 def compileevent(self, event):
701 """Generate code for a single event"""
702 [name, desc, code, subcode, returns, accepts, arguments] = event
703 fp = self.fp
704 funcname = identify(name)
705 #
706 # generate name->keyword map
707 #
708 if arguments:
709 fp.write("\t_argmap_%s = {\n"%funcname)
710 for a in arguments:
711 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
712 fp.write("\t}\n\n")
713
714 #
715 # Generate function header
716 #
717 has_arg = (not is_null(accepts))
718 opt_arg = (has_arg and is_optional(accepts))
719
720 fp.write("\tdef %s(self, "%funcname)
721 if has_arg:
722 if not opt_arg:
723 fp.write("_object, ") # Include direct object, if it has one
724 else:
725 fp.write("_object=None, ") # Also include if it is optional
726 else:
727 fp.write("_no_object=None, ") # For argument checking
728 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
729 #
730 # Generate doc string (important, since it may be the only
731 # available documentation, due to our name-remaping)
732 #
733 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
734 if has_arg:
735 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
736 elif opt_arg:
737 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
738 for arg in arguments:
739 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
740 getdatadoc(arg[2])))
741 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
742 if not is_null(returns):
743 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
744 fp.write('\t\t"""\n')
745 #
746 # Fiddle the args so everything ends up in 'arguments' dictionary
747 #
748 fp.write("\t\t_code = %s\n"% `code`)
749 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
750 #
751 # Do keyword name substitution
752 #
753 if arguments:
754 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
755 else:
756 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
757 #
758 # Stuff required arg (if there is one) into arguments
759 #
760 if has_arg:
761 fp.write("\t\t_arguments['----'] = _object\n")
762 elif opt_arg:
763 fp.write("\t\tif _object:\n")
764 fp.write("\t\t\t_arguments['----'] = _object\n")
765 else:
766 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
767 fp.write("\n")
768 #
769 # Do enum-name substitution
770 #
771 for a in arguments:
772 if is_enum(a[2]):
773 kname = a[1]
774 ename = a[2][0]
775 if ename <> '****':
776 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
777 (`kname`, identify(ename)))
778 self.enumsneeded[ename] = 1
779 fp.write("\n")
780 #
781 # Do the transaction
782 #
783 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
784 fp.write("\t\t\t\t_arguments, _attributes)\n")
785 #
786 # Error handling
787 #
788 fp.write("\t\tif _arguments.get('errn', 0):\n")
789 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
790 fp.write("\t\t# XXXX Optionally decode result\n")
791 #
792 # Decode result
793 #
794 fp.write("\t\tif _arguments.has_key('----'):\n")
795 if is_enum(returns):
796 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
797 fp.write("\t\t\treturn _arguments['----']\n")
798 fp.write("\n")
799
800 def findenumsinevent(self, event):
801 """Find all enums for a single event"""
802 [name, desc, code, subcode, returns, accepts, arguments] = event
803 for a in arguments:
804 if is_enum(a[2]):
805 ename = a[2][0]
806 if ename <> '****':
807 self.enumsneeded[ename] = 1
808
Jack Jansen12b2b762000-08-20 19:30:56 +0000809#
810# This class stores the code<->name translations for a single module. It is used
811# to keep the information while we're compiling the module, but we also keep these objects
812# around so if one suite refers to, say, an enum in another suite we know where to
813# find it. Finally, if we really can't find a code, the user can add modules by
814# hand.
815#
816class CodeNameMapper:
817
Jack Jansen18c9b132003-04-01 13:32:17 +0000818 def __init__(self, interact=1, verbose=None):
Jack Jansen12b2b762000-08-20 19:30:56 +0000819 self.code2name = {
820 "property" : {},
821 "class" : {},
822 "enum" : {},
823 "comparison" : {},
824 }
825 self.name2code = {
826 "property" : {},
827 "class" : {},
828 "enum" : {},
829 "comparison" : {},
830 }
831 self.modulename = None
832 self.star_imported = 0
Jack Jansen59cdbce2003-03-21 16:28:09 +0000833 self.can_interact = interact
Jack Jansen18c9b132003-04-01 13:32:17 +0000834 self.verbose = verbose
Jack Jansen12b2b762000-08-20 19:30:56 +0000835
836 def addnamecode(self, type, name, code):
837 self.name2code[type][name] = code
838 if not self.code2name[type].has_key(code):
839 self.code2name[type][code] = name
840
841 def hasname(self, type, name):
842 return self.name2code[type].has_key(name)
843
844 def hascode(self, type, code):
845 return self.code2name[type].has_key(code)
846
847 def findcodename(self, type, code):
848 if not self.hascode(type, code):
849 return None, None, None
850 name = self.code2name[type][code]
851 if self.modulename and not self.star_imported:
852 qualname = '%s.%s'%(self.modulename, name)
853 else:
854 qualname = name
855 return name, qualname, self.modulename
856
857 def getall(self, type):
858 return self.code2name[type].items()
859
860 def addmodule(self, module, name, star_imported):
861 self.modulename = name
862 self.star_imported = star_imported
863 for code, name in module._propdeclarations.items():
864 self.addnamecode('property', name, code)
865 for code, name in module._classdeclarations.items():
866 self.addnamecode('class', name, code)
867 for code in module._enumdeclarations.keys():
868 self.addnamecode('enum', '_Enum_'+identify(code), code)
869 for code, name in module._compdeclarations.items():
870 self.addnamecode('comparison', name, code)
871
872 def prepareforexport(self, name=None):
873 if not self.modulename:
874 self.modulename = name
875 return self
876
Jack Jansen66544221997-08-08 14:49:02 +0000877class ObjectCompiler:
Jack Jansen18c9b132003-04-01 13:32:17 +0000878 def __init__(self, fp, basesuite=None, othernamemappers=None, interact=1,
879 verbose=None):
Jack Jansen66544221997-08-08 14:49:02 +0000880 self.fp = fp
Jack Jansen18c9b132003-04-01 13:32:17 +0000881 self.verbose = verbose
Jack Jansen60762cb2000-08-17 22:11:45 +0000882 self.basesuite = basesuite
Jack Jansen59cdbce2003-03-21 16:28:09 +0000883 self.can_interact = interact
Jack Jansen18c9b132003-04-01 13:32:17 +0000884 self.namemappers = [CodeNameMapper(self.can_interact, self.verbose)]
Jack Jansen12b2b762000-08-20 19:30:56 +0000885 if othernamemappers:
886 self.othernamemappers = othernamemappers[:]
887 else:
888 self.othernamemappers = []
889 if basesuite:
Jack Jansen18c9b132003-04-01 13:32:17 +0000890 basemapper = CodeNameMapper(self.can_interact, self.verbose)
Jack Jansen12b2b762000-08-20 19:30:56 +0000891 basemapper.addmodule(basesuite, '', 1)
892 self.namemappers.append(basemapper)
893
894 def getprecompinfo(self, modname):
895 list = []
896 for mapper in self.namemappers:
897 emapper = mapper.prepareforexport(modname)
898 if emapper:
899 list.append(emapper)
900 return list
Jack Jansen66544221997-08-08 14:49:02 +0000901
902 def findcodename(self, type, code):
903 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000904 # First try: check whether we already know about this code.
905 for mapper in self.namemappers:
906 if mapper.hascode(type, code):
907 return mapper.findcodename(type, code)
908 # Second try: maybe one of the other modules knows about it.
909 for mapper in self.othernamemappers:
910 if mapper.hascode(type, code):
911 self.othernamemappers.remove(mapper)
912 self.namemappers.append(mapper)
913 if self.fp:
914 self.fp.write("import %s\n"%mapper.modulename)
915 break
916 else:
917 # If all this has failed we ask the user for a guess on where it could
918 # be and retry.
919 if self.fp:
920 m = self.askdefinitionmodule(type, code)
921 else:
922 m = None
923 if not m: return None, None, None
Jack Jansen18c9b132003-04-01 13:32:17 +0000924 mapper = CodeNameMapper(self.can_interact, self.verbose)
Jack Jansen12b2b762000-08-20 19:30:56 +0000925 mapper.addmodule(m, m.__name__, 0)
926 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000927
928 def askdefinitionmodule(self, type, code):
Jack Jansen59cdbce2003-03-21 16:28:09 +0000929 if not self.can_interact:
Jack Jansen18c9b132003-04-01 13:32:17 +0000930 if self.verbose:
931 print >>self.verbose, "** No definition for %s '%s' found" % (type, code)
Jack Jansen59cdbce2003-03-21 16:28:09 +0000932 return None
Jack Jansendf976ca2003-01-26 20:35:47 +0000933 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
934 if not path: return
935 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000936 modname = os.path.splitext(file)[0]
937 if not path in sys.path:
938 sys.path.insert(0, path)
939 m = __import__(modname)
940 self.fp.write("import %s\n"%modname)
941 return m
942
943 def compileclass(self, cls):
944 [name, code, desc, properties, elements] = cls
945 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000946 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000947 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000948 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
949 if self.fp:
950 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000951 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000952 if self.fp:
953 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000954 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000955 self.fp.write('\twant = %s\n' % `code`)
956 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen00c34832003-03-28 23:37:05 +0000957 properties.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000958 for prop in properties:
959 self.compileproperty(prop)
Jack Jansen00c34832003-03-28 23:37:05 +0000960 elements.sort()
Jack Jansen66544221997-08-08 14:49:02 +0000961 for elem in elements:
962 self.compileelement(elem)
963
964 def compileproperty(self, prop):
965 [name, code, what] = prop
966 if code == 'c@#!':
967 # Something silly with plurals. Skip it.
968 return
969 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000970 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000971 # plural forms and such
972 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000973 if pname == othername:
974 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000975 if self.fp:
Jack Jansenbc956052003-04-01 22:01:58 +0000976 self.fp.write("\n_Prop_%s = _Prop_%s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000977 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000978 if self.fp:
Jack Jansenbc956052003-04-01 22:01:58 +0000979 self.fp.write("class _Prop_%s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000980 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000981 self.fp.write("\twhich = %s\n" % `code`)
982 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000983 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000984
985 def compileelement(self, elem):
986 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000987 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000988 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000989
Jack Jansen66544221997-08-08 14:49:02 +0000990 def fillclasspropsandelems(self, cls):
991 [name, code, desc, properties, elements] = cls
992 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000993 if self.namemappers[0].hascode('class', code) and \
994 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000995 # This is an other name (plural or so) for something else. Skip.
Jack Jansen2dc16f22003-03-30 22:39:39 +0000996 if self.fp and (elements or len(properties) > 1 or (len(properties) == 1 and
997 properties[0][1] != 'c@#!')):
Jack Jansen18c9b132003-04-01 13:32:17 +0000998 if self.verbose:
999 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 +00001000 raise RuntimeError, "About to skip non-empty class"
Jack Jansen66544221997-08-08 14:49:02 +00001001 return
1002 plist = []
1003 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +00001004 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +00001005 for prop in properties:
1006 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +00001007 if pcode == "c@#^":
1008 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +00001009 if pcode == 'c@#!':
1010 continue
1011 pname = identify(pname)
1012 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +00001013
1014 superclassnames = []
1015 for superclass in superclasses:
1016 superId, superDesc, dummy = superclass
1017 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
Jack Jansen8307e022003-03-29 00:08:24 +00001018 # I don't think this is correct:
1019 if superclassname == cname:
1020 pass # superclassnames.append(fullyqualifiedname)
1021 else:
1022 superclassnames.append(superclassname)
Jack Jansen8b777672002-08-07 14:49:00 +00001023
1024 if self.fp:
1025 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
1026
Jack Jansen66544221997-08-08 14:49:02 +00001027 for elem in elements:
1028 [ecode, keyform] = elem
1029 if ecode == 'c@#!':
1030 continue
1031 name, ename, module = self.findcodename('class', ecode)
1032 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +00001033 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001034 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +00001035 else:
Jack Jansen34d11f02000-03-07 23:40:13 +00001036 elist.append((name, ename))
Jack Jansen00c34832003-03-28 23:37:05 +00001037
1038 plist.sort()
1039 elist.sort()
Jack Jansen12b2b762000-08-20 19:30:56 +00001040
1041 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +00001042 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +00001043 for n in plist:
Jack Jansenbc956052003-04-01 22:01:58 +00001044 self.fp.write("\t'%s' : _Prop_%s,\n"%(n, n))
Jack Jansen12b2b762000-08-20 19:30:56 +00001045 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +00001046 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +00001047 for n, fulln in elist:
1048 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
1049 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +00001050
1051 def compilecomparison(self, comp):
1052 [name, code, comment] = comp
1053 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +00001054 self.namemappers[0].addnamecode('comparison', iname, code)
1055 if self.fp:
1056 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +00001057 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +00001058
1059 def compileenumeration(self, enum):
1060 [code, items] = enum
1061 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +00001062 if self.fp:
1063 self.fp.write("%s = {\n" % name)
1064 for item in items:
1065 self.compileenumerator(item)
1066 self.fp.write("}\n\n")
1067 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +00001068 return code
1069
1070 def compileenumerator(self, item):
1071 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +00001072 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +00001073
1074 def checkforenum(self, enum):
1075 """This enum code is used by an event. Make sure it's available"""
1076 name, fullname, module = self.findcodename('enum', enum)
1077 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +00001078 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +00001079 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +00001080 return
1081 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +00001082 if self.fp:
1083 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +00001084
1085 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +00001086 if not self.fp:
1087 return
Jack Jansen66544221997-08-08 14:49:02 +00001088 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001089
Jack Jansen66544221997-08-08 14:49:02 +00001090 self.fp.write("_classdeclarations = {\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001091 classlist = self.namemappers[0].getall('class')
1092 classlist.sort()
1093 for k, v in classlist:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001094 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +00001095 self.fp.write("}\n")
Jack Jansen00c34832003-03-28 23:37:05 +00001096
Jack Jansenbc956052003-04-01 22:01:58 +00001097## self.fp.write("\n_propdeclarations = {\n")
1098## proplist = self.namemappers[0].getall('property')
1099## proplist.sort()
1100## for k, v in proplist:
1101## self.fp.write("\t%s : _Prop_%s,\n" % (`k`, v))
1102## self.fp.write("}\n")
1103##
1104## self.fp.write("\n_compdeclarations = {\n")
1105## complist = self.namemappers[0].getall('comparison')
1106## complist.sort()
1107## for k, v in complist:
1108## self.fp.write("\t%s : %s,\n" % (`k`, v))
1109## self.fp.write("}\n")
1110##
1111## self.fp.write("\n_enumdeclarations = {\n")
1112## enumlist = self.namemappers[0].getall('enum')
1113## enumlist.sort()
1114## for k, v in enumlist:
1115## self.fp.write("\t%s : %s,\n" % (`k`, v))
1116## self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +00001117
1118def compiledata(data):
1119 [type, description, flags] = data
1120 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
1121
1122def is_null(data):
1123 return data[0] == 'null'
1124
1125def is_optional(data):
1126 return (data[2] & 0x8000)
1127
1128def is_enum(data):
1129 return (data[2] & 0x2000)
1130
1131def getdatadoc(data):
1132 [type, descr, flags] = data
1133 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +00001134 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +00001135 if type == '****':
1136 return 'anything'
1137 if type == 'obj ':
1138 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001139 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +00001140
1141dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
1142def compiledataflags(flags):
1143 bits = []
1144 for i in range(16):
1145 if flags & (1<<i):
1146 if i in dataflagdict.keys():
1147 bits.append(dataflagdict[i])
1148 else:
1149 bits.append(`i`)
1150 return '[%s]' % string.join(bits)
1151
Jack Jansen8b777672002-08-07 14:49:00 +00001152def ascii(str):
1153 """Return a string with all non-ascii characters hex-encoded"""
1154 if type(str) != type(''):
1155 return map(ascii, str)
1156 rv = ''
1157 for c in str:
1158 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
1159 rv = rv + c
1160 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +00001161 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +00001162 return rv
1163
Jack Jansen5ccd8261995-07-17 11:43:20 +00001164def identify(str):
1165 """Turn any string into an identifier:
1166 - replace space by _
1167 - replace other illegal chars by _xx_ (hex code)
1168 - prepend _ if the result is a python keyword
1169 """
Jack Jansen66544221997-08-08 14:49:02 +00001170 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +00001171 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +00001172 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +00001173 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001174 ok2 = ok + string.digits
1175 for c in str:
1176 if c in ok:
1177 rv = rv + c
1178 elif c == ' ':
1179 rv = rv + '_'
1180 else:
1181 rv = rv + '_%02.2x_'%ord(c)
1182 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +00001183 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +00001184 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +00001185 return rv
1186
1187# Call the main program
1188
1189if __name__ == '__main__':
1190 main()
1191 sys.exit(1)