blob: cd725d4a0b370bb35589d478baf237098f07bc20 [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
Jack Jansen7e0bc112003-03-21 12:04:19 +0000324 fp.write("\ndef getbaseclasses(v):\n")
325 fp.write("\tif not v._propdict:\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000326 fp.write("\t\tv._propdict = {}\n")
327 fp.write("\t\tv._elemdict = {}\n")
Jack Jansen7e0bc112003-03-21 12:04:19 +0000328 fp.write("\t\tfor superclassname in getattr(v, '_superclassnames', []):\n")
329 fp.write("\t\t\tsuperclass = eval(superclassname)\n")
330 fp.write("\t\t\tgetbaseclasses(superclass)\n")
331 fp.write("\t\t\tv._propdict.update(getattr(superclass, '_propdict', {}))\n")
332 fp.write("\t\t\tv._elemdict.update(getattr(superclass, '_elemdict', {}))\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000333 fp.write("\t\tv._propdict.update(v._privpropdict)\n")
334 fp.write("\t\tv._elemdict.update(v._privelemdict)\n")
Jack Jansen21f67582002-08-07 15:44:53 +0000335 fp.write("\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000336 fp.write("import StdSuites\n")
337 if allprecompinfo:
338 fp.write("\n#\n# Set property and element dictionaries now that all classes have been defined\n#\n")
339 for codenamemapper in allprecompinfo:
340 for k, v in codenamemapper.getall('class'):
341 fp.write("getbaseclasses(%s)\n" % v)
342
343 # Generate a code-to-name mapper for all of the types (classes) declared in this module
344 if allprecompinfo:
345 fp.write("\n#\n# Indices of types declared in this module\n#\n")
346 fp.write("_classdeclarations = {\n")
347 for codenamemapper in allprecompinfo:
348 for k, v in codenamemapper.getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000349 fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen8b777672002-08-07 14:49:00 +0000350 fp.write("}\n")
351
Jack Jansen60762cb2000-08-17 22:11:45 +0000352 if suitelist:
353 fp.write("\n\nclass %s(%s_Events"%(packagename, suitelist[0][1]))
354 for code, modname in suitelist[1:]:
Jack Jansen12b2b762000-08-20 19:30:56 +0000355 fp.write(",\n\t\t%s_Events"%modname)
356 fp.write(",\n\t\taetools.TalkTo):\n")
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000357 fp.write("\t_signature = %s\n\n"%`creatorsignature`)
Jack Jansen8b777672002-08-07 14:49:00 +0000358 fp.write("\t_moduleName = '%s'\n\n"%packagename)
Jack Jansen60762cb2000-08-17 22:11:45 +0000359 fp.close()
Jack Jansen12b2b762000-08-20 19:30:56 +0000360
361def precompilesuite(suite, basepackage=None):
362 """Parse a single suite without generating the output. This step is needed
363 so we can resolve recursive references by suites to enums/comps/etc declared
364 in other suites"""
Jack Jansen5ccd8261995-07-17 11:43:20 +0000365 [name, desc, code, level, version, events, classes, comps, enums] = suite
366
367 modname = identify(name)
Jack Jansen60762cb2000-08-17 22:11:45 +0000368 if len(modname) > 28:
369 modname = modname[:27]
Jack Jansendf976ca2003-01-26 20:35:47 +0000370 pathname = EasyDialogs.AskFileForSave(message='Python output file',
371 savedFileName=modname+'.py')
372 if not pathname:
Jack Jansen12b2b762000-08-20 19:30:56 +0000373 return None, None, None, None, None
374
Jack Jansen60762cb2000-08-17 22:11:45 +0000375 modname = os.path.splitext(os.path.split(pathname)[1])[0]
Jack Jansen12b2b762000-08-20 19:30:56 +0000376
377 if basepackage and basepackage._code_to_module.has_key(code):
378 # We are an extension of a baseclass (usually an application extending
379 # Standard_Suite or so). Import everything from our base module
380 basemodule = basepackage._code_to_module[code]
381 else:
382 # We are not an extension.
383 basemodule = None
384
385 enumsneeded = {}
386 for event in events:
387 findenumsinevent(event, enumsneeded)
388
389 objc = ObjectCompiler(None, basemodule)
390 for cls in classes:
391 objc.compileclass(cls)
392 for cls in classes:
393 objc.fillclasspropsandelems(cls)
394 for comp in comps:
395 objc.compilecomparison(comp)
396 for enum in enums:
397 objc.compileenumeration(enum)
398
399 for enum in enumsneeded.keys():
400 objc.checkforenum(enum)
401
402 objc.dumpindex()
403
404 precompinfo = objc.getprecompinfo(modname)
405
Jack Jansendf976ca2003-01-26 20:35:47 +0000406 return code, suite, pathname, modname, precompinfo
Jack Jansen12b2b762000-08-20 19:30:56 +0000407
Jack Jansendf976ca2003-01-26 20:35:47 +0000408def compilesuite((suite, pathname, modname), major, minor, language, script, fname, basepackage, precompinfo):
Jack Jansen12b2b762000-08-20 19:30:56 +0000409 """Generate code for a single suite"""
410 [name, desc, code, level, version, events, classes, comps, enums] = suite
411
Jack Jansendf976ca2003-01-26 20:35:47 +0000412 fp = open(pathname, 'w')
413 MacOS.SetCreatorAndType(pathname, 'Pyth', 'TEXT')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000414
Jack Jansen8b777672002-08-07 14:49:00 +0000415 fp.write('"""Suite %s: %s\n' % (ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000416 fp.write("Level %d, version %d\n\n" % (level, version))
Jack Jansen8b777672002-08-07 14:49:00 +0000417 fp.write("Generated from %s\n"%ascii(fname))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000418 fp.write("AETE/AEUT resource version %d/%d, language %d, script %d\n" % \
419 (major, minor, language, script))
420 fp.write('"""\n\n')
Jack Jansen5ccd8261995-07-17 11:43:20 +0000421
422 fp.write('import aetools\n')
423 fp.write('import MacOS\n\n')
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000424 fp.write("_code = %s\n\n"% `code`)
Jack Jansen60762cb2000-08-17 22:11:45 +0000425 if basepackage and basepackage._code_to_module.has_key(code):
426 # We are an extension of a baseclass (usually an application extending
427 # Standard_Suite or so). Import everything from our base module
Jack Jansen12b2b762000-08-20 19:30:56 +0000428 fp.write('from %s import *\n'%basepackage._code_to_fullname[code][0])
Jack Jansen60762cb2000-08-17 22:11:45 +0000429 basemodule = basepackage._code_to_module[code]
Jack Jansen7ebcbf52002-01-22 23:24:03 +0000430 elif basepackage and basepackage._code_to_module.has_key(code.lower()):
431 # This is needed by CodeWarrior and some others.
432 fp.write('from %s import *\n'%basepackage._code_to_fullname[code.lower()][0])
433 basemodule = basepackage._code_to_module[code.lower()]
Jack Jansen60762cb2000-08-17 22:11:45 +0000434 else:
435 # We are not an extension.
436 basemodule = None
437 compileclassheader(fp, modname, basemodule)
Jack Jansen66544221997-08-08 14:49:02 +0000438
439 enumsneeded = {}
Jack Jansen5ccd8261995-07-17 11:43:20 +0000440 if events:
441 for event in events:
Jack Jansen66544221997-08-08 14:49:02 +0000442 compileevent(fp, event, enumsneeded)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000443 else:
444 fp.write("\tpass\n\n")
Jack Jansen66544221997-08-08 14:49:02 +0000445
Jack Jansen12b2b762000-08-20 19:30:56 +0000446 objc = ObjectCompiler(fp, basemodule, precompinfo)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000447 for cls in classes:
Jack Jansen66544221997-08-08 14:49:02 +0000448 objc.compileclass(cls)
449 for cls in classes:
450 objc.fillclasspropsandelems(cls)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000451 for comp in comps:
Jack Jansen66544221997-08-08 14:49:02 +0000452 objc.compilecomparison(comp)
453 for enum in enums:
454 objc.compileenumeration(enum)
455
456 for enum in enumsneeded.keys():
457 objc.checkforenum(enum)
458
459 objc.dumpindex()
Jack Jansen60762cb2000-08-17 22:11:45 +0000460
461 return code, modname
Jack Jansen5ccd8261995-07-17 11:43:20 +0000462
Jack Jansen60762cb2000-08-17 22:11:45 +0000463def compileclassheader(fp, name, module=None):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000464 """Generate class boilerplate"""
Jack Jansen60762cb2000-08-17 22:11:45 +0000465 classname = '%s_Events'%name
Jack Jansen12b2b762000-08-20 19:30:56 +0000466 if module:
467 modshortname = string.split(module.__name__, '.')[-1]
468 baseclassname = '%s_Events'%modshortname
469 fp.write("class %s(%s):\n\n"%(classname, baseclassname))
Jack Jansen60762cb2000-08-17 22:11:45 +0000470 else:
471 fp.write("class %s:\n\n"%classname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000472
Jack Jansen66544221997-08-08 14:49:02 +0000473def compileevent(fp, event, enumsneeded):
Jack Jansen5ccd8261995-07-17 11:43:20 +0000474 """Generate code for a single event"""
475 [name, desc, code, subcode, returns, accepts, arguments] = event
476 funcname = identify(name)
477 #
478 # generate name->keyword map
479 #
480 if arguments:
481 fp.write("\t_argmap_%s = {\n"%funcname)
482 for a in arguments:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000483 fp.write("\t\t%s : %s,\n"%(`identify(a[0])`, `a[1]`))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000484 fp.write("\t}\n\n")
485
486 #
487 # Generate function header
488 #
489 has_arg = (not is_null(accepts))
490 opt_arg = (has_arg and is_optional(accepts))
491
Jack Jansen84264771995-10-03 14:35:58 +0000492 fp.write("\tdef %s(self, "%funcname)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000493 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000494 if not opt_arg:
495 fp.write("_object, ") # Include direct object, if it has one
496 else:
497 fp.write("_object=None, ") # Also include if it is optional
Jack Jansen5ccd8261995-07-17 11:43:20 +0000498 else:
Jack Jansen84264771995-10-03 14:35:58 +0000499 fp.write("_no_object=None, ") # For argument checking
500 fp.write("_attributes={}, **_arguments):\n") # include attribute dict and args
Jack Jansen5ccd8261995-07-17 11:43:20 +0000501 #
502 # Generate doc string (important, since it may be the only
503 # available documentation, due to our name-remaping)
504 #
Jack Jansen21f67582002-08-07 15:44:53 +0000505 fp.write('\t\t"""%s: %s\n'%(ascii(name), ascii(desc)))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000506 if has_arg:
507 fp.write("\t\tRequired argument: %s\n"%getdatadoc(accepts))
508 elif opt_arg:
509 fp.write("\t\tOptional argument: %s\n"%getdatadoc(accepts))
510 for arg in arguments:
511 fp.write("\t\tKeyword argument %s: %s\n"%(identify(arg[0]),
512 getdatadoc(arg[2])))
513 fp.write("\t\tKeyword argument _attributes: AppleEvent attribute dictionary\n")
514 if not is_null(returns):
515 fp.write("\t\tReturns: %s\n"%getdatadoc(returns))
516 fp.write('\t\t"""\n')
517 #
518 # Fiddle the args so everything ends up in 'arguments' dictionary
519 #
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000520 fp.write("\t\t_code = %s\n"% `code`)
521 fp.write("\t\t_subcode = %s\n\n"% `subcode`)
Jack Jansen73215141995-10-09 23:09:23 +0000522 #
523 # Do keyword name substitution
524 #
525 if arguments:
526 fp.write("\t\taetools.keysubst(_arguments, self._argmap_%s)\n"%funcname)
527 else:
528 fp.write("\t\tif _arguments: raise TypeError, 'No optional args expected'\n")
529 #
530 # Stuff required arg (if there is one) into arguments
531 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000532 if has_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000533 fp.write("\t\t_arguments['----'] = _object\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000534 elif opt_arg:
Jack Jansen84264771995-10-03 14:35:58 +0000535 fp.write("\t\tif _object:\n")
536 fp.write("\t\t\t_arguments['----'] = _object\n")
537 else:
538 fp.write("\t\tif _no_object != None: raise TypeError, 'No direct arg expected'\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000539 fp.write("\n")
540 #
Jack Jansen73215141995-10-09 23:09:23 +0000541 # Do enum-name substitution
Jack Jansen5ccd8261995-07-17 11:43:20 +0000542 #
Jack Jansen5ccd8261995-07-17 11:43:20 +0000543 for a in arguments:
544 if is_enum(a[2]):
545 kname = a[1]
546 ename = a[2][0]
Jack Jansena2408e91996-04-16 14:36:46 +0000547 if ename <> '****':
548 fp.write("\t\taetools.enumsubst(_arguments, %s, _Enum_%s)\n" %
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000549 (`kname`, identify(ename)))
Jack Jansen66544221997-08-08 14:49:02 +0000550 enumsneeded[ename] = 1
Jack Jansen5ccd8261995-07-17 11:43:20 +0000551 fp.write("\n")
552 #
553 # Do the transaction
554 #
Jack Jansen84264771995-10-03 14:35:58 +0000555 fp.write("\t\t_reply, _arguments, _attributes = self.send(_code, _subcode,\n")
556 fp.write("\t\t\t\t_arguments, _attributes)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000557 #
558 # Error handling
559 #
Jack Jansen18983532002-04-23 21:03:21 +0000560 fp.write("\t\tif _arguments.get('errn', 0):\n")
Jack Jansen977fbf21996-09-20 15:29:59 +0000561 fp.write("\t\t\traise aetools.Error, aetools.decodeerror(_arguments)\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000562 fp.write("\t\t# XXXX Optionally decode result\n")
563 #
564 # Decode result
565 #
Jack Jansen84264771995-10-03 14:35:58 +0000566 fp.write("\t\tif _arguments.has_key('----'):\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000567 if is_enum(returns):
568 fp.write("\t\t\t# XXXX Should do enum remapping here...\n")
Jack Jansen84264771995-10-03 14:35:58 +0000569 fp.write("\t\t\treturn _arguments['----']\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000570 fp.write("\n")
571
572# print "\n# Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
573# print "# returns", compiledata(returns)
574# print "# accepts", compiledata(accepts)
575# for arg in arguments:
576# compileargument(arg)
577
578def compileargument(arg):
579 [name, keyword, what] = arg
580 print "# %s (%s)" % (name, `keyword`), compiledata(what)
581
Jack Jansen12b2b762000-08-20 19:30:56 +0000582def findenumsinevent(event, enumsneeded):
583 """Find all enums for a single event"""
584 [name, desc, code, subcode, returns, accepts, arguments] = event
585 for a in arguments:
586 if is_enum(a[2]):
587 ename = a[2][0]
588 if ename <> '****':
589 enumsneeded[ename] = 1
590
591#
592# This class stores the code<->name translations for a single module. It is used
593# to keep the information while we're compiling the module, but we also keep these objects
594# around so if one suite refers to, say, an enum in another suite we know where to
595# find it. Finally, if we really can't find a code, the user can add modules by
596# hand.
597#
598class CodeNameMapper:
599
600 def __init__(self):
601 self.code2name = {
602 "property" : {},
603 "class" : {},
604 "enum" : {},
605 "comparison" : {},
606 }
607 self.name2code = {
608 "property" : {},
609 "class" : {},
610 "enum" : {},
611 "comparison" : {},
612 }
613 self.modulename = None
614 self.star_imported = 0
615
616 def addnamecode(self, type, name, code):
617 self.name2code[type][name] = code
618 if not self.code2name[type].has_key(code):
619 self.code2name[type][code] = name
620
621 def hasname(self, type, name):
622 return self.name2code[type].has_key(name)
623
624 def hascode(self, type, code):
625 return self.code2name[type].has_key(code)
626
627 def findcodename(self, type, code):
628 if not self.hascode(type, code):
629 return None, None, None
630 name = self.code2name[type][code]
631 if self.modulename and not self.star_imported:
632 qualname = '%s.%s'%(self.modulename, name)
633 else:
634 qualname = name
635 return name, qualname, self.modulename
636
637 def getall(self, type):
638 return self.code2name[type].items()
639
640 def addmodule(self, module, name, star_imported):
641 self.modulename = name
642 self.star_imported = star_imported
643 for code, name in module._propdeclarations.items():
644 self.addnamecode('property', name, code)
645 for code, name in module._classdeclarations.items():
646 self.addnamecode('class', name, code)
647 for code in module._enumdeclarations.keys():
648 self.addnamecode('enum', '_Enum_'+identify(code), code)
649 for code, name in module._compdeclarations.items():
650 self.addnamecode('comparison', name, code)
651
652 def prepareforexport(self, name=None):
653 if not self.modulename:
654 self.modulename = name
655 return self
656
Jack Jansen66544221997-08-08 14:49:02 +0000657class ObjectCompiler:
Jack Jansen12b2b762000-08-20 19:30:56 +0000658 def __init__(self, fp, basesuite=None, othernamemappers=None):
Jack Jansen66544221997-08-08 14:49:02 +0000659 self.fp = fp
Jack Jansen60762cb2000-08-17 22:11:45 +0000660 self.basesuite = basesuite
Jack Jansen12b2b762000-08-20 19:30:56 +0000661 self.namemappers = [CodeNameMapper()]
662 if othernamemappers:
663 self.othernamemappers = othernamemappers[:]
664 else:
665 self.othernamemappers = []
666 if basesuite:
667 basemapper = CodeNameMapper()
668 basemapper.addmodule(basesuite, '', 1)
669 self.namemappers.append(basemapper)
670
671 def getprecompinfo(self, modname):
672 list = []
673 for mapper in self.namemappers:
674 emapper = mapper.prepareforexport(modname)
675 if emapper:
676 list.append(emapper)
677 return list
Jack Jansen66544221997-08-08 14:49:02 +0000678
679 def findcodename(self, type, code):
680 while 1:
Jack Jansen12b2b762000-08-20 19:30:56 +0000681 # First try: check whether we already know about this code.
682 for mapper in self.namemappers:
683 if mapper.hascode(type, code):
684 return mapper.findcodename(type, code)
685 # Second try: maybe one of the other modules knows about it.
686 for mapper in self.othernamemappers:
687 if mapper.hascode(type, code):
688 self.othernamemappers.remove(mapper)
689 self.namemappers.append(mapper)
690 if self.fp:
691 self.fp.write("import %s\n"%mapper.modulename)
692 break
693 else:
694 # If all this has failed we ask the user for a guess on where it could
695 # be and retry.
696 if self.fp:
697 m = self.askdefinitionmodule(type, code)
698 else:
699 m = None
700 if not m: return None, None, None
701 mapper = CodeNameMapper()
702 mapper.addmodule(m, m.__name__, 0)
703 self.namemappers.append(mapper)
Jack Jansen66544221997-08-08 14:49:02 +0000704
705 def askdefinitionmodule(self, type, code):
Jack Jansendf976ca2003-01-26 20:35:47 +0000706 path = EasyDialogs.AskFileForSave(message='Where is %s %s declared?'%(type, code))
707 if not path: return
708 path, file = os.path.split(path)
Jack Jansen66544221997-08-08 14:49:02 +0000709 modname = os.path.splitext(file)[0]
710 if not path in sys.path:
711 sys.path.insert(0, path)
712 m = __import__(modname)
713 self.fp.write("import %s\n"%modname)
714 return m
715
716 def compileclass(self, cls):
717 [name, code, desc, properties, elements] = cls
718 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000719 if self.namemappers[0].hascode('class', code):
Jack Jansen66544221997-08-08 14:49:02 +0000720 # plural forms and such
Jack Jansen12b2b762000-08-20 19:30:56 +0000721 othername, dummy, dummy = self.namemappers[0].findcodename('class', code)
722 if self.fp:
723 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000724 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000725 if self.fp:
726 self.fp.write('\nclass %s(aetools.ComponentItem):\n' % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000727 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(desc)))
Jack Jansen12b2b762000-08-20 19:30:56 +0000728 self.fp.write('\twant = %s\n' % `code`)
729 self.namemappers[0].addnamecode('class', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000730 for prop in properties:
731 self.compileproperty(prop)
732 for elem in elements:
733 self.compileelement(elem)
734
735 def compileproperty(self, prop):
736 [name, code, what] = prop
737 if code == 'c@#!':
738 # Something silly with plurals. Skip it.
739 return
740 pname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000741 if self.namemappers[0].hascode('property', code):
Jack Jansen2eda2442000-08-20 19:42:52 +0000742 # plural forms and such
743 othername, dummy, dummy = self.namemappers[0].findcodename('property', code)
Jack Jansen9d6d2c02000-08-22 20:34:35 +0000744 if pname == othername:
745 return
Jack Jansen12b2b762000-08-20 19:30:56 +0000746 if self.fp:
Jack Jansen2eda2442000-08-20 19:42:52 +0000747 self.fp.write("\n%s = %s\n"%(pname, othername))
Jack Jansen66544221997-08-08 14:49:02 +0000748 else:
Jack Jansen12b2b762000-08-20 19:30:56 +0000749 if self.fp:
750 self.fp.write("class %s(aetools.NProperty):\n" % pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000751 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(what[1])))
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000752 self.fp.write("\twhich = %s\n" % `code`)
753 self.fp.write("\twant = %s\n" % `what[0]`)
Jack Jansen2eda2442000-08-20 19:42:52 +0000754 self.namemappers[0].addnamecode('property', pname, code)
Jack Jansen66544221997-08-08 14:49:02 +0000755
756 def compileelement(self, elem):
757 [code, keyform] = elem
Jack Jansen12b2b762000-08-20 19:30:56 +0000758 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000759 self.fp.write("# element %s as %s\n" % (`code`, keyform))
Jack Jansen5ccd8261995-07-17 11:43:20 +0000760
Jack Jansen66544221997-08-08 14:49:02 +0000761 def fillclasspropsandelems(self, cls):
762 [name, code, desc, properties, elements] = cls
763 cname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000764 if self.namemappers[0].hascode('class', code) and \
765 self.namemappers[0].findcodename('class', code)[0] != cname:
Jack Jansen66544221997-08-08 14:49:02 +0000766 # This is an other name (plural or so) for something else. Skip.
767 return
768 plist = []
769 elist = []
Jack Jansen8b777672002-08-07 14:49:00 +0000770 superclasses = []
Jack Jansen66544221997-08-08 14:49:02 +0000771 for prop in properties:
772 [pname, pcode, what] = prop
Jack Jansen8b777672002-08-07 14:49:00 +0000773 if pcode == "c@#^":
774 superclasses.append(what)
Jack Jansen66544221997-08-08 14:49:02 +0000775 if pcode == 'c@#!':
776 continue
777 pname = identify(pname)
778 plist.append(pname)
Jack Jansen8b777672002-08-07 14:49:00 +0000779
780 superclassnames = []
781 for superclass in superclasses:
782 superId, superDesc, dummy = superclass
783 superclassname, fullyqualifiedname, module = self.findcodename("class", superId)
784 superclassnames.append(superclassname)
785
786 if self.fp:
787 self.fp.write("%s._superclassnames = %s\n"%(cname, `superclassnames`))
788
Jack Jansen66544221997-08-08 14:49:02 +0000789 for elem in elements:
790 [ecode, keyform] = elem
791 if ecode == 'c@#!':
792 continue
793 name, ename, module = self.findcodename('class', ecode)
794 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000795 if self.fp:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000796 self.fp.write("# XXXX %s element %s not found!!\n"%(cname, `ecode`))
Jack Jansen66544221997-08-08 14:49:02 +0000797 else:
Jack Jansen34d11f02000-03-07 23:40:13 +0000798 elist.append((name, ename))
Jack Jansen12b2b762000-08-20 19:30:56 +0000799
800 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000801 self.fp.write("%s._privpropdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000802 for n in plist:
803 self.fp.write("\t'%s' : %s,\n"%(n, n))
804 self.fp.write("}\n")
Jack Jansen8b777672002-08-07 14:49:00 +0000805 self.fp.write("%s._privelemdict = {\n"%cname)
Jack Jansen12b2b762000-08-20 19:30:56 +0000806 for n, fulln in elist:
807 self.fp.write("\t'%s' : %s,\n"%(n, fulln))
808 self.fp.write("}\n")
Jack Jansen66544221997-08-08 14:49:02 +0000809
810 def compilecomparison(self, comp):
811 [name, code, comment] = comp
812 iname = identify(name)
Jack Jansen12b2b762000-08-20 19:30:56 +0000813 self.namemappers[0].addnamecode('comparison', iname, code)
814 if self.fp:
815 self.fp.write("class %s(aetools.NComparison):\n" % iname)
Jack Jansen8b777672002-08-07 14:49:00 +0000816 self.fp.write('\t"""%s - %s """\n' % (ascii(name), ascii(comment)))
Jack Jansen66544221997-08-08 14:49:02 +0000817
818 def compileenumeration(self, enum):
819 [code, items] = enum
820 name = "_Enum_%s" % identify(code)
Jack Jansen12b2b762000-08-20 19:30:56 +0000821 if self.fp:
822 self.fp.write("%s = {\n" % name)
823 for item in items:
824 self.compileenumerator(item)
825 self.fp.write("}\n\n")
826 self.namemappers[0].addnamecode('enum', name, code)
Jack Jansen66544221997-08-08 14:49:02 +0000827 return code
828
829 def compileenumerator(self, item):
830 [name, code, desc] = item
Jack Jansen21f67582002-08-07 15:44:53 +0000831 self.fp.write("\t%s : %s,\t# %s\n" % (`identify(name)`, `code`, ascii(desc)))
Jack Jansen66544221997-08-08 14:49:02 +0000832
833 def checkforenum(self, enum):
834 """This enum code is used by an event. Make sure it's available"""
835 name, fullname, module = self.findcodename('enum', enum)
836 if not name:
Jack Jansen12b2b762000-08-20 19:30:56 +0000837 if self.fp:
Jack Jansen8b777672002-08-07 14:49:00 +0000838 self.fp.write("_Enum_%s = None # XXXX enum %s not found!!\n"%(identify(enum), ascii(enum)))
Jack Jansen66544221997-08-08 14:49:02 +0000839 return
840 if module:
Jack Jansen12b2b762000-08-20 19:30:56 +0000841 if self.fp:
842 self.fp.write("from %s import %s\n"%(module, name))
Jack Jansen66544221997-08-08 14:49:02 +0000843
844 def dumpindex(self):
Jack Jansen12b2b762000-08-20 19:30:56 +0000845 if not self.fp:
846 return
Jack Jansen66544221997-08-08 14:49:02 +0000847 self.fp.write("\n#\n# Indices of types declared in this module\n#\n")
848 self.fp.write("_classdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000849 for k, v in self.namemappers[0].getall('class'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000850 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000851 self.fp.write("}\n")
852 self.fp.write("\n_propdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000853 for k, v in self.namemappers[0].getall('property'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000854 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000855 self.fp.write("}\n")
856 self.fp.write("\n_compdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000857 for k, v in self.namemappers[0].getall('comparison'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000858 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000859 self.fp.write("}\n")
860 self.fp.write("\n_enumdeclarations = {\n")
Jack Jansen12b2b762000-08-20 19:30:56 +0000861 for k, v in self.namemappers[0].getall('enum'):
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000862 self.fp.write("\t%s : %s,\n" % (`k`, v))
Jack Jansen66544221997-08-08 14:49:02 +0000863 self.fp.write("}\n")
Jack Jansen5ccd8261995-07-17 11:43:20 +0000864
865def compiledata(data):
866 [type, description, flags] = data
867 return "%s -- %s %s" % (`type`, `description`, compiledataflags(flags))
868
869def is_null(data):
870 return data[0] == 'null'
871
872def is_optional(data):
873 return (data[2] & 0x8000)
874
875def is_enum(data):
876 return (data[2] & 0x2000)
877
878def getdatadoc(data):
879 [type, descr, flags] = data
880 if descr:
Jack Jansen8b777672002-08-07 14:49:00 +0000881 return ascii(descr)
Jack Jansen5ccd8261995-07-17 11:43:20 +0000882 if type == '****':
883 return 'anything'
884 if type == 'obj ':
885 return 'an AE object reference'
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000886 return "undocumented, typecode %s"%`type`
Jack Jansen5ccd8261995-07-17 11:43:20 +0000887
888dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
889def compiledataflags(flags):
890 bits = []
891 for i in range(16):
892 if flags & (1<<i):
893 if i in dataflagdict.keys():
894 bits.append(dataflagdict[i])
895 else:
896 bits.append(`i`)
897 return '[%s]' % string.join(bits)
898
Jack Jansen8b777672002-08-07 14:49:00 +0000899def ascii(str):
900 """Return a string with all non-ascii characters hex-encoded"""
901 if type(str) != type(''):
902 return map(ascii, str)
903 rv = ''
904 for c in str:
905 if c in ('\t', '\n', '\r') or ' ' <= c < chr(0x7f):
906 rv = rv + c
907 else:
Jack Jansen2f7f8c42002-08-07 15:05:42 +0000908 rv = rv + '\\' + 'x%02.2x' % ord(c)
Jack Jansen8b777672002-08-07 14:49:00 +0000909 return rv
910
Jack Jansen5ccd8261995-07-17 11:43:20 +0000911def identify(str):
912 """Turn any string into an identifier:
913 - replace space by _
914 - replace other illegal chars by _xx_ (hex code)
915 - prepend _ if the result is a python keyword
916 """
Jack Jansen66544221997-08-08 14:49:02 +0000917 if not str:
Jack Jansen21f67582002-08-07 15:44:53 +0000918 return "empty_ae_name_"
Jack Jansen5ccd8261995-07-17 11:43:20 +0000919 rv = ''
Fred Drake79e75e12001-07-20 19:05:50 +0000920 ok = string.ascii_letters + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000921 ok2 = ok + string.digits
922 for c in str:
923 if c in ok:
924 rv = rv + c
925 elif c == ' ':
926 rv = rv + '_'
927 else:
928 rv = rv + '_%02.2x_'%ord(c)
929 ok = ok2
Jack Jansenb2ecc2c2002-01-24 22:44:07 +0000930 if keyword.iskeyword(rv):
Jack Jansen21f67582002-08-07 15:44:53 +0000931 rv = rv + '_'
Jack Jansen5ccd8261995-07-17 11:43:20 +0000932 return rv
933
934# Call the main program
935
936if __name__ == '__main__':
937 main()
938 sys.exit(1)