blob: 1cd2ef69a5c7e8850257439c86ff6db7529dcae3 [file] [log] [blame]
Jack Jansen5ccd8261995-07-17 11:43:20 +00001"""
2gensuitemodule - Generate an AE suite module from an aete/aeut resource
3
Jack Jansen60762cb2000-08-17 22:11:45 +00004Based on aete.py.
5
6Reading and understanding this code is left as an exercise to the reader.
Jack Jansen5ccd8261995-07-17 11:43:20 +00007"""
8
9import MacOS
Jack Jansendf976ca2003-01-26 20:35:47 +000010import EasyDialogs
Jack Jansen5ccd8261995-07-17 11:43:20 +000011import os
12import string
13import sys
14import types
15import StringIO
Jack Jansenb2ecc2c2002-01-24 22:44:07 +000016import keyword
Jack Jansen40926062002-03-30 23:43:04 +000017import macresource
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000018import aetools
19import distutils.sysconfig
20import OSATerminology
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000021from Carbon.Res import *
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000022import MacOS
Jack Jansen5ccd8261995-07-17 11:43:20 +000023
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000024_MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
25DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
26DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
Jack Jansen40926062002-03-30 23:43:04 +000027
Jack Jansen5ccd8261995-07-17 11:43:20 +000028def main():
Jack Jansen40926062002-03-30 23:43:04 +000029 if len(sys.argv) > 1:
30 for filename in sys.argv[1:]:
31 processfile(filename)
32 else:
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000033 # The dialogOptionFlags below allows selection of .app bundles.
34 filename = EasyDialogs.AskFileForOpen(
35 message='Select scriptable application',
36 dialogOptionFlags=0x1056)
Jack Jansendf976ca2003-01-26 20:35:47 +000037 if not filename:
Jack Jansen40926062002-03-30 23:43:04 +000038 sys.exit(0)
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000039 try:
40 processfile(filename)
41 except MacOS.Error, arg:
42 print "Error getting terminology:", arg
43 print "Retry, manually parsing resources"
44 processfile_fromresource(filename)
Jack Jansen5ccd8261995-07-17 11:43:20 +000045
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000046def processfile_fromresource(fullname):
Jack Jansen5ccd8261995-07-17 11:43:20 +000047 """Process all resources in a single file"""
48 cur = CurResFile()
Jack Jansen40926062002-03-30 23:43:04 +000049 print "Processing", fullname
50 rf = macresource.open_pathname(fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000051 try:
52 UseResFile(rf)
53 resources = []
54 for i in range(Count1Resources('aete')):
55 res = Get1IndResource('aete', 1+i)
56 resources.append(res)
57 for i in range(Count1Resources('aeut')):
58 res = Get1IndResource('aeut', 1+i)
59 resources.append(res)
60 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Jack Jansen60762cb2000-08-17 22:11:45 +000061 aetelist = []
Jack Jansen5ccd8261995-07-17 11:43:20 +000062 for res in resources:
63 print "decoding", res.GetResInfo(), "..."
64 data = res.data
65 aete = decode(data)
Jack Jansen60762cb2000-08-17 22:11:45 +000066 aetelist.append((aete, res.GetResInfo()))
Jack Jansen5ccd8261995-07-17 11:43:20 +000067 finally:
68 if rf <> cur:
69 CloseResFile(rf)
70 UseResFile(cur)
Jack Jansen60762cb2000-08-17 22:11:45 +000071 # switch back (needed for dialogs in Python)
72 UseResFile(cur)
73 compileaetelist(aetelist, fullname)
Jack Jansen5ccd8261995-07-17 11:43:20 +000074
Jack Jansenfa1bf1c2003-03-06 23:04:38 +000075def processfile(fullname):
76 """Ask an application for its terminology and process that"""
77 aedescobj, launched = OSATerminology.GetSysTerminology(fullname)
78 if launched:
79 print "Launched", fullname
80 raw = aetools.unpack(aedescobj)
81 if not raw:
82 print 'Unpack returned empty value:', raw
83 return
84 if not raw[0].data:
85 print 'Unpack returned value without data:', raw
86 return
87 aedata = raw[0]
88 aete = decode(aedata.data)
89 compileaete(aete, None, fullname)
90
Jack Jansen60762cb2000-08-17 22:11:45 +000091def compileaetelist(aetelist, fullname):
92 for aete, resinfo in aetelist:
93 compileaete(aete, resinfo, fullname)
94
Jack Jansen5ccd8261995-07-17 11:43:20 +000095def decode(data):
96 """Decode a resource into a python data structure"""
97 f = StringIO.StringIO(data)
98 aete = generic(getaete, f)
99 aete = simplify(aete)
100 processed = f.tell()
101 unprocessed = len(f.read())
102 total = f.tell()
103 if unprocessed:
104 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
105 (processed, unprocessed, total))
106 return aete
107
108def simplify(item):
109 """Recursively replace singleton tuples by their constituent item"""
110 if type(item) is types.ListType:
111 return map(simplify, item)
112 elif type(item) == types.TupleType and len(item) == 2:
113 return simplify(item[1])
114 else:
115 return item
116
117
118# Here follows the aete resource decoder.
119# It is presented bottom-up instead of top-down because there are direct
120# references to the lower-level part-decoders from the high-level part-decoders.
121
122def getbyte(f, *args):
123 c = f.read(1)
124 if not c:
125 raise EOFError, 'in getbyte' + str(args)
126 return ord(c)
127
128def getword(f, *args):
129 getalign(f)
130 s = f.read(2)
131 if len(s) < 2:
132 raise EOFError, 'in getword' + str(args)
133 return (ord(s[0])<<8) | ord(s[1])
134
135def getlong(f, *args):
136 getalign(f)
137 s = f.read(4)
138 if len(s) < 4:
139 raise EOFError, 'in getlong' + str(args)
140 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
141
142def getostype(f, *args):
143 getalign(f)
144 s = f.read(4)
145 if len(s) < 4:
146 raise EOFError, 'in getostype' + str(args)
147 return s
148
149def getpstr(f, *args):
150 c = f.read(1)
151 if len(c) < 1:
152 raise EOFError, 'in getpstr[1]' + str(args)
153 nbytes = ord(c)
154 if nbytes == 0: return ''
155 s = f.read(nbytes)
156 if len(s) < nbytes:
157 raise EOFError, 'in getpstr[2]' + str(args)
158 return s
159
160def getalign(f):
161 if f.tell() & 1:
162 c = f.read(1)
163 ##if c <> '\0':
164 ## print 'align:', `c`
165
166def getlist(f, description, getitem):
167 count = getword(f)
168 list = []
169 for i in range(count):
170 list.append(generic(getitem, f))
171 getalign(f)
172 return list
173
174def alt_generic(what, f, *args):
175 print "generic", `what`, args
176 res = vageneric(what, f, args)
177 print '->', `res`
178 return res
179
180def generic(what, f, *args):
181 if type(what) == types.FunctionType:
182 return apply(what, (f,) + args)
183 if type(what) == types.ListType:
184 record = []
185 for thing in what:
186 item = apply(generic, thing[:1] + (f,) + thing[1:])
187 record.append((thing[1], item))
188 return record
189 return "BAD GENERIC ARGS: %s" % `what`
190
191getdata = [
192 (getostype, "type"),
193 (getpstr, "description"),
194 (getword, "flags")
195 ]
196getargument = [
197 (getpstr, "name"),
198 (getostype, "keyword"),
199 (getdata, "what")
200 ]
201getevent = [
202 (getpstr, "name"),
203 (getpstr, "description"),
204 (getostype, "suite code"),
205 (getostype, "event code"),
206 (getdata, "returns"),
207 (getdata, "accepts"),
208 (getlist, "optional arguments", getargument)
209 ]
210getproperty = [
211 (getpstr, "name"),
212 (getostype, "code"),
213 (getdata, "what")
214 ]
215getelement = [
216 (getostype, "type"),
217 (getlist, "keyform", getostype)
218 ]
219getclass = [
220 (getpstr, "name"),
221 (getostype, "class code"),
222 (getpstr, "description"),
223 (getlist, "properties", getproperty),
224 (getlist, "elements", getelement)
225 ]
226getcomparison = [
227 (getpstr, "operator name"),
228 (getostype, "operator ID"),
229 (getpstr, "operator comment"),
230 ]
231getenumerator = [
232 (getpstr, "enumerator name"),
233 (getostype, "enumerator ID"),
234 (getpstr, "enumerator comment")
235 ]
236getenumeration = [
237 (getostype, "enumeration ID"),
238 (getlist, "enumerator", getenumerator)
239 ]
240getsuite = [
241 (getpstr, "suite name"),
242 (getpstr, "suite description"),
243 (getostype, "suite ID"),
244 (getword, "suite level"),
245 (getword, "suite version"),
246 (getlist, "events", getevent),
247 (getlist, "classes", getclass),
248 (getlist, "comparisons", getcomparison),
249 (getlist, "enumerations", getenumeration)
250 ]
251getaete = [
252 (getword, "major/minor version in BCD"),
253 (getword, "language code"),
254 (getword, "script code"),
255 (getlist, "suites", getsuite)
256 ]
257
Jack Jansen60762cb2000-08-17 22:11:45 +0000258def compileaete(aete, resinfo, fname):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000259 """Generate code for a full aete resource. fname passed for doc purposes"""
260 [version, language, script, suites] = aete
261 major, minor = divmod(version, 256)
Jack Jansendf976ca2003-01-26 20:35:47 +0000262 creatorsignature, dummy = MacOS.GetCreatorAndType(fname)
Jack Jansen40926062002-03-30 23:43:04 +0000263 packagename = identify(os.path.splitext(os.path.basename(fname))[0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000264 if language:
265 packagename = packagename+'_lang%d'%language
266 if script:
267 packagename = packagename+'_script%d'%script
268 if len(packagename) > 27:
269 packagename = packagename[:27]
Jack Jansendf976ca2003-01-26 20:35:47 +0000270 pathname = EasyDialogs.AskFolder(message='Create and select package folder for %s'%packagename,
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000271 defaultLocation=DEFAULT_USER_PACKAGEFOLDER)
Jack Jansendf976ca2003-01-26 20:35:47 +0000272 if not pathname:
Jack Jansen60762cb2000-08-17 22:11:45 +0000273 return
Jack Jansen60762cb2000-08-17 22:11:45 +0000274 packagename = os.path.split(os.path.normpath(pathname))[1]
Jack Jansendf976ca2003-01-26 20:35:47 +0000275 basepkgname = EasyDialogs.AskFolder(message='Package folder for base suite (usually StdSuites)',
Jack Jansenfa1bf1c2003-03-06 23:04:38 +0000276 defaultLocation=DEFAULT_STANDARD_PACKAGEFOLDER)
Jack Jansendf976ca2003-01-26 20:35:47 +0000277 if basepkgname:
278 dirname, basepkgname = os.path.split(os.path.normpath(basepkgname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000279 if not dirname in sys.path:
280 sys.path.insert(0, dirname)
281 basepackage = __import__(basepkgname)
282 else:
283 basepackage = None
Jack Jansen60762cb2000-08-17 22:11:45 +0000284 suitelist = []
Jack Jansen12b2b762000-08-20 19:30:56 +0000285 allprecompinfo = []
286 allsuites = []
Jack Jansen5ccd8261995-07-17 11:43:20 +0000287 for suite in suites:
Jack Jansendf976ca2003-01-26 20:35:47 +0000288 code, suite, pathname, modname, precompinfo = precompilesuite(suite, basepackage)
Jack Jansen12b2b762000-08-20 19:30:56 +0000289 if not code:
290 continue
291 allprecompinfo = allprecompinfo + precompinfo
Jack Jansendf976ca2003-01-26 20:35:47 +0000292 suiteinfo = suite, pathname, modname
Jack Jansen12b2b762000-08-20 19:30:56 +0000293 suitelist.append((code, modname))
294 allsuites.append(suiteinfo)
295 for suiteinfo in allsuites:
296 compilesuite(suiteinfo, major, minor, language, script, fname, basepackage, allprecompinfo)
Jack Jansendf976ca2003-01-26 20:35:47 +0000297 initfilename = EasyDialogs.AskFileForSave(message='Package module',
298 savedFileName='__init__.py')
299 if not initfilename:
Jack Jansen60762cb2000-08-17 22:11:45 +0000300 return
Jack Jansendf976ca2003-01-26 20:35:47 +0000301 fp = open(initfilename, 'w')
302 MacOS.SetCreatorAndType(initfilename, 'Pyth', 'TEXT')
Jack Jansen60762cb2000-08-17 22:11:45 +0000303 fp.write('"""\n')
304 fp.write("Package generated from %s\n"%fname)
Jack Jansen8b777672002-08-07 14:49:00 +0000305 if resinfo:
306 fp.write("Resource %s resid %d %s\n"%(ascii(resinfo[1]), resinfo[0], ascii(resinfo[2])))
Jack Jansen60762cb2000-08-17 22:11:45 +0000307 fp.write('"""\n')
308 fp.write('import aetools\n')
Jack Jansen6aa92c52000-08-20 21:59:03 +0000309 fp.write('Error = aetools.Error\n')
Jack Jansen60762cb2000-08-17 22:11:45 +0000310 for code, modname in suitelist:
311 fp.write("import %s\n" % modname)
312 fp.write("\n\n_code_to_module = {\n")
313 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000314 fp.write("\t'%s' : %s,\n"%(ascii(code), modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000315 fp.write("}\n\n")
316 fp.write("\n\n_code_to_fullname = {\n")
317 for code, modname in suitelist:
Jack Jansen8b777672002-08-07 14:49:00 +0000318 fp.write("\t'%s' : ('%s.%s', '%s'),\n"%(ascii(code), packagename, modname, modname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000319 fp.write("}\n\n")
320 for code, modname in suitelist:
321 fp.write("from %s import *\n"%modname)
Jack Jansen8b777672002-08-07 14:49:00 +0000322
323 # Generate property dicts and element dicts for all types declared in this module
324 fp.write("def getbaseclasses(v):\n")
Jack Jansen7ff034b2002-08-07 15:52:44 +0000325 fp.write("\tif hasattr(v, '_superclassnames') and not hasattr(v, '_propdict'):\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000326 fp.write("\t\tv._propdict = {}\n")
327 fp.write("\t\tv._elemdict = {}\n")
328 fp.write("\t\tfor superclass in v._superclassnames:\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000329## fp.write("\t\t\tgetbaseclasses(superclass)\n")
330 fp.write("\t\t\tv._propdict.update(getattr(eval(superclass), '_privpropdict', {}))\n")
331 fp.write("\t\t\tv._elemdict.update(getattr(eval(superclass), '_privelemdict', {}))\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000332 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
333 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000334 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000335 fp.write("import StdSuites\n")
336 if allprecompinfo:
337 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
338 for codenamemapper in allprecompinfo:
339 for k, v in codenamemapper.getall('class'):
340 fp.write("getbaseclasses(%s)\n" % v)
341
342 # Generate a code-to-name mapper for all of the types (classes) declared in this module
343 if allprecompinfo:
344 fp.write("\n#\n# Indices of types declared in this module\n#\n")
345 fp.write("_classdeclarations = {\n")
346 for codenamemapper in allprecompinfo:
347 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000348 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000349 fp.write("}\n")
350
Jack Jansen60762cb2000-08-17 22:11:45 +0000351 if suitelist:
352 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
353 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000354 fp.write(",\n\t\t%s_Events"%modname)
355 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000356 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000357 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000358 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000359
360def precompilesuite(suite, basepackage=None):
361 """Parse a single suite without generating the output. This step is needed
362 so we can resolve recursive references by suites to enums/comps/etc declared
363 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000364 [name, desc, code, level, version, events, classes, comps, enums] = suite
365
366 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000367 if len(modname) > 28:
368 modname = modname[:27]
Jack Jansendf976ca2003-01-26 20:35:47 +0000369 pathname = EasyDialogs.AskFileForSave(message='Python output file',
370 savedFileName=modname+'.py')
371 if not pathname:
Jack Jansen12b2b762000-08-20 19:30:56 +0000372 return None, None, None, None, None
373
Jack Jansen60762cb2000-08-17 22:11:45 +0000374 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000375
376 if basepackage and basepackage._code_to_module.has_key(code):
377 # We are an extension of a baseclass (usually an application extending
378 # Standard_Suite or so). Import everything from our base module
379 basemodule = basepackage._code_to_module[code]
380 else:
381 # We are not an extension.
382 basemodule = None
383
384 enumsneeded = {}
385 for event in events:
386 findenumsinevent(event, enumsneeded)
387
388 objc = ObjectCompiler(None, basemodule)
389 for cls in classes:
390 objc.compileclass(cls)
391 for cls in classes:
392 objc.fillclasspropsandelems(cls)
393 for comp in comps:
394 objc.compilecomparison(comp)
395 for enum in enums:
396 objc.compileenumeration(enum)
397
398 for enum in enumsneeded.keys():
399 objc.checkforenum(enum)
400
401 objc.dumpindex()
402
403 precompinfo = objc.getprecompinfo(modname)
404
Jack Jansendf976ca2003-01-26 20:35:47 +0000405 return code, suite, pathname, modname, precompinfo
Jack Jansen12b2b762000-08-20 19:30:56 +0000406
Jack Jansendf976ca2003-01-26 20:35:47 +0000407def compilesuite((suite, pathname, modname), major, minor, language, script, fname, basepackage, precompinfo):
Jack Jansen12b2b762000-08-20 19:30:56 +0000408 """Generate code for a single suite"""
409 [name, desc, code, level, version, events, classes, comps, enums] = suite
410
Jack Jansendf976ca2003-01-26 20:35:47 +0000411 fp = open(pathname, 'w')
412 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000413
Jack Jansen8b777672002-08-07 14:49:00 +0000414 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000415 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000416 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000417 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
418 (major, minor, language, script))
419 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000420
421 fp.write('import aetools\n')
422 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000423 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000424 if basepackage and basepackage._code_to_module.has_key(code):
425 # We are an extension of a baseclass (usually an application extending
426 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000427 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000428 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000429 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
430 # This is needed by CodeWarrior and some others.
431 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
432 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000433 else:
434 # We are not an extension.
435 basemodule = None
436 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000437
438 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000439 if events:
440 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000441 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000442 else:
443 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000444
Jack Jansen12b2b762000-08-20 19:30:56 +0000445 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000446 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000447 objc.compileclass(cls)
448 for cls in classes:
449 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000450 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000451 objc.compilecomparison(comp)
452 for enum in enums:
453 objc.compileenumeration(enum)
454
455 for enum in enumsneeded.keys():
456 objc.checkforenum(enum)
457
458 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000459
460 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000461
Jack Jansen60762cb2000-08-17 22:11:45 +0000462def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000463 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000464 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000465 if module:
466 modshortname = string.split(module.__name__, '.')[-1]
467 baseclassname = '%s_Events'%modshortname
468 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000469 else:
470 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000471
Jack Jansen66544221997-08-08 14:49:02 +0000472def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000473 """Generate code for a single event"""
474 [name, desc, code, subcode, returns, accepts, arguments] = event
475 funcname = identify(name)
476 #
477 # generate name->keyword map
478 #
479 if arguments:
480 fp.write("\t_argmap_%s = {\n"%funcname)
481 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000482 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000483 fp.write("\t}\n\n")
484
485 #
486 # Generate function header
487 #
488 has_arg = (not is_null(accepts))
489 opt_arg = (has_arg and is_optional(accepts))
490
Jack Jansen84264771995-10-03 14:35:58 +0000491 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000492 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000493 if not opt_arg:
494 fp.write("_object, ") # Include direct object, if it has one
495 else:
496 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000497 else:
Jack Jansen84264771995-10-03 14:35:58 +0000498 fp.write("_no_object=None, ") # For argument checking
499 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000500 #
501 # Generate doc string (important, since it may be the only
502 # available documentation, due to our name-remaping)
503 #
Jack Jansen21f67582002-08-07 15:44:53 +0000504 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000505 if has_arg:
506 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
507 elif opt_arg:
508 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
509 for arg in arguments:
510 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
511 getdatadoc(arg[2])))
512 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
513 if not is_null(returns):
514 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
515 fp.write('\t\t"""\n')
516 #
517 # Fiddle the args so everything ends up in 'arguments' dictionary
518 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000519 fp.write("\t\t_code = %s\n"% `code`)
520 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000521 #
522 # Do keyword name substitution
523 #
524 if arguments:
525 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
526 else:
527 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
528 #
529 # Stuff required arg (if there is one) into arguments
530 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000531 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000532 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000533 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000534 fp.write("\t\tif _object:\n")
535 fp.write("\t\t\t_arguments['----'] = _object\n")
536 else:
537 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000538 fp.write("\n")
539 #
Jack Jansen73215141995-10-09 23:09:23 +0000540 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000541 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000542 for a in arguments:
543 if is_enum(a[2]):
544 kname = a[1]
545 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000546 if ename <> '****':
547 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000548 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000549 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000550 fp.write("\n")
551 #
552 # Do the transaction
553 #
Jack Jansen84264771995-10-03 14:35:58 +0000554 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
555 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000556 #
557 # Error handling
558 #
Jack Jansen18983532002-04-23 21:03:21 +0000559 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000560 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000561 fp.write("\t\t# XXXX Optionally decode result\n")
562 #
563 # Decode result
564 #
Jack Jansen84264771995-10-03 14:35:58 +0000565 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000566 if is_enum(returns):
567 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000568 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000569 fp.write("\n")
570
571# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
572# print "# returns", compiledata(returns)
573# print "# accepts", compiledata(accepts)
574# for arg in arguments:
575# compileargument(arg)
576
577def compileargument(arg):
578 [name, keyword, what] = arg
579 print "# %s (%s)" % (name, `keyword`), compiledata(what)
580
Jack Jansen12b2b762000-08-20 19:30:56 +0000581def findenumsinevent(event, enumsneeded):
582 """Find all enums for a single event"""
583 [name, desc, code, subcode, returns, accepts, arguments] = event
584 for a in arguments:
585 if is_enum(a[2]):
586 ename = a[2][0]
587 if ename <> '****':
588 enumsneeded[ename] = 1
589
590#
591# This class stores the code<->name translations for a single module. It is used
592# to keep the information while we're compiling the module, but we also keep these objects
593# around so if one suite refers to, say, an enum in another suite we know where to
594# find it. Finally, if we really can't find a code, the user can add modules by
595# hand.
596#
597class CodeNameMapper:
598
599 def __init__(self):
600 self.code2name = {
601 "property" : {},
602 "class" : {},
603 "enum" : {},
604 "comparison" : {},
605 }
606 self.name2code = {
607 "property" : {},
608 "class" : {},
609 "enum" : {},
610 "comparison" : {},
611 }
612 self.modulename = None
613 self.star_imported = 0
614
615 def addnamecode(self, type, name, code):
616 self.name2code[type][name] = code
617 if not self.code2name[type].has_key(code):
618 self.code2name[type][code] = name
619
620 def hasname(self, type, name):
621 return self.name2code[type].has_key(name)
622
623 def hascode(self, type, code):
624 return self.code2name[type].has_key(code)
625
626 def findcodename(self, type, code):
627 if not self.hascode(type, code):
628 return None, None, None
629 name = self.code2name[type][code]
630 if self.modulename and not self.star_imported:
631 qualname = '%s.%s'%(self.modulename, name)
632 else:
633 qualname = name
634 return name, qualname, self.modulename
635
636 def getall(self, type):
637 return self.code2name[type].items()
638
639 def addmodule(self, module, name, star_imported):
640 self.modulename = name
641 self.star_imported = star_imported
642 for code, name in module._propdeclarations.items():
643 self.addnamecode('property', name, code)
644 for code, name in module._classdeclarations.items():
645 self.addnamecode('class', name, code)
646 for code in module._enumdeclarations.keys():
647 self.addnamecode('enum', '_Enum_'+identify(code), code)
648 for code, name in module._compdeclarations.items():
649 self.addnamecode('comparison', name, code)
650
651 def prepareforexport(self, name=None):
652 if not self.modulename:
653 self.modulename = name
654 return self
655
Jack Jansen66544221997-08-08 14:49:02 +0000656class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000657 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000658 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000659 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000660 self.namemappers = [CodeNameMapper()]
661 if othernamemappers:
662 self.othernamemappers = othernamemappers[:]
663 else:
664 self.othernamemappers = []
665 if basesuite:
666 basemapper = CodeNameMapper()
667 basemapper.addmodule(basesuite, '', 1)
668 self.namemappers.append(basemapper)
669
670 def getprecompinfo(self, modname):
671 list = []
672 for mapper in self.namemappers:
673 emapper = mapper.prepareforexport(modname)
674 if emapper:
675 list.append(emapper)
676 return list
Jack Jansen66544221997-08-08 14:49:02 +0000677
678 def findcodename(self, type, code):
679 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000680 # First try: check whether we already know about this code.
681 for mapper in self.namemappers:
682 if mapper.hascode(type, code):
683 return mapper.findcodename(type, code)
684 # Second try: maybe one of the other modules knows about it.
685 for mapper in self.othernamemappers:
686 if mapper.hascode(type, code):
687 self.othernamemappers.remove(mapper)
688 self.namemappers.append(mapper)
689 if self.fp:
690 self.fp.write("import %s\n"%mapper.modulename)
691 break
692 else:
693 # If all this has failed we ask the user for a guess on where it could
694 # be and retry.
695 if self.fp:
696 m = self.askdefinitionmodule(type, code)
697 else:
698 m = None
699 if not m: return None, None, None
700 mapper = CodeNameMapper()
701 mapper.addmodule(m, m.__name__, 0)
702 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000703
704 def askdefinitionmodule(self, type, code):
Jack Jansendf976ca2003-01-26 20:35:47 +0000705 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
706 if not path: return
707 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000708 modname = os.path.splitext(file)[0]
709 if not path in sys.path:
710 sys.path.insert(0, path)
711 m = __import__(modname)
712 self.fp.write("import %s\n"%modname)
713 return m
714
715 def compileclass(self, cls):
716 [name, code, desc, properties, elements] = cls
717 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000718 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000719 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000720 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
721 if self.fp:
722 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000723 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000724 if self.fp:
725 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000726 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000727 self.fp.write('\twant = %s\n' % `code`)
728 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000729 for prop in properties:
730 self.compileproperty(prop)
731 for elem in elements:
732 self.compileelement(elem)
733
734 def compileproperty(self, prop):
735 [name, code, what] = prop
736 if code == 'c@#!':
737 # Something silly with plurals. Skip it.
738 return
739 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000740 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000741 # plural forms and such
742 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000743 if pname == othername:
744 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000745 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000746 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000747 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000748 if self.fp:
749 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000750 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000751 self.fp.write("\twhich = %s\n" % `code`)
752 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000753 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000754
755 def compileelement(self, elem):
756 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000757 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000758 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000759
Jack Jansen66544221997-08-08 14:49:02 +0000760 def fillclasspropsandelems(self, cls):
761 [name, code, desc, properties, elements] = cls
762 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000763 if self.namemappers[0].hascode('class', code) and \
764 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000765 # This is an other name (plural or so) for something else. Skip.
766 return
767 plist = []
768 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000769 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000770 for prop in properties:
771 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000772 if pcode == "c@#^":
773 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000774 if pcode == 'c@#!':
775 continue
776 pname = identify(pname)
777 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000778
779 superclassnames = []
780 for superclass in superclasses:
781 superId, superDesc, dummy = superclass
782 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
783 superclassnames.append(superclassname)
784
785 if self.fp:
786 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
787
Jack Jansen66544221997-08-08 14:49:02 +0000788 for elem in elements:
789 [ecode, keyform] = elem
790 if ecode == 'c@#!':
791 continue
792 name, ename, module = self.findcodename('class', ecode)
793 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000794 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000795 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000796 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000797 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000798
799 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000800 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000801 for n in plist:
802 self.fp.write("\t'%s' : %s,\n"%(n, n))
803 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000804 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000805 for n, fulln in elist:
806 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
807 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000808
809 def compilecomparison(self, comp):
810 [name, code, comment] = comp
811 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000812 self.namemappers[0].addnamecode('comparison', iname, code)
813 if self.fp:
814 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000815 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000816
817 def compileenumeration(self, enum):
818 [code, items] = enum
819 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000820 if self.fp:
821 self.fp.write("%s = {\n" % name)
822 for item in items:
823 self.compileenumerator(item)
824 self.fp.write("}\n\n")
825 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000826 return code
827
828 def compileenumerator(self, item):
829 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +0000830 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +0000831
832 def checkforenum(self, enum):
833 """This enum code is used by an event. Make sure it's available"""
834 name, fullname, module = self.findcodename('enum', enum)
835 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000836 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000837 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000838 return
839 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000840 if self.fp:
841 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000842
843 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000844 if not self.fp:
845 return
Jack Jansen66544221997-08-08 14:49:02 +0000846 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
847 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000848 for k, v in self.namemappers[0].getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000849 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000850 self.fp.write("}\n")
851 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000852 for k, v in self.namemappers[0].getall('property'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000853 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000854 self.fp.write("}\n")
855 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000856 for k, v in self.namemappers[0].getall('comparison'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000857 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000858 self.fp.write("}\n")
859 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000860 for k, v in self.namemappers[0].getall('enum'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000861 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000862 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000863
864def compiledata(data):
865 [type, description, flags] = data
866 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
867
868def is_null(data):
869 return data[0] == 'null'
870
871def is_optional(data):
872 return (data[2] & 0x8000)
873
874def is_enum(data):
875 return (data[2] & 0x2000)
876
877def getdatadoc(data):
878 [type, descr, flags] = data
879 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +0000880 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000881 if type == '****':
882 return 'anything'
883 if type == 'obj ':
884 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000885 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000886
887dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
888def compiledataflags(flags):
889 bits = []
890 for i in range(16):
891 if flags & (1<<i):
892 if i in dataflagdict.keys():
893 bits.append(dataflagdict[i])
894 else:
895 bits.append(`i`)
896 return '[%s]' % string.join(bits)
897
Jack Jansen8b777672002-08-07 14:49:00 +0000898def ascii(str):
899 """Return a string with all non-ascii characters hex-encoded"""
900 if type(str) != type(''):
901 return map(ascii, str)
902 rv = ''
903 for c in str:
904 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
905 rv = rv + c
906 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000907 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +0000908 return rv
909
Jack Jansen5ccd8261995-07-17 11:43:20 +0000910def identify(str):
911 """Turn any string into an identifier:
912 - replace space by _
913 - replace other illegal chars by _xx_ (hex code)
914 - prepend _ if the result is a python keyword
915 """
Jack Jansen66544221997-08-08 14:49:02 +0000916 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +0000917 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000918 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000919 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000920 ok2 = ok + string.digits
921 for c in str:
922 if c in ok:
923 rv = rv + c
924 elif c == ' ':
925 rv = rv + '_'
926 else:
927 rv = rv + '_%02.2x_'%ord(c)
928 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000929 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +0000930 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000931 return rv
932
933# Call the main program
934
935if __name__ == '__main__':
936 main()
937 sys.exit(1)