blob: 2bae08a4def08ff19ab91cdbb27d8838693a5f62 [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
Benjamin Peterson23681932008-05-12 21:42:13 +00009from warnings import warnpy3k
Benjamin Petersona6864e02008-07-14 17:42:17 +000010warnpy3k("In 3.x, the gensuitemodule module is removed.", stacklevel=2)
Benjamin Peterson23681932008-05-12 21:42:13 +000011
Jack Jansen5ccd8261995-07-17 11:43:20 +000012import MacOS
Jack Jansendf976ca2003-01-26 20:35:47 +000013import EasyDialogs
Jack Jansen5ccd8261995-07-17 11:43:20 +000014import os
15import string
16import sys
17import types
18import StringIO
Jack Jansenb2ecc2c2002-01-24 22:44:07 +000019import keyword
Jack Jansen40926062002-03-30 23:43:04 +000020import macresource
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000021import aetools
22import distutils.sysconfig
23import OSATerminology
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000024from Carbon.Res import *
Jack Jansen1269be52003-03-29 22:54:00 +000025import Carbon.Folder
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000026import MacOS
Jack Jansen714caa02003-03-21 16:07:39 +000027import getopt
Jack Jansen1269be52003-03-29 22:54:00 +000028import plistlib
Jack Jansen5ccd8261995-07-17 11:43:20 +000029
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000030_MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
31DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
32DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
Jack Jansen40926062002-03-30 23:43:04 +000033
Jack Jansen714caa02003-03-21 16:07:39 +000034def usage():
Jack Jansen0ae32202003-04-09 13:25:43 +000035 sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0])
36 sys.stderr.write("""Options:
Tim Peters182b5ac2004-07-18 06:16:08 +000037--output pkgdir Pathname of the output package (short: -o)
Jack Jansen714caa02003-03-21 16:07:39 +000038--resource Parse resource file in stead of launching application (-r)
39--base package Use another base package in stead of default StdSuites (-b)
40--edit old=new Edit suite names, use empty new to skip a suite (-e)
Jack Jansen59cdbce2003-03-21 16:28:09 +000041--creator code Set creator code for package (-c)
Jack Jansen18c9b132003-04-01 13:32:17 +000042--dump Dump aete resource to stdout in stead of creating module (-d)
43--verbose Tell us what happens (-v)
Jack Jansen714caa02003-03-21 16:07:39 +000044""")
Jack Jansen0ae32202003-04-09 13:25:43 +000045 sys.exit(1)
Jack Jansen714caa02003-03-21 16:07:39 +000046
Jack Jansen5ccd8261995-07-17 11:43:20 +000047def main():
Jack Jansen0ae32202003-04-09 13:25:43 +000048 if len(sys.argv) > 1:
49 SHORTOPTS = "rb:o:e:c:dv"
50 LONGOPTS = ("resource", "base=", "output=", "edit=", "creator=", "dump", "verbose")
51 try:
52 opts, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
53 except getopt.GetoptError:
54 usage()
Tim Peters182b5ac2004-07-18 06:16:08 +000055
Jack Jansen0ae32202003-04-09 13:25:43 +000056 process_func = processfile
57 basepkgname = 'StdSuites'
58 output = None
59 edit_modnames = []
60 creatorsignature = None
61 dump = None
62 verbose = None
Tim Peters182b5ac2004-07-18 06:16:08 +000063
Jack Jansen0ae32202003-04-09 13:25:43 +000064 for o, a in opts:
65 if o in ('-r', '--resource'):
66 process_func = processfile_fromresource
67 if o in ('-b', '--base'):
68 basepkgname = a
69 if o in ('-o', '--output'):
70 output = a
71 if o in ('-e', '--edit'):
72 split = a.split('=')
73 if len(split) != 2:
74 usage()
75 edit_modnames.append(split)
76 if o in ('-c', '--creator'):
77 if len(a) != 4:
78 sys.stderr.write("creator must be 4-char string\n")
79 sys.exit(1)
80 creatorsignature = a
81 if o in ('-d', '--dump'):
82 dump = sys.stdout
83 if o in ('-v', '--verbose'):
84 verbose = sys.stderr
Tim Peters182b5ac2004-07-18 06:16:08 +000085
86
Jack Jansen0ae32202003-04-09 13:25:43 +000087 if output and len(args) > 1:
88 sys.stderr.write("%s: cannot specify --output with multiple inputs\n" % sys.argv[0])
89 sys.exit(1)
Tim Peters182b5ac2004-07-18 06:16:08 +000090
Jack Jansen0ae32202003-04-09 13:25:43 +000091 for filename in args:
Tim Peters182b5ac2004-07-18 06:16:08 +000092 process_func(filename, output=output, basepkgname=basepkgname,
Jack Jansen0ae32202003-04-09 13:25:43 +000093 edit_modnames=edit_modnames, creatorsignature=creatorsignature,
94 dump=dump, verbose=verbose)
95 else:
96 main_interactive()
Tim Peters182b5ac2004-07-18 06:16:08 +000097
Jack Jansen1269be52003-03-29 22:54:00 +000098def main_interactive(interact=0, basepkgname='StdSuites'):
Jack Jansen0ae32202003-04-09 13:25:43 +000099 if interact:
100 # Ask for save-filename for each module
101 edit_modnames = None
102 else:
103 # Use default filenames for each module
104 edit_modnames = []
105 appsfolder = Carbon.Folder.FSFindFolder(-32765, 'apps', 0)
106 filename = EasyDialogs.AskFileForOpen(
107 message='Select scriptable application',
108 dialogOptionFlags=0x1056, # allow selection of .app bundles
109 defaultLocation=appsfolder)
110 if not filename:
111 return
112 if not is_scriptable(filename):
113 if EasyDialogs.AskYesNoCancel(
114 "Warning: application does not seem scriptable",
115 yes="Continue", default=2, no="") <= 0:
116 return
117 try:
118 processfile(filename, edit_modnames=edit_modnames, basepkgname=basepkgname,
119 verbose=sys.stderr)
120 except MacOS.Error, arg:
121 print "Error getting terminology:", arg
122 print "Retry, manually parsing resources"
123 processfile_fromresource(filename, edit_modnames=edit_modnames,
124 basepkgname=basepkgname, verbose=sys.stderr)
Tim Peters182b5ac2004-07-18 06:16:08 +0000125
Jack Jansen1269be52003-03-29 22:54:00 +0000126def is_scriptable(application):
Jack Jansen0ae32202003-04-09 13:25:43 +0000127 """Return true if the application is scriptable"""
128 if os.path.isdir(application):
129 plistfile = os.path.join(application, 'Contents', 'Info.plist')
130 if not os.path.exists(plistfile):
131 return False
132 plist = plistlib.Plist.fromFile(plistfile)
133 return plist.get('NSAppleScriptEnabled', False)
134 # If it is a file test for an aete/aeut resource.
135 currf = CurResFile()
136 try:
137 refno = macresource.open_pathname(application)
138 except MacOS.Error:
139 return False
140 UseResFile(refno)
141 n_terminology = Count1Resources('aete') + Count1Resources('aeut') + \
142 Count1Resources('scsz') + Count1Resources('osiz')
143 CloseResFile(refno)
144 UseResFile(currf)
145 return n_terminology > 0
Jack Jansen5ccd8261995-07-17 11:43:20 +0000146
Tim Peters182b5ac2004-07-18 06:16:08 +0000147def processfile_fromresource(fullname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000148 edit_modnames=None, creatorsignature=None, dump=None, verbose=None):
149 """Process all resources in a single file"""
150 if not is_scriptable(fullname) and verbose:
151 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
152 cur = CurResFile()
153 if verbose:
154 print >>verbose, "Processing", fullname
155 rf = macresource.open_pathname(fullname)
156 try:
157 UseResFile(rf)
158 resources = []
159 for i in range(Count1Resources('aete')):
160 res = Get1IndResource('aete', 1+i)
161 resources.append(res)
162 for i in range(Count1Resources('aeut')):
163 res = Get1IndResource('aeut', 1+i)
164 resources.append(res)
Tim Peters182b5ac2004-07-18 06:16:08 +0000165 if verbose:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000166 print >>verbose, "\nLISTING aete+aeut RESOURCES IN", repr(fullname)
Jack Jansen0ae32202003-04-09 13:25:43 +0000167 aetelist = []
168 for res in resources:
169 if verbose:
170 print >>verbose, "decoding", res.GetResInfo(), "..."
171 data = res.data
172 aete = decode(data, verbose)
173 aetelist.append((aete, res.GetResInfo()))
174 finally:
Mark Dickinsona3a50502010-04-03 18:17:54 +0000175 if rf != cur:
Jack Jansen0ae32202003-04-09 13:25:43 +0000176 CloseResFile(rf)
177 UseResFile(cur)
178 # switch back (needed for dialogs in Python)
179 UseResFile(cur)
180 if dump:
181 dumpaetelist(aetelist, dump)
Tim Peters182b5ac2004-07-18 06:16:08 +0000182 compileaetelist(aetelist, fullname, output=output,
Jack Jansen0ae32202003-04-09 13:25:43 +0000183 basepkgname=basepkgname, edit_modnames=edit_modnames,
184 creatorsignature=creatorsignature, verbose=verbose)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000185
Tim Peters182b5ac2004-07-18 06:16:08 +0000186def processfile(fullname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000187 edit_modnames=None, creatorsignature=None, dump=None,
188 verbose=None):
189 """Ask an application for its terminology and process that"""
190 if not is_scriptable(fullname) and verbose:
191 print >>verbose, "Warning: app does not seem scriptable: %s" % fullname
192 if verbose:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000193 print >>verbose, "\nASKING FOR aete DICTIONARY IN", repr(fullname)
Jack Jansen0ae32202003-04-09 13:25:43 +0000194 try:
195 aedescobj, launched = OSATerminology.GetAppTerminology(fullname)
196 except MacOS.Error, arg:
197 if arg[0] in (-1701, -192): # errAEDescNotFound, resNotFound
198 if verbose:
199 print >>verbose, "GetAppTerminology failed with errAEDescNotFound/resNotFound, trying manually"
200 aedata, sig = getappterminology(fullname, verbose=verbose)
201 if not creatorsignature:
202 creatorsignature = sig
203 else:
204 raise
205 else:
206 if launched:
207 if verbose:
208 print >>verbose, "Launched", fullname
209 raw = aetools.unpack(aedescobj)
Tim Peters182b5ac2004-07-18 06:16:08 +0000210 if not raw:
Jack Jansen0ae32202003-04-09 13:25:43 +0000211 if verbose:
212 print >>verbose, 'Unpack returned empty value:', raw
213 return
214 if not raw[0].data:
215 if verbose:
216 print >>verbose, 'Unpack returned value without data:', raw
217 return
218 aedata = raw[0]
219 aete = decode(aedata.data, verbose)
220 if dump:
221 dumpaetelist([aete], dump)
222 return
223 compileaete(aete, None, fullname, output=output, basepkgname=basepkgname,
Tim Peters182b5ac2004-07-18 06:16:08 +0000224 creatorsignature=creatorsignature, edit_modnames=edit_modnames,
Jack Jansen0ae32202003-04-09 13:25:43 +0000225 verbose=verbose)
Tim Peters182b5ac2004-07-18 06:16:08 +0000226
Jack Jansen18c9b132003-04-01 13:32:17 +0000227def getappterminology(fullname, verbose=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000228 """Get application terminology by sending an AppleEvent"""
229 # First check that we actually can send AppleEvents
230 if not MacOS.WMAvailable():
231 raise RuntimeError, "Cannot send AppleEvents, no access to window manager"
232 # Next, a workaround for a bug in MacOS 10.2: sending events will hang unless
233 # you have created an event loop first.
234 import Carbon.Evt
235 Carbon.Evt.WaitNextEvent(0,0)
236 if os.path.isdir(fullname):
237 # Now get the signature of the application, hoping it is a bundle
238 pkginfo = os.path.join(fullname, 'Contents', 'PkgInfo')
239 if not os.path.exists(pkginfo):
240 raise RuntimeError, "No PkgInfo file found"
241 tp_cr = open(pkginfo, 'rb').read()
242 cr = tp_cr[4:8]
243 else:
244 # Assume it is a file
245 cr, tp = MacOS.GetCreatorAndType(fullname)
246 # Let's talk to it and ask for its AETE
247 talker = aetools.TalkTo(cr)
248 try:
249 talker._start()
250 except (MacOS.Error, aetools.Error), arg:
251 if verbose:
252 print >>verbose, 'Warning: start() failed, continuing anyway:', arg
253 reply = talker.send("ascr", "gdte")
254 #reply2 = talker.send("ascr", "gdut")
255 # Now pick the bits out of the return that we need.
256 return reply[1]['----'], cr
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000257
Tim Peters182b5ac2004-07-18 06:16:08 +0000258
259def compileaetelist(aetelist, fullname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000260 edit_modnames=None, creatorsignature=None, verbose=None):
261 for aete, resinfo in aetelist:
Tim Peters182b5ac2004-07-18 06:16:08 +0000262 compileaete(aete, resinfo, fullname, output=output,
Jack Jansen0ae32202003-04-09 13:25:43 +0000263 basepkgname=basepkgname, edit_modnames=edit_modnames,
264 creatorsignature=creatorsignature, verbose=verbose)
Jack Jansen18c9b132003-04-01 13:32:17 +0000265
266def dumpaetelist(aetelist, output):
Jack Jansen0ae32202003-04-09 13:25:43 +0000267 import pprint
268 pprint.pprint(aetelist, output)
Tim Peters182b5ac2004-07-18 06:16:08 +0000269
Jack Jansen18c9b132003-04-01 13:32:17 +0000270def decode(data, verbose=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000271 """Decode a resource into a python data structure"""
272 f = StringIO.StringIO(data)
273 aete = generic(getaete, f)
274 aete = simplify(aete)
275 processed = f.tell()
276 unprocessed = len(f.read())
277 total = f.tell()
278 if unprocessed and verbose:
279 verbose.write("%d processed + %d unprocessed = %d total\n" %
280 (processed, unprocessed, total))
281 return aete
Jack Jansen5ccd8261995-07-17 11:43:20 +0000282
283def simplify(item):
Jack Jansen0ae32202003-04-09 13:25:43 +0000284 """Recursively replace singleton tuples by their constituent item"""
285 if type(item) is types.ListType:
286 return map(simplify, item)
287 elif type(item) == types.TupleType and len(item) == 2:
288 return simplify(item[1])
289 else:
290 return item
Jack Jansen5ccd8261995-07-17 11:43:20 +0000291
292
293# Here follows the aete resource decoder.
294# It is presented bottom-up instead of top-down because there are direct
295# references to the lower-level part-decoders from the high-level part-decoders.
296
297def getbyte(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000298 c = f.read(1)
299 if not c:
300 raise EOFError, 'in getbyte' + str(args)
301 return ord(c)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000302
303def getword(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000304 getalign(f)
305 s = f.read(2)
306 if len(s) < 2:
307 raise EOFError, 'in getword' + str(args)
308 return (ord(s[0])<<8) | ord(s[1])
Jack Jansen5ccd8261995-07-17 11:43:20 +0000309
310def getlong(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000311 getalign(f)
312 s = f.read(4)
313 if len(s) < 4:
314 raise EOFError, 'in getlong' + str(args)
315 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
Jack Jansen5ccd8261995-07-17 11:43:20 +0000316
317def getostype(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000318 getalign(f)
319 s = f.read(4)
320 if len(s) < 4:
321 raise EOFError, 'in getostype' + str(args)
322 return s
Jack Jansen5ccd8261995-07-17 11:43:20 +0000323
324def getpstr(f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000325 c = f.read(1)
326 if len(c) < 1:
327 raise EOFError, 'in getpstr[1]' + str(args)
328 nbytes = ord(c)
329 if nbytes == 0: return ''
330 s = f.read(nbytes)
331 if len(s) < nbytes:
332 raise EOFError, 'in getpstr[2]' + str(args)
333 return s
Jack Jansen5ccd8261995-07-17 11:43:20 +0000334
335def getalign(f):
Jack Jansen0ae32202003-04-09 13:25:43 +0000336 if f.tell() & 1:
337 c = f.read(1)
Mark Dickinsona3a50502010-04-03 18:17:54 +0000338 ##if c != '\0':
Walter Dörwald70a6b492004-02-12 17:35:32 +0000339 ## print align:', repr(c)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000340
341def getlist(f, description, getitem):
Jack Jansen0ae32202003-04-09 13:25:43 +0000342 count = getword(f)
343 list = []
344 for i in range(count):
345 list.append(generic(getitem, f))
346 getalign(f)
347 return list
Jack Jansen5ccd8261995-07-17 11:43:20 +0000348
349def alt_generic(what, f, *args):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000350 print "generic", repr(what), args
Jack Jansen0ae32202003-04-09 13:25:43 +0000351 res = vageneric(what, f, args)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000352 print '->', repr(res)
Jack Jansen0ae32202003-04-09 13:25:43 +0000353 return res
Jack Jansen5ccd8261995-07-17 11:43:20 +0000354
355def generic(what, f, *args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000356 if type(what) == types.FunctionType:
357 return apply(what, (f,) + args)
358 if type(what) == types.ListType:
359 record = []
360 for thing in what:
361 item = apply(generic, thing[:1] + (f,) + thing[1:])
362 record.append((thing[1], item))
363 return record
Walter Dörwald70a6b492004-02-12 17:35:32 +0000364 return "BAD GENERIC ARGS: %r" % (what,)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000365
366getdata = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000367 (getostype, "type"),
368 (getpstr, "description"),
369 (getword, "flags")
370 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000371getargument = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000372 (getpstr, "name"),
373 (getostype, "keyword"),
374 (getdata, "what")
375 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000376getevent = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000377 (getpstr, "name"),
378 (getpstr, "description"),
379 (getostype, "suite code"),
380 (getostype, "event code"),
381 (getdata, "returns"),
382 (getdata, "accepts"),
383 (getlist, "optional arguments", getargument)
384 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000385getproperty = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000386 (getpstr, "name"),
387 (getostype, "code"),
388 (getdata, "what")
389 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000390getelement = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000391 (getostype, "type"),
392 (getlist, "keyform", getostype)
393 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000394getclass = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000395 (getpstr, "name"),
396 (getostype, "class code"),
397 (getpstr, "description"),
398 (getlist, "properties", getproperty),
399 (getlist, "elements", getelement)
400 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000401getcomparison = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000402 (getpstr, "operator name"),
403 (getostype, "operator ID"),
404 (getpstr, "operator comment"),
405 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000406getenumerator = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000407 (getpstr, "enumerator name"),
408 (getostype, "enumerator ID"),
409 (getpstr, "enumerator comment")
410 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000411getenumeration = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000412 (getostype, "enumeration ID"),
413 (getlist, "enumerator", getenumerator)
414 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000415getsuite = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000416 (getpstr, "suite name"),
417 (getpstr, "suite description"),
418 (getostype, "suite ID"),
419 (getword, "suite level"),
420 (getword, "suite version"),
421 (getlist, "events", getevent),
422 (getlist, "classes", getclass),
423 (getlist, "comparisons", getcomparison),
424 (getlist, "enumerations", getenumeration)
425 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000426getaete = [
Jack Jansen0ae32202003-04-09 13:25:43 +0000427 (getword, "major/minor version in BCD"),
428 (getword, "language code"),
429 (getword, "script code"),
430 (getlist, "suites", getsuite)
431 ]
Jack Jansen5ccd8261995-07-17 11:43:20 +0000432
Tim Peters182b5ac2004-07-18 06:16:08 +0000433def compileaete(aete, resinfo, fname, output=None, basepkgname=None,
Jack Jansen0ae32202003-04-09 13:25:43 +0000434 edit_modnames=None, creatorsignature=None, verbose=None):
435 """Generate code for a full aete resource. fname passed for doc purposes"""
436 [version, language, script, suites] = aete
437 major, minor = divmod(version, 256)
438 if not creatorsignature:
439 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
440 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
441 if language:
442 packagename = packagename+'_lang%d'%language
443 if script:
444 packagename = packagename+'_script%d'%script
445 if len(packagename) > 27:
446 packagename = packagename[:27]
447 if output:
448 # XXXX Put this in site-packages if it isn't a full pathname?
449 if not os.path.exists(output):
450 os.mkdir(output)
451 pathname = output
452 else:
453 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
454 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
455 output = pathname
456 if not pathname:
457 return
458 packagename = os.path.split(os.path.normpath(pathname))[1]
459 if not basepkgname:
460 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
461 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
462 if basepkgname:
463 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
464 if dirname and not dirname in sys.path:
465 sys.path.insert(0, dirname)
466 basepackage = __import__(basepkgname)
467 else:
468 basepackage = None
469 suitelist = []
470 allprecompinfo = []
471 allsuites = []
472 for suite in suites:
473 compiler = SuiteCompiler(suite, basepackage, output, edit_modnames, verbose)
474 code, modname, precompinfo = compiler.precompilesuite()
475 if not code:
476 continue
477 allprecompinfo = allprecompinfo + precompinfo
478 suiteinfo = suite, pathname, modname
479 suitelist.append((code, modname))
480 allsuites.append(compiler)
481 for compiler in allsuites:
482 compiler.compilesuite(major, minor, language, script, fname, allprecompinfo)
483 initfilename = os.path.join(output, '__init__.py')
484 fp = open(initfilename, 'w')
485 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
486 fp.write('"""\n')
487 fp.write("Package generated from %s\n"%ascii(fname))
488 if resinfo:
489 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
490 fp.write('"""\n')
491 fp.write('import aetools\n')
492 fp.write('Error = aetools.Error\n')
493 suitelist.sort()
494 for code, modname in suitelist:
495 fp.write("import %s\n" % modname)
496 fp.write("\n\n_code_to_module = {\n")
497 for code, modname in suitelist:
498 fp.write(" '%s' : %s,\n"%(ascii(code), modname))
499 fp.write("}\n\n")
500 fp.write("\n\n_code_to_fullname = {\n")
501 for code, modname in suitelist:
502 fp.write(" '%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
503 fp.write("}\n\n")
504 for code, modname in suitelist:
505 fp.write("from %s import *\n"%modname)
Tim Peters182b5ac2004-07-18 06:16:08 +0000506
Jack Jansen0ae32202003-04-09 13:25:43 +0000507 # Generate property dicts and element dicts for all types declared in this module
508 fp.write("\ndef getbaseclasses(v):\n")
509 fp.write(" if not getattr(v, '_propdict', None):\n")
510 fp.write(" v._propdict = {}\n")
511 fp.write(" v._elemdict = {}\n")
512 fp.write(" for superclassname in getattr(v, '_superclassnames', []):\n")
513 fp.write(" superclass = eval(superclassname)\n")
514 fp.write(" getbaseclasses(superclass)\n")
515 fp.write(" v._propdict.update(getattr(superclass, '_propdict', {}))\n")
516 fp.write(" v._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
517 fp.write(" v._propdict.update(getattr(v, '_privpropdict', {}))\n")
518 fp.write(" v._elemdict.update(getattr(v, '_privelemdict', {}))\n")
519 fp.write("\n")
520 fp.write("import StdSuites\n")
521 allprecompinfo.sort()
522 if allprecompinfo:
523 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
524 for codenamemapper in allprecompinfo:
525 for k, v in codenamemapper.getall('class'):
526 fp.write("getbaseclasses(%s)\n" % v)
Jack Jansen8b777672002-08-07 14:49:00 +0000527
Jack Jansen0ae32202003-04-09 13:25:43 +0000528 # Generate a code-to-name mapper for all of the types (classes) declared in this module
Jack Jansenc8882b12003-06-13 14:27:35 +0000529 application_class = None
Jack Jansen0ae32202003-04-09 13:25:43 +0000530 if allprecompinfo:
531 fp.write("\n#\n# Indices of types declared in this module\n#\n")
532 fp.write("_classdeclarations = {\n")
533 for codenamemapper in allprecompinfo:
534 for k, v in codenamemapper.getall('class'):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000535 fp.write(" %r : %s,\n" % (k, v))
Jack Jansen2cf9b952003-06-18 14:17:34 +0000536 if k == 'capp':
537 application_class = v
Jack Jansen0ae32202003-04-09 13:25:43 +0000538 fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000539
Tim Peters182b5ac2004-07-18 06:16:08 +0000540
Jack Jansen0ae32202003-04-09 13:25:43 +0000541 if suitelist:
542 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
543 for code, modname in suitelist[1:]:
544 fp.write(",\n %s_Events"%modname)
545 fp.write(",\n aetools.TalkTo):\n")
Walter Dörwald70a6b492004-02-12 17:35:32 +0000546 fp.write(" _signature = %r\n\n"%(creatorsignature,))
Jack Jansen0ae32202003-04-09 13:25:43 +0000547 fp.write(" _moduleName = '%s'\n\n"%packagename)
Jack Jansenc8882b12003-06-13 14:27:35 +0000548 if application_class:
549 fp.write(" _elemdict = %s._elemdict\n" % application_class)
550 fp.write(" _propdict = %s._propdict\n" % application_class)
Jack Jansen0ae32202003-04-09 13:25:43 +0000551 fp.close()
Jack Jansen3279cb02003-04-01 14:25:49 +0000552
553class SuiteCompiler:
Jack Jansen0ae32202003-04-09 13:25:43 +0000554 def __init__(self, suite, basepackage, output, edit_modnames, verbose):
555 self.suite = suite
556 self.basepackage = basepackage
557 self.edit_modnames = edit_modnames
558 self.output = output
559 self.verbose = verbose
Tim Peters182b5ac2004-07-18 06:16:08 +0000560
Jack Jansen0ae32202003-04-09 13:25:43 +0000561 # Set by precompilesuite
562 self.pathname = None
563 self.modname = None
Tim Peters182b5ac2004-07-18 06:16:08 +0000564
Jack Jansen0ae32202003-04-09 13:25:43 +0000565 # Set by compilesuite
566 self.fp = None
567 self.basemodule = None
568 self.enumsneeded = {}
Tim Peters182b5ac2004-07-18 06:16:08 +0000569
Jack Jansen0ae32202003-04-09 13:25:43 +0000570 def precompilesuite(self):
571 """Parse a single suite without generating the output. This step is needed
572 so we can resolve recursive references by suites to enums/comps/etc declared
573 in other suites"""
574 [name, desc, code, level, version, events, classes, comps, enums] = self.suite
Tim Peters182b5ac2004-07-18 06:16:08 +0000575
Jack Jansen0ae32202003-04-09 13:25:43 +0000576 modname = identify(name)
577 if len(modname) > 28:
578 modname = modname[:27]
579 if self.edit_modnames is None:
580 self.pathname = EasyDialogs.AskFileForSave(message='Python output file',
581 savedFileName=modname+'.py')
582 else:
583 for old, new in self.edit_modnames:
584 if old == modname:
585 modname = new
586 if modname:
587 self.pathname = os.path.join(self.output, modname + '.py')
588 else:
589 self.pathname = None
590 if not self.pathname:
591 return None, None, None
Tim Peters182b5ac2004-07-18 06:16:08 +0000592
Jack Jansen0ae32202003-04-09 13:25:43 +0000593 self.modname = os.path.splitext(os.path.split(self.pathname)[1])[0]
Tim Peters182b5ac2004-07-18 06:16:08 +0000594
Mark Dickinsona3a50502010-04-03 18:17:54 +0000595 if self.basepackage and code in self.basepackage._code_to_module:
Jack Jansen0ae32202003-04-09 13:25:43 +0000596 # We are an extension of a baseclass (usually an application extending
597 # Standard_Suite or so). Import everything from our base module
598 basemodule = self.basepackage._code_to_module[code]
599 else:
600 # We are not an extension.
601 basemodule = None
Tim Peters182b5ac2004-07-18 06:16:08 +0000602
Jack Jansen0ae32202003-04-09 13:25:43 +0000603 self.enumsneeded = {}
604 for event in events:
605 self.findenumsinevent(event)
Tim Peters182b5ac2004-07-18 06:16:08 +0000606
Jack Jansen0ae32202003-04-09 13:25:43 +0000607 objc = ObjectCompiler(None, self.modname, basemodule, interact=(self.edit_modnames is None),
608 verbose=self.verbose)
609 for cls in classes:
610 objc.compileclass(cls)
611 for cls in classes:
612 objc.fillclasspropsandelems(cls)
613 for comp in comps:
614 objc.compilecomparison(comp)
615 for enum in enums:
616 objc.compileenumeration(enum)
Tim Peters182b5ac2004-07-18 06:16:08 +0000617
Jack Jansen0ae32202003-04-09 13:25:43 +0000618 for enum in self.enumsneeded.keys():
619 objc.checkforenum(enum)
Tim Peters182b5ac2004-07-18 06:16:08 +0000620
Jack Jansen0ae32202003-04-09 13:25:43 +0000621 objc.dumpindex()
Tim Peters182b5ac2004-07-18 06:16:08 +0000622
Jack Jansen0ae32202003-04-09 13:25:43 +0000623 precompinfo = objc.getprecompinfo(self.modname)
Tim Peters182b5ac2004-07-18 06:16:08 +0000624
Jack Jansen0ae32202003-04-09 13:25:43 +0000625 return code, self.modname, precompinfo
Tim Peters182b5ac2004-07-18 06:16:08 +0000626
Jack Jansen0ae32202003-04-09 13:25:43 +0000627 def compilesuite(self, major, minor, language, script, fname, precompinfo):
628 """Generate code for a single suite"""
629 [name, desc, code, level, version, events, classes, comps, enums] = self.suite
630 # Sort various lists, so re-generated source is easier compared
631 def class_sorter(k1, k2):
632 """Sort classes by code, and make sure main class sorts before synonyms"""
633 # [name, code, desc, properties, elements] = cls
634 if k1[1] < k2[1]: return -1
635 if k1[1] > k2[1]: return 1
636 if not k2[3] or k2[3][0][1] == 'c@#!':
637 # This is a synonym, the other one is better
638 return -1
639 if not k1[3] or k1[3][0][1] == 'c@#!':
640 # This is a synonym, the other one is better
641 return 1
642 return 0
Tim Peters182b5ac2004-07-18 06:16:08 +0000643
Jack Jansen0ae32202003-04-09 13:25:43 +0000644 events.sort()
645 classes.sort(class_sorter)
646 comps.sort()
647 enums.sort()
Tim Peters182b5ac2004-07-18 06:16:08 +0000648
Jack Jansen0ae32202003-04-09 13:25:43 +0000649 self.fp = fp = open(self.pathname, 'w')
650 MacOS.SetCreatorAndType(self.pathname, 'Pyth', 'TEXT')
Tim Peters182b5ac2004-07-18 06:16:08 +0000651
Jack Jansen0ae32202003-04-09 13:25:43 +0000652 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
653 fp.write("Level %d, version %d\n\n" % (level, version))
654 fp.write("Generated from %s\n"%ascii(fname))
655 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
656 (major, minor, language, script))
657 fp.write('"""\n\n')
Tim Peters182b5ac2004-07-18 06:16:08 +0000658
Jack Jansen0ae32202003-04-09 13:25:43 +0000659 fp.write('import aetools\n')
660 fp.write('import MacOS\n\n')
Walter Dörwald70a6b492004-02-12 17:35:32 +0000661 fp.write("_code = %r\n\n"% (code,))
Mark Dickinsona3a50502010-04-03 18:17:54 +0000662 if self.basepackage and code in self.basepackage._code_to_module:
Jack Jansen0ae32202003-04-09 13:25:43 +0000663 # We are an extension of a baseclass (usually an application extending
664 # Standard_Suite or so). Import everything from our base module
665 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code][0])
666 basemodule = self.basepackage._code_to_module[code]
Mark Dickinsona3a50502010-04-03 18:17:54 +0000667 elif self.basepackage and code.lower() in self.basepackage._code_to_module:
Jack Jansen0ae32202003-04-09 13:25:43 +0000668 # This is needed by CodeWarrior and some others.
669 fp.write('from %s import *\n'%self.basepackage._code_to_fullname[code.lower()][0])
670 basemodule = self.basepackage._code_to_module[code.lower()]
671 else:
672 # We are not an extension.
673 basemodule = None
674 self.basemodule = basemodule
675 self.compileclassheader()
Tim Peters182b5ac2004-07-18 06:16:08 +0000676
Jack Jansen0ae32202003-04-09 13:25:43 +0000677 self.enumsneeded = {}
678 if events:
679 for event in events:
680 self.compileevent(event)
681 else:
682 fp.write(" pass\n\n")
Tim Peters182b5ac2004-07-18 06:16:08 +0000683
Jack Jansen0ae32202003-04-09 13:25:43 +0000684 objc = ObjectCompiler(fp, self.modname, basemodule, precompinfo, interact=(self.edit_modnames is None),
685 verbose=self.verbose)
686 for cls in classes:
687 objc.compileclass(cls)
688 for cls in classes:
689 objc.fillclasspropsandelems(cls)
690 for comp in comps:
691 objc.compilecomparison(comp)
692 for enum in enums:
693 objc.compileenumeration(enum)
Tim Peters182b5ac2004-07-18 06:16:08 +0000694
Jack Jansen0ae32202003-04-09 13:25:43 +0000695 for enum in self.enumsneeded.keys():
696 objc.checkforenum(enum)
Tim Peters182b5ac2004-07-18 06:16:08 +0000697
Jack Jansen0ae32202003-04-09 13:25:43 +0000698 objc.dumpindex()
Tim Peters182b5ac2004-07-18 06:16:08 +0000699
Jack Jansen0ae32202003-04-09 13:25:43 +0000700 def compileclassheader(self):
701 """Generate class boilerplate"""
702 classname = '%s_Events'%self.modname
703 if self.basemodule:
704 modshortname = string.split(self.basemodule.__name__, '.')[-1]
705 baseclassname = '%s_Events'%modshortname
706 self.fp.write("class %s(%s):\n\n"%(classname, baseclassname))
707 else:
708 self.fp.write("class %s:\n\n"%classname)
Tim Peters182b5ac2004-07-18 06:16:08 +0000709
Jack Jansen0ae32202003-04-09 13:25:43 +0000710 def compileevent(self, event):
711 """Generate code for a single event"""
712 [name, desc, code, subcode, returns, accepts, arguments] = event
713 fp = self.fp
714 funcname = identify(name)
715 #
716 # generate name->keyword map
717 #
718 if arguments:
719 fp.write(" _argmap_%s = {\n"%funcname)
720 for a in arguments:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000721 fp.write(" %r : %r,\n"%(identify(a[0]), a[1]))
Jack Jansen0ae32202003-04-09 13:25:43 +0000722 fp.write(" }\n\n")
Tim Peters182b5ac2004-07-18 06:16:08 +0000723
Jack Jansen0ae32202003-04-09 13:25:43 +0000724 #
725 # Generate function header
726 #
727 has_arg = (not is_null(accepts))
728 opt_arg = (has_arg and is_optional(accepts))
Tim Peters182b5ac2004-07-18 06:16:08 +0000729
Jack Jansen0ae32202003-04-09 13:25:43 +0000730 fp.write(" def %s(self, "%funcname)
731 if has_arg:
732 if not opt_arg:
733 fp.write("_object, ") # Include direct object, if it has one
734 else:
735 fp.write("_object=None, ") # Also include if it is optional
736 else:
737 fp.write("_no_object=None, ") # For argument checking
738 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
739 #
740 # Generate doc string (important, since it may be the only
741 # available documentation, due to our name-remaping)
742 #
743 fp.write(' """%s: %s\n'%(ascii(name), ascii(desc)))
744 if has_arg:
745 fp.write(" Required argument: %s\n"%getdatadoc(accepts))
746 elif opt_arg:
747 fp.write(" Optional argument: %s\n"%getdatadoc(accepts))
748 for arg in arguments:
749 fp.write(" Keyword argument %s: %s\n"%(identify(arg[0]),
750 getdatadoc(arg[2])))
751 fp.write(" Keyword argument _attributes: AppleEvent attribute dictionary\n")
752 if not is_null(returns):
753 fp.write(" Returns: %s\n"%getdatadoc(returns))
754 fp.write(' """\n')
755 #
756 # Fiddle the args so everything ends up in 'arguments' dictionary
757 #
Walter Dörwald70a6b492004-02-12 17:35:32 +0000758 fp.write(" _code = %r\n"% (code,))
759 fp.write(" _subcode = %r\n\n"% (subcode,))
Jack Jansen0ae32202003-04-09 13:25:43 +0000760 #
761 # Do keyword name substitution
762 #
763 if arguments:
764 fp.write(" aetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
765 else:
766 fp.write(" if _arguments: raise TypeError, 'No optional args expected'\n")
767 #
768 # Stuff required arg (if there is one) into arguments
769 #
770 if has_arg:
771 fp.write(" _arguments['----'] = _object\n")
772 elif opt_arg:
773 fp.write(" if _object:\n")
774 fp.write(" _arguments['----'] = _object\n")
775 else:
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000776 fp.write(" if _no_object is not None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen0ae32202003-04-09 13:25:43 +0000777 fp.write("\n")
778 #
779 # Do enum-name substitution
780 #
781 for a in arguments:
782 if is_enum(a[2]):
783 kname = a[1]
784 ename = a[2][0]
Mark Dickinsona3a50502010-04-03 18:17:54 +0000785 if ename != '****':
Walter Dörwald70a6b492004-02-12 17:35:32 +0000786 fp.write(" aetools.enumsubst(_arguments, %r, _Enum_%s)\n" %
787 (kname, identify(ename)))
Jack Jansen0ae32202003-04-09 13:25:43 +0000788 self.enumsneeded[ename] = 1
789 fp.write("\n")
790 #
791 # Do the transaction
792 #
793 fp.write(" _reply, _arguments, _attributes = self.send(_code, _subcode,\n")
794 fp.write(" _arguments, _attributes)\n")
795 #
796 # Error handling
797 #
798 fp.write(" if _arguments.get('errn', 0):\n")
799 fp.write(" raise aetools.Error, aetools.decodeerror(_arguments)\n")
800 fp.write(" # XXXX Optionally decode result\n")
801 #
802 # Decode result
803 #
Mark Dickinsona3a50502010-04-03 18:17:54 +0000804 fp.write(" if '----' in _arguments:\n")
Jack Jansen0ae32202003-04-09 13:25:43 +0000805 if is_enum(returns):
806 fp.write(" # XXXX Should do enum remapping here...\n")
807 fp.write(" return _arguments['----']\n")
808 fp.write("\n")
Tim Peters182b5ac2004-07-18 06:16:08 +0000809
Jack Jansen0ae32202003-04-09 13:25:43 +0000810 def findenumsinevent(self, event):
811 """Find all enums for a single event"""
812 [name, desc, code, subcode, returns, accepts, arguments] = event
813 for a in arguments:
814 if is_enum(a[2]):
815 ename = a[2][0]
Mark Dickinsona3a50502010-04-03 18:17:54 +0000816 if ename != '****':
Jack Jansen0ae32202003-04-09 13:25:43 +0000817 self.enumsneeded[ename] = 1
Tim Peters182b5ac2004-07-18 06:16:08 +0000818
Jack Jansen12b2b762000-08-20 19:30:56 +0000819#
820# This class stores the code<->name translations for a single module. It is used
821# to keep the information while we're compiling the module, but we also keep these objects
822# around so if one suite refers to, say, an enum in another suite we know where to
823# find it. Finally, if we really can't find a code, the user can add modules by
824# hand.
825#
826class CodeNameMapper:
Tim Peters182b5ac2004-07-18 06:16:08 +0000827
Jack Jansen0ae32202003-04-09 13:25:43 +0000828 def __init__(self, interact=1, verbose=None):
829 self.code2name = {
830 "property" : {},
831 "class" : {},
832 "enum" : {},
833 "comparison" : {},
834 }
835 self.name2code = {
836 "property" : {},
837 "class" : {},
838 "enum" : {},
839 "comparison" : {},
840 }
841 self.modulename = None
842 self.star_imported = 0
843 self.can_interact = interact
844 self.verbose = verbose
Tim Peters182b5ac2004-07-18 06:16:08 +0000845
Jack Jansen0ae32202003-04-09 13:25:43 +0000846 def addnamecode(self, type, name, code):
847 self.name2code[type][name] = code
Mark Dickinsona3a50502010-04-03 18:17:54 +0000848 if code not in self.code2name[type]:
Jack Jansen0ae32202003-04-09 13:25:43 +0000849 self.code2name[type][code] = name
Tim Peters182b5ac2004-07-18 06:16:08 +0000850
Jack Jansen0ae32202003-04-09 13:25:43 +0000851 def hasname(self, name):
852 for dict in self.name2code.values():
Mark Dickinsona3a50502010-04-03 18:17:54 +0000853 if name in dict:
Jack Jansen0ae32202003-04-09 13:25:43 +0000854 return True
855 return False
Tim Peters182b5ac2004-07-18 06:16:08 +0000856
Jack Jansen0ae32202003-04-09 13:25:43 +0000857 def hascode(self, type, code):
Mark Dickinsona3a50502010-04-03 18:17:54 +0000858 return code in self.code2name[type]
Tim Peters182b5ac2004-07-18 06:16:08 +0000859
Jack Jansen0ae32202003-04-09 13:25:43 +0000860 def findcodename(self, type, code):
861 if not self.hascode(type, code):
862 return None, None, None
863 name = self.code2name[type][code]
864 if self.modulename and not self.star_imported:
865 qualname = '%s.%s'%(self.modulename, name)
866 else:
867 qualname = name
868 return name, qualname, self.modulename
Tim Peters182b5ac2004-07-18 06:16:08 +0000869
Jack Jansen0ae32202003-04-09 13:25:43 +0000870 def getall(self, type):
871 return self.code2name[type].items()
Tim Peters182b5ac2004-07-18 06:16:08 +0000872
Jack Jansen0ae32202003-04-09 13:25:43 +0000873 def addmodule(self, module, name, star_imported):
874 self.modulename = name
875 self.star_imported = star_imported
876 for code, name in module._propdeclarations.items():
877 self.addnamecode('property', name, code)
878 for code, name in module._classdeclarations.items():
879 self.addnamecode('class', name, code)
880 for code in module._enumdeclarations.keys():
881 self.addnamecode('enum', '_Enum_'+identify(code), code)
882 for code, name in module._compdeclarations.items():
883 self.addnamecode('comparison', name, code)
Tim Peters182b5ac2004-07-18 06:16:08 +0000884
Jack Jansen0ae32202003-04-09 13:25:43 +0000885 def prepareforexport(self, name=None):
886 if not self.modulename:
887 self.modulename = name
888 return self
Tim Peters182b5ac2004-07-18 06:16:08 +0000889
Jack Jansen66544221997-08-08 14:49:02 +0000890class ObjectCompiler:
Tim Peters182b5ac2004-07-18 06:16:08 +0000891 def __init__(self, fp, modname, basesuite, othernamemappers=None, interact=1,
Jack Jansen0ae32202003-04-09 13:25:43 +0000892 verbose=None):
893 self.fp = fp
894 self.verbose = verbose
895 self.basesuite = basesuite
896 self.can_interact = interact
897 self.modulename = modname
898 self.namemappers = [CodeNameMapper(self.can_interact, self.verbose)]
899 if othernamemappers:
900 self.othernamemappers = othernamemappers[:]
901 else:
902 self.othernamemappers = []
903 if basesuite:
904 basemapper = CodeNameMapper(self.can_interact, self.verbose)
905 basemapper.addmodule(basesuite, '', 1)
906 self.namemappers.append(basemapper)
Tim Peters182b5ac2004-07-18 06:16:08 +0000907
Jack Jansen0ae32202003-04-09 13:25:43 +0000908 def getprecompinfo(self, modname):
909 list = []
910 for mapper in self.namemappers:
911 emapper = mapper.prepareforexport(modname)
912 if emapper:
913 list.append(emapper)
914 return list
Tim Peters182b5ac2004-07-18 06:16:08 +0000915
Jack Jansen0ae32202003-04-09 13:25:43 +0000916 def findcodename(self, type, code):
917 while 1:
918 # First try: check whether we already know about this code.
919 for mapper in self.namemappers:
920 if mapper.hascode(type, code):
921 return mapper.findcodename(type, code)
922 # Second try: maybe one of the other modules knows about it.
923 for mapper in self.othernamemappers:
924 if mapper.hascode(type, code):
925 self.othernamemappers.remove(mapper)
926 self.namemappers.append(mapper)
927 if self.fp:
928 self.fp.write("import %s\n"%mapper.modulename)
929 break
930 else:
931 # If all this has failed we ask the user for a guess on where it could
932 # be and retry.
933 if self.fp:
934 m = self.askdefinitionmodule(type, code)
935 else:
936 m = None
937 if not m: return None, None, None
938 mapper = CodeNameMapper(self.can_interact, self.verbose)
939 mapper.addmodule(m, m.__name__, 0)
940 self.namemappers.append(mapper)
Tim Peters182b5ac2004-07-18 06:16:08 +0000941
Jack Jansen0ae32202003-04-09 13:25:43 +0000942 def hasname(self, name):
943 for mapper in self.othernamemappers:
944 if mapper.hasname(name) and mapper.modulename != self.modulename:
945 if self.verbose:
946 print >>self.verbose, "Duplicate Python identifier:", name, self.modulename, mapper.modulename
947 return True
948 return False
Tim Peters182b5ac2004-07-18 06:16:08 +0000949
Jack Jansen0ae32202003-04-09 13:25:43 +0000950 def askdefinitionmodule(self, type, code):
951 if not self.can_interact:
952 if self.verbose:
953 print >>self.verbose, "** No definition for %s '%s' found" % (type, code)
954 return None
955 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
956 if not path: return
957 path, file = os.path.split(path)
958 modname = os.path.splitext(file)[0]
959 if not path in sys.path:
960 sys.path.insert(0, path)
961 m = __import__(modname)
962 self.fp.write("import %s\n"%modname)
963 return m
Tim Peters182b5ac2004-07-18 06:16:08 +0000964
Jack Jansen0ae32202003-04-09 13:25:43 +0000965 def compileclass(self, cls):
966 [name, code, desc, properties, elements] = cls
967 pname = identify(name)
968 if self.namemappers[0].hascode('class', code):
969 # plural forms and such
970 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
971 if self.fp:
972 self.fp.write("\n%s = %s\n"%(pname, othername))
973 else:
974 if self.fp:
975 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
976 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(desc)))
Walter Dörwald70a6b492004-02-12 17:35:32 +0000977 self.fp.write(' want = %r\n' % (code,))
Jack Jansen0ae32202003-04-09 13:25:43 +0000978 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansenc8882b12003-06-13 14:27:35 +0000979 is_application_class = (code == 'capp')
Jack Jansen0ae32202003-04-09 13:25:43 +0000980 properties.sort()
981 for prop in properties:
Jack Jansenc8882b12003-06-13 14:27:35 +0000982 self.compileproperty(prop, is_application_class)
Jack Jansen0ae32202003-04-09 13:25:43 +0000983 elements.sort()
984 for elem in elements:
985 self.compileelement(elem)
Tim Peters182b5ac2004-07-18 06:16:08 +0000986
Jack Jansenc8882b12003-06-13 14:27:35 +0000987 def compileproperty(self, prop, is_application_class=False):
Jack Jansen0ae32202003-04-09 13:25:43 +0000988 [name, code, what] = prop
989 if code == 'c@#!':
990 # Something silly with plurals. Skip it.
991 return
992 pname = identify(name)
993 if self.namemappers[0].hascode('property', code):
994 # plural forms and such
995 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
996 if pname == othername:
997 return
998 if self.fp:
999 self.fp.write("\n_Prop_%s = _Prop_%s\n"%(pname, othername))
1000 else:
Jack Jansen0ae32202003-04-09 13:25:43 +00001001 if self.fp:
1002 self.fp.write("class _Prop_%s(aetools.NProperty):\n" % pname)
1003 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(what[1])))
Walter Dörwald70a6b492004-02-12 17:35:32 +00001004 self.fp.write(" which = %r\n" % (code,))
1005 self.fp.write(" want = %r\n" % (what[0],))
Jack Jansen0ae32202003-04-09 13:25:43 +00001006 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansenc8882b12003-06-13 14:27:35 +00001007 if is_application_class and self.fp:
1008 self.fp.write("%s = _Prop_%s()\n" % (pname, pname))
Tim Peters182b5ac2004-07-18 06:16:08 +00001009
Jack Jansen0ae32202003-04-09 13:25:43 +00001010 def compileelement(self, elem):
1011 [code, keyform] = elem
1012 if self.fp:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001013 self.fp.write("# element %r as %s\n" % (code, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +00001014
Jack Jansen0ae32202003-04-09 13:25:43 +00001015 def fillclasspropsandelems(self, cls):
1016 [name, code, desc, properties, elements] = cls
1017 cname = identify(name)
1018 if self.namemappers[0].hascode('class', code) and \
1019 self.namemappers[0].findcodename('class', code)[0] != cname:
1020 # This is an other name (plural or so) for something else. Skip.
1021 if self.fp and (elements or len(properties) > 1 or (len(properties) == 1 and
1022 properties[0][1] != 'c@#!')):
1023 if self.verbose:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001024 print >>self.verbose, '** Skip multiple %s of %s (code %r)' % (cname, self.namemappers[0].findcodename('class', code)[0], code)
Jack Jansen0ae32202003-04-09 13:25:43 +00001025 raise RuntimeError, "About to skip non-empty class"
1026 return
1027 plist = []
1028 elist = []
1029 superclasses = []
1030 for prop in properties:
1031 [pname, pcode, what] = prop
1032 if pcode == "c@#^":
1033 superclasses.append(what)
1034 if pcode == 'c@#!':
1035 continue
1036 pname = identify(pname)
1037 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +00001038
Jack Jansen0ae32202003-04-09 13:25:43 +00001039 superclassnames = []
1040 for superclass in superclasses:
1041 superId, superDesc, dummy = superclass
1042 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
1043 # I don't think this is correct:
1044 if superclassname == cname:
1045 pass # superclassnames.append(fullyqualifiedname)
1046 else:
1047 superclassnames.append(superclassname)
Jack Jansen8b777672002-08-07 14:49:00 +00001048
Jack Jansen0ae32202003-04-09 13:25:43 +00001049 if self.fp:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001050 self.fp.write("%s._superclassnames = %r\n"%(cname, superclassnames))
Jack Jansen8b777672002-08-07 14:49:00 +00001051
Jack Jansen0ae32202003-04-09 13:25:43 +00001052 for elem in elements:
1053 [ecode, keyform] = elem
1054 if ecode == 'c@#!':
1055 continue
1056 name, ename, module = self.findcodename('class', ecode)
1057 if not name:
1058 if self.fp:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001059 self.fp.write("# XXXX %s element %r not found!!\n"%(cname, ecode))
Jack Jansen0ae32202003-04-09 13:25:43 +00001060 else:
1061 elist.append((name, ename))
Tim Peters182b5ac2004-07-18 06:16:08 +00001062
Jack Jansen0ae32202003-04-09 13:25:43 +00001063 plist.sort()
1064 elist.sort()
Tim Peters182b5ac2004-07-18 06:16:08 +00001065
Jack Jansen0ae32202003-04-09 13:25:43 +00001066 if self.fp:
1067 self.fp.write("%s._privpropdict = {\n"%cname)
1068 for n in plist:
1069 self.fp.write(" '%s' : _Prop_%s,\n"%(n, n))
1070 self.fp.write("}\n")
1071 self.fp.write("%s._privelemdict = {\n"%cname)
1072 for n, fulln in elist:
1073 self.fp.write(" '%s' : %s,\n"%(n, fulln))
1074 self.fp.write("}\n")
Tim Peters182b5ac2004-07-18 06:16:08 +00001075
Jack Jansen0ae32202003-04-09 13:25:43 +00001076 def compilecomparison(self, comp):
1077 [name, code, comment] = comp
1078 iname = identify(name)
1079 self.namemappers[0].addnamecode('comparison', iname, code)
1080 if self.fp:
1081 self.fp.write("class %s(aetools.NComparison):\n" % iname)
1082 self.fp.write(' """%s - %s """\n' % (ascii(name), ascii(comment)))
Tim Peters182b5ac2004-07-18 06:16:08 +00001083
Jack Jansen0ae32202003-04-09 13:25:43 +00001084 def compileenumeration(self, enum):
1085 [code, items] = enum
1086 name = "_Enum_%s" % identify(code)
1087 if self.fp:
1088 self.fp.write("%s = {\n" % name)
1089 for item in items:
1090 self.compileenumerator(item)
1091 self.fp.write("}\n\n")
1092 self.namemappers[0].addnamecode('enum', name, code)
1093 return code
Tim Peters182b5ac2004-07-18 06:16:08 +00001094
Jack Jansen0ae32202003-04-09 13:25:43 +00001095 def compileenumerator(self, item):
1096 [name, code, desc] = item
Walter Dörwald70a6b492004-02-12 17:35:32 +00001097 self.fp.write(" %r : %r,\t# %s\n" % (identify(name), code, ascii(desc)))
Tim Peters182b5ac2004-07-18 06:16:08 +00001098
Jack Jansen0ae32202003-04-09 13:25:43 +00001099 def checkforenum(self, enum):
1100 """This enum code is used by an event. Make sure it's available"""
1101 name, fullname, module = self.findcodename('enum', enum)
1102 if not name:
1103 if self.fp:
1104 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
1105 return
1106 if module:
1107 if self.fp:
1108 self.fp.write("from %s import %s\n"%(module, name))
Tim Peters182b5ac2004-07-18 06:16:08 +00001109
Jack Jansen0ae32202003-04-09 13:25:43 +00001110 def dumpindex(self):
1111 if not self.fp:
1112 return
1113 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
Tim Peters182b5ac2004-07-18 06:16:08 +00001114
Jack Jansen0ae32202003-04-09 13:25:43 +00001115 self.fp.write("_classdeclarations = {\n")
1116 classlist = self.namemappers[0].getall('class')
1117 classlist.sort()
1118 for k, v in classlist:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001119 self.fp.write(" %r : %s,\n" % (k, v))
Jack Jansen0ae32202003-04-09 13:25:43 +00001120 self.fp.write("}\n")
Tim Peters182b5ac2004-07-18 06:16:08 +00001121
Jack Jansen35760662003-04-12 22:27:11 +00001122 self.fp.write("\n_propdeclarations = {\n")
1123 proplist = self.namemappers[0].getall('property')
1124 proplist.sort()
1125 for k, v in proplist:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001126 self.fp.write(" %r : _Prop_%s,\n" % (k, v))
Jack Jansen35760662003-04-12 22:27:11 +00001127 self.fp.write("}\n")
Tim Peters182b5ac2004-07-18 06:16:08 +00001128
Jack Jansen35760662003-04-12 22:27:11 +00001129 self.fp.write("\n_compdeclarations = {\n")
1130 complist = self.namemappers[0].getall('comparison')
1131 complist.sort()
1132 for k, v in complist:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001133 self.fp.write(" %r : %s,\n" % (k, v))
Jack Jansen35760662003-04-12 22:27:11 +00001134 self.fp.write("}\n")
Tim Peters182b5ac2004-07-18 06:16:08 +00001135
Jack Jansen35760662003-04-12 22:27:11 +00001136 self.fp.write("\n_enumdeclarations = {\n")
1137 enumlist = self.namemappers[0].getall('enum')
1138 enumlist.sort()
1139 for k, v in enumlist:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001140 self.fp.write(" %r : %s,\n" % (k, v))
Jack Jansen35760662003-04-12 22:27:11 +00001141 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +00001142
1143def compiledata(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001144 [type, description, flags] = data
Walter Dörwald70a6b492004-02-12 17:35:32 +00001145 return "%r -- %r %s" % (type, description, compiledataflags(flags))
Tim Peters182b5ac2004-07-18 06:16:08 +00001146
Jack Jansen5ccd8261995-07-17 11:43:20 +00001147def is_null(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001148 return data[0] == 'null'
Tim Peters182b5ac2004-07-18 06:16:08 +00001149
Jack Jansen5ccd8261995-07-17 11:43:20 +00001150def is_optional(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001151 return (data[2] & 0x8000)
Tim Peters182b5ac2004-07-18 06:16:08 +00001152
Jack Jansen5ccd8261995-07-17 11:43:20 +00001153def is_enum(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001154 return (data[2] & 0x2000)
Tim Peters182b5ac2004-07-18 06:16:08 +00001155
Jack Jansen5ccd8261995-07-17 11:43:20 +00001156def getdatadoc(data):
Jack Jansen0ae32202003-04-09 13:25:43 +00001157 [type, descr, flags] = data
1158 if descr:
1159 return ascii(descr)
1160 if type == '****':
1161 return 'anything'
1162 if type == 'obj ':
1163 return 'an AE object reference'
Walter Dörwald70a6b492004-02-12 17:35:32 +00001164 return "undocumented, typecode %r"%(type,)
Jack Jansen5ccd8261995-07-17 11:43:20 +00001165
1166dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
1167def compiledataflags(flags):
Jack Jansen0ae32202003-04-09 13:25:43 +00001168 bits = []
1169 for i in range(16):
1170 if flags & (1<<i):
1171 if i in dataflagdict.keys():
1172 bits.append(dataflagdict[i])
1173 else:
Walter Dörwald70a6b492004-02-12 17:35:32 +00001174 bits.append(repr(i))
Jack Jansen0ae32202003-04-09 13:25:43 +00001175 return '[%s]' % string.join(bits)
Tim Peters182b5ac2004-07-18 06:16:08 +00001176
Jack Jansen8b777672002-08-07 14:49:00 +00001177def ascii(str):
Jack Jansen0ae32202003-04-09 13:25:43 +00001178 """Return a string with all non-ascii characters hex-encoded"""
1179 if type(str) != type(''):
1180 return map(ascii, str)
1181 rv = ''
1182 for c in str:
1183 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
1184 rv = rv + c
1185 else:
1186 rv = rv + '\\' + 'x%02.2x' % ord(c)
1187 return rv
Tim Peters182b5ac2004-07-18 06:16:08 +00001188
Jack Jansen5ccd8261995-07-17 11:43:20 +00001189def identify(str):
Jack Jansen0ae32202003-04-09 13:25:43 +00001190 """Turn any string into an identifier:
1191 - replace space by _
1192 - replace other illegal chars by _xx_ (hex code)
Jack Jansen36d49a92003-04-16 13:10:53 +00001193 - append _ if the result is a python keyword
Jack Jansen0ae32202003-04-09 13:25:43 +00001194 """
1195 if not str:
1196 return "empty_ae_name_"
1197 rv = ''
1198 ok = string.ascii_letters + '_'
1199 ok2 = ok + string.digits
1200 for c in str:
1201 if c in ok:
1202 rv = rv + c
1203 elif c == ' ':
1204 rv = rv + '_'
1205 else:
1206 rv = rv + '_%02.2x_'%ord(c)
1207 ok = ok2
1208 if keyword.iskeyword(rv):
1209 rv = rv + '_'
1210 return rv
Jack Jansen5ccd8261995-07-17 11:43:20 +00001211
1212# Call the main program
1213
1214if __name__ == '__main__':
Jack Jansen0ae32202003-04-09 13:25:43 +00001215 main()
1216 sys.exit(1)