1 | """
|
---|
2 | gensuitemodule - Generate an AE suite module from an aete/aeut resource
|
---|
3 |
|
---|
4 | Based on aete.py.
|
---|
5 |
|
---|
6 | Reading and understanding this code is left as an exercise to the reader.
|
---|
7 | """
|
---|
8 |
|
---|
9 | from warnings import warnpy3k
|
---|
10 | warnpy3k("In 3.x, the gensuitemodule module is removed.", stacklevel=2)
|
---|
11 |
|
---|
12 | import MacOS
|
---|
13 | import EasyDialogs
|
---|
14 | import os
|
---|
15 | import string
|
---|
16 | import sys
|
---|
17 | import types
|
---|
18 | import StringIO
|
---|
19 | import keyword
|
---|
20 | import macresource
|
---|
21 | import aetools
|
---|
22 | import distutils.sysconfig
|
---|
23 | import OSATerminology
|
---|
24 | from Carbon.Res import *
|
---|
25 | import Carbon.Folder
|
---|
26 | import MacOS
|
---|
27 | import getopt
|
---|
28 | import plistlib
|
---|
29 |
|
---|
30 | _MAC_LIB_FOLDER=os.path.dirname(aetools.__file__)
|
---|
31 | DEFAULT_STANDARD_PACKAGEFOLDER=os.path.join(_MAC_LIB_FOLDER, 'lib-scriptpackages')
|
---|
32 | DEFAULT_USER_PACKAGEFOLDER=distutils.sysconfig.get_python_lib()
|
---|
33 |
|
---|
34 | def usage():
|
---|
35 | sys.stderr.write("Usage: %s [opts] application-or-resource-file\n" % sys.argv[0])
|
---|
36 | sys.stderr.write("""Options:
|
---|
37 | --output pkgdir Pathname of the output package (short: -o)
|
---|
38 | --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)
|
---|
41 | --creator code Set creator code for package (-c)
|
---|
42 | --dump Dump aete resource to stdout in stead of creating module (-d)
|
---|
43 | --verbose Tell us what happens (-v)
|
---|
44 | """)
|
---|
45 | sys.exit(1)
|
---|
46 |
|
---|
47 | def main():
|
---|
48 | 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()
|
---|
55 |
|
---|
56 | process_func = processfile
|
---|
57 | basepkgname = 'StdSuites'
|
---|
58 | output = None
|
---|
59 | edit_modnames = []
|
---|
60 | creatorsignature = None
|
---|
61 | dump = None
|
---|
62 | verbose = None
|
---|
63 |
|
---|
64 | 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
|
---|
85 |
|
---|
86 |
|
---|
87 | 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)
|
---|
90 |
|
---|
91 | for filename in args:
|
---|
92 | process_func(filename, output=output, basepkgname=basepkgname,
|
---|
93 | edit_modnames=edit_modnames, creatorsignature=creatorsignature,
|
---|
94 | dump=dump, verbose=verbose)
|
---|
95 | else:
|
---|
96 | main_interactive()
|
---|
97 |
|
---|
98 | def main_interactive(interact=0, basepkgname='StdSuites'):
|
---|
99 | 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)
|
---|
125 |
|
---|
126 | def is_scriptable(application):
|
---|
127 | """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
|
---|
146 |
|
---|
147 | def processfile_fromresource(fullname, output=None, basepkgname=None,
|
---|
148 | 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)
|
---|
165 | if verbose:
|
---|
166 | print >>verbose, "\nLISTING aete+aeut RESOURCES IN", repr(fullname)
|
---|
167 | 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:
|
---|
175 | if rf != cur:
|
---|
176 | CloseResFile(rf)
|
---|
177 | UseResFile(cur)
|
---|
178 | # switch back (needed for dialogs in Python)
|
---|
179 | UseResFile(cur)
|
---|
180 | if dump:
|
---|
181 | dumpaetelist(aetelist, dump)
|
---|
182 | compileaetelist(aetelist, fullname, output=output,
|
---|
183 | basepkgname=basepkgname, edit_modnames=edit_modnames,
|
---|
184 | creatorsignature=creatorsignature, verbose=verbose)
|
---|
185 |
|
---|
186 | def processfile(fullname, output=None, basepkgname=None,
|
---|
187 | 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:
|
---|
193 | print >>verbose, "\nASKING FOR aete DICTIONARY IN", repr(fullname)
|
---|
194 | 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)
|
---|
210 | if not raw:
|
---|
211 | 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,
|
---|
224 | creatorsignature=creatorsignature, edit_modnames=edit_modnames,
|
---|
225 | verbose=verbose)
|
---|
226 |
|
---|
227 | def getappterminology(fullname, verbose=None):
|
---|
228 | """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
|
---|
257 |
|
---|
258 |
|
---|
259 | def compileaetelist(aetelist, fullname, output=None, basepkgname=None,
|
---|
260 | edit_modnames=None, creatorsignature=None, verbose=None):
|
---|
261 | for aete, resinfo in aetelist:
|
---|
262 | compileaete(aete, resinfo, fullname, output=output,
|
---|
263 | basepkgname=basepkgname, edit_modnames=edit_modnames,
|
---|
264 | creatorsignature=creatorsignature, verbose=verbose)
|
---|
265 |
|
---|
266 | def dumpaetelist(aetelist, output):
|
---|
267 | import pprint
|
---|
268 | pprint.pprint(aetelist, output)
|
---|
269 |
|
---|
270 | def decode(data, verbose=None):
|
---|
271 | """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
|
---|
282 |
|
---|
283 | def simplify(item):
|
---|
284 | """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
|
---|
291 |
|
---|
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 |
|
---|
297 | def getbyte(f, *args):
|
---|
298 | c = f.read(1)
|
---|
299 | if not c:
|
---|
300 | raise EOFError, 'in getbyte' + str(args)
|
---|
301 | return ord(c)
|
---|
302 |
|
---|
303 | def getword(f, *args):
|
---|
304 | 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])
|
---|
309 |
|
---|
310 | def getlong(f, *args):
|
---|
311 | 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])
|
---|
316 |
|
---|
317 | def getostype(f, *args):
|
---|
318 | getalign(f)
|
---|
319 | s = f.read(4)
|
---|
320 | if len(s) < 4:
|
---|
321 | raise EOFError, 'in getostype' + str(args)
|
---|
322 | return s
|
---|
323 |
|
---|
324 | def getpstr(f, *args):
|
---|
325 | 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
|
---|
334 |
|
---|
335 | def getalign(f):
|
---|
336 | if f.tell() & 1:
|
---|
337 | c = f.read(1)
|
---|
338 | ##if c != '\0':
|
---|
339 | ## print align:', repr(c)
|
---|
340 |
|
---|
341 | def getlist(f, description, getitem):
|
---|
342 | count = getword(f)
|
---|
343 | list = []
|
---|
344 | for i in range(count):
|
---|
345 | list.append(generic(getitem, f))
|
---|
346 | getalign(f)
|
---|
347 | return list
|
---|
348 |
|
---|
349 | def alt_generic(what, f, *args):
|
---|
350 | print "generic", repr(what), args
|
---|
351 | res = vageneric(what, f, args)
|
---|
352 | print '->', repr(res)
|
---|
353 | return res
|
---|
354 |
|
---|
355 | def generic(what, f, *args):
|
---|
356 | 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
|
---|
364 | return "BAD GENERIC ARGS: %r" % (what,)
|
---|
365 |
|
---|
366 | getdata = [
|
---|
367 | (getostype, "type"),
|
---|
368 | (getpstr, "description"),
|
---|
369 | (getword, "flags")
|
---|
370 | ]
|
---|
371 | getargument = [
|
---|
372 | (getpstr, "name"),
|
---|
373 | (getostype, "keyword"),
|
---|
374 | (getdata, "what")
|
---|
375 | ]
|
---|
376 | getevent = [
|
---|
377 | (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 | ]
|
---|
385 | getproperty = [
|
---|
386 | (getpstr, "name"),
|
---|
387 | (getostype, "code"),
|
---|
388 | (getdata, "what")
|
---|
389 | ]
|
---|
390 | getelement = [
|
---|
391 | (getostype, "type"),
|
---|
392 | (getlist, "keyform", getostype)
|
---|
393 | ]
|
---|
394 | getclass = [
|
---|
395 | (getpstr, "name"),
|
---|
396 | (getostype, "class code"),
|
---|
397 | (getpstr, "description"),
|
---|
398 | (getlist, "properties", getproperty),
|
---|
399 | (getlist, "elements", getelement)
|
---|
400 | ]
|
---|
401 | getcomparison = [
|
---|
402 | (getpstr, "operator name"),
|
---|
403 | (getostype, "operator ID"),
|
---|
404 | (getpstr, "operator comment"),
|
---|
405 | ]
|
---|
406 | getenumerator = [
|
---|
407 | (getpstr, "enumerator name"),
|
---|
408 | (getostype, "enumerator ID"),
|
---|
409 | (getpstr, "enumerator comment")
|
---|
410 | ]
|
---|
411 | getenumeration = [
|
---|
412 | (getostype, "enumeration ID"),
|
---|
413 | (getlist, "enumerator", getenumerator)
|
---|
414 | ]
|
---|
415 | getsuite = [
|
---|
416 | (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 | ]
|
---|
426 | getaete = [
|
---|
427 | (getword, "major/minor version in BCD"),
|
---|
428 | (getword, "language code"),
|
---|
429 | (getword, "script code"),
|
---|
430 | (getlist, "suites", getsuite)
|
---|
431 | ]
|
---|
432 |
|
---|
433 | def compileaete(aete, resinfo, fname, output=None, basepkgname=None,
|
---|
434 | 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)
|
---|
506 |
|
---|
507 | # 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)
|
---|
527 |
|
---|
528 | # Generate a code-to-name mapper for all of the types (classes) declared in this module
|
---|
529 | application_class = None
|
---|
530 | 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'):
|
---|
535 | fp.write(" %r : %s,\n" % (k, v))
|
---|
536 | if k == 'capp':
|
---|
537 | application_class = v
|
---|
538 | fp.write("}\n")
|
---|
539 |
|
---|
540 |
|
---|
541 | 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")
|
---|
546 | fp.write(" _signature = %r\n\n"%(creatorsignature,))
|
---|
547 | fp.write(" _moduleName = '%s'\n\n"%packagename)
|
---|
548 | if application_class:
|
---|
549 | fp.write(" _elemdict = %s._elemdict\n" % application_class)
|
---|
550 | fp.write(" _propdict = %s._propdict\n" % application_class)
|
---|
551 | fp.close()
|
---|
552 |
|
---|
553 | class SuiteCompiler:
|
---|
554 | 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
|
---|
560 |
|
---|
561 | # Set by precompilesuite
|
---|
562 | self.pathname = None
|
---|
563 | self.modname = None
|
---|
564 |
|
---|
565 | # Set by compilesuite
|
---|
566 | self.fp = None
|
---|
567 | self.basemodule = None
|
---|
568 | self.enumsneeded = {}
|
---|
569 |
|
---|
570 | 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
|
---|
575 |
|
---|
576 | 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
|
---|
592 |
|
---|
593 | self.modname = os.path.splitext(os.path.split(self.pathname)[1])[0]
|
---|
594 |
|
---|
595 | if self.basepackage and code in self.basepackage._code_to_module:
|
---|
596 | # 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
|
---|
602 |
|
---|
603 | self.enumsneeded = {}
|
---|
604 | for event in events:
|
---|
605 | self.findenumsinevent(event)
|
---|
606 |
|
---|
607 | 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)
|
---|
617 |
|
---|
618 | for enum in self.enumsneeded.keys():
|
---|
619 | objc.checkforenum(enum)
|
---|
620 |
|
---|
621 | objc.dumpindex()
|
---|
622 |
|
---|
623 | precompinfo = objc.getprecompinfo(self.modname)
|
---|
624 |
|
---|
625 | return code, self.modname, precompinfo
|
---|
626 |
|
---|
627 | 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
|
---|
643 |
|
---|
644 | events.sort()
|
---|
645 | classes.sort(class_sorter)
|
---|
646 | comps.sort()
|
---|
647 | enums.sort()
|
---|
648 |
|
---|
649 | self.fp = fp = open(self.pathname, 'w')
|
---|
650 | MacOS.SetCreatorAndType(self.pathname, 'Pyth', 'TEXT')
|
---|
651 |
|
---|
652 | 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')
|
---|
658 |
|
---|
659 | fp.write('import aetools\n')
|
---|
660 | fp.write('import MacOS\n\n')
|
---|
661 | fp.write("_code = %r\n\n"% (code,))
|
---|
662 | if self.basepackage and code in self.basepackage._code_to_module:
|
---|
663 | # 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]
|
---|
667 | elif self.basepackage and code.lower() in self.basepackage._code_to_module:
|
---|
668 | # 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()
|
---|
676 |
|
---|
677 | self.enumsneeded = {}
|
---|
678 | if events:
|
---|
679 | for event in events:
|
---|
680 | self.compileevent(event)
|
---|
681 | else:
|
---|
682 | fp.write(" pass\n\n")
|
---|
683 |
|
---|
684 | 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)
|
---|
694 |
|
---|
695 | for enum in self.enumsneeded.keys():
|
---|
696 | objc.checkforenum(enum)
|
---|
697 |
|
---|
698 | objc.dumpindex()
|
---|
699 |
|
---|
700 | 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)
|
---|
709 |
|
---|
710 | 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:
|
---|
721 | fp.write(" %r : %r,\n"%(identify(a[0]), a[1]))
|
---|
722 | fp.write(" }\n\n")
|
---|
723 |
|
---|
724 | #
|
---|
725 | # Generate function header
|
---|
726 | #
|
---|
727 | has_arg = (not is_null(accepts))
|
---|
728 | opt_arg = (has_arg and is_optional(accepts))
|
---|
729 |
|
---|
730 | 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 | #
|
---|
758 | fp.write(" _code = %r\n"% (code,))
|
---|
759 | fp.write(" _subcode = %r\n\n"% (subcode,))
|
---|
760 | #
|
---|
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:
|
---|
776 | fp.write(" if _no_object is not None: raise TypeError, 'No direct arg expected'\n")
|
---|
777 | 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]
|
---|
785 | if ename != '****':
|
---|
786 | fp.write(" aetools.enumsubst(_arguments, %r, _Enum_%s)\n" %
|
---|
787 | (kname, identify(ename)))
|
---|
788 | 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 | #
|
---|
804 | fp.write(" if '----' in _arguments:\n")
|
---|
805 | if is_enum(returns):
|
---|
806 | fp.write(" # XXXX Should do enum remapping here...\n")
|
---|
807 | fp.write(" return _arguments['----']\n")
|
---|
808 | fp.write("\n")
|
---|
809 |
|
---|
810 | 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]
|
---|
816 | if ename != '****':
|
---|
817 | self.enumsneeded[ename] = 1
|
---|
818 |
|
---|
819 | #
|
---|
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 | #
|
---|
826 | class CodeNameMapper:
|
---|
827 |
|
---|
828 | 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
|
---|
845 |
|
---|
846 | def addnamecode(self, type, name, code):
|
---|
847 | self.name2code[type][name] = code
|
---|
848 | if code not in self.code2name[type]:
|
---|
849 | self.code2name[type][code] = name
|
---|
850 |
|
---|
851 | def hasname(self, name):
|
---|
852 | for dict in self.name2code.values():
|
---|
853 | if name in dict:
|
---|
854 | return True
|
---|
855 | return False
|
---|
856 |
|
---|
857 | def hascode(self, type, code):
|
---|
858 | return code in self.code2name[type]
|
---|
859 |
|
---|
860 | 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
|
---|
869 |
|
---|
870 | def getall(self, type):
|
---|
871 | return self.code2name[type].items()
|
---|
872 |
|
---|
873 | 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)
|
---|
884 |
|
---|
885 | def prepareforexport(self, name=None):
|
---|
886 | if not self.modulename:
|
---|
887 | self.modulename = name
|
---|
888 | return self
|
---|
889 |
|
---|
890 | class ObjectCompiler:
|
---|
891 | def __init__(self, fp, modname, basesuite, othernamemappers=None, interact=1,
|
---|
892 | 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)
|
---|
907 |
|
---|
908 | 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
|
---|
915 |
|
---|
916 | 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)
|
---|
941 |
|
---|
942 | 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
|
---|
949 |
|
---|
950 | 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
|
---|
964 |
|
---|
965 | 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)))
|
---|
977 | self.fp.write(' want = %r\n' % (code,))
|
---|
978 | self.namemappers[0].addnamecode('class', pname, code)
|
---|
979 | is_application_class = (code == 'capp')
|
---|
980 | properties.sort()
|
---|
981 | for prop in properties:
|
---|
982 | self.compileproperty(prop, is_application_class)
|
---|
983 | elements.sort()
|
---|
984 | for elem in elements:
|
---|
985 | self.compileelement(elem)
|
---|
986 |
|
---|
987 | def compileproperty(self, prop, is_application_class=False):
|
---|
988 | [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:
|
---|
1001 | 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])))
|
---|
1004 | self.fp.write(" which = %r\n" % (code,))
|
---|
1005 | self.fp.write(" want = %r\n" % (what[0],))
|
---|
1006 | self.namemappers[0].addnamecode('property', pname, code)
|
---|
1007 | if is_application_class and self.fp:
|
---|
1008 | self.fp.write("%s = _Prop_%s()\n" % (pname, pname))
|
---|
1009 |
|
---|
1010 | def compileelement(self, elem):
|
---|
1011 | [code, keyform] = elem
|
---|
1012 | if self.fp:
|
---|
1013 | self.fp.write("# element %r as %s\n" % (code, keyform))
|
---|
1014 |
|
---|
1015 | 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:
|
---|
1024 | print >>self.verbose, '** Skip multiple %s of %s (code %r)' % (cname, self.namemappers[0].findcodename('class', code)[0], code)
|
---|
1025 | 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)
|
---|
1038 |
|
---|
1039 | 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)
|
---|
1048 |
|
---|
1049 | if self.fp:
|
---|
1050 | self.fp.write("%s._superclassnames = %r\n"%(cname, superclassnames))
|
---|
1051 |
|
---|
1052 | 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:
|
---|
1059 | self.fp.write("# XXXX %s element %r not found!!\n"%(cname, ecode))
|
---|
1060 | else:
|
---|
1061 | elist.append((name, ename))
|
---|
1062 |
|
---|
1063 | plist.sort()
|
---|
1064 | elist.sort()
|
---|
1065 |
|
---|
1066 | 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")
|
---|
1075 |
|
---|
1076 | 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)))
|
---|
1083 |
|
---|
1084 | 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
|
---|
1094 |
|
---|
1095 | def compileenumerator(self, item):
|
---|
1096 | [name, code, desc] = item
|
---|
1097 | self.fp.write(" %r : %r,\t# %s\n" % (identify(name), code, ascii(desc)))
|
---|
1098 |
|
---|
1099 | 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))
|
---|
1109 |
|
---|
1110 | 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")
|
---|
1114 |
|
---|
1115 | self.fp.write("_classdeclarations = {\n")
|
---|
1116 | classlist = self.namemappers[0].getall('class')
|
---|
1117 | classlist.sort()
|
---|
1118 | for k, v in classlist:
|
---|
1119 | self.fp.write(" %r : %s,\n" % (k, v))
|
---|
1120 | self.fp.write("}\n")
|
---|
1121 |
|
---|
1122 | self.fp.write("\n_propdeclarations = {\n")
|
---|
1123 | proplist = self.namemappers[0].getall('property')
|
---|
1124 | proplist.sort()
|
---|
1125 | for k, v in proplist:
|
---|
1126 | self.fp.write(" %r : _Prop_%s,\n" % (k, v))
|
---|
1127 | self.fp.write("}\n")
|
---|
1128 |
|
---|
1129 | self.fp.write("\n_compdeclarations = {\n")
|
---|
1130 | complist = self.namemappers[0].getall('comparison')
|
---|
1131 | complist.sort()
|
---|
1132 | for k, v in complist:
|
---|
1133 | self.fp.write(" %r : %s,\n" % (k, v))
|
---|
1134 | self.fp.write("}\n")
|
---|
1135 |
|
---|
1136 | self.fp.write("\n_enumdeclarations = {\n")
|
---|
1137 | enumlist = self.namemappers[0].getall('enum')
|
---|
1138 | enumlist.sort()
|
---|
1139 | for k, v in enumlist:
|
---|
1140 | self.fp.write(" %r : %s,\n" % (k, v))
|
---|
1141 | self.fp.write("}\n")
|
---|
1142 |
|
---|
1143 | def compiledata(data):
|
---|
1144 | [type, description, flags] = data
|
---|
1145 | return "%r -- %r %s" % (type, description, compiledataflags(flags))
|
---|
1146 |
|
---|
1147 | def is_null(data):
|
---|
1148 | return data[0] == 'null'
|
---|
1149 |
|
---|
1150 | def is_optional(data):
|
---|
1151 | return (data[2] & 0x8000)
|
---|
1152 |
|
---|
1153 | def is_enum(data):
|
---|
1154 | return (data[2] & 0x2000)
|
---|
1155 |
|
---|
1156 | def getdatadoc(data):
|
---|
1157 | [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'
|
---|
1164 | return "undocumented, typecode %r"%(type,)
|
---|
1165 |
|
---|
1166 | dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
|
---|
1167 | def compiledataflags(flags):
|
---|
1168 | 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:
|
---|
1174 | bits.append(repr(i))
|
---|
1175 | return '[%s]' % string.join(bits)
|
---|
1176 |
|
---|
1177 | def ascii(str):
|
---|
1178 | """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
|
---|
1188 |
|
---|
1189 | def identify(str):
|
---|
1190 | """Turn any string into an identifier:
|
---|
1191 | - replace space by _
|
---|
1192 | - replace other illegal chars by _xx_ (hex code)
|
---|
1193 | - append _ if the result is a python keyword
|
---|
1194 | """
|
---|
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
|
---|
1211 |
|
---|
1212 | # Call the main program
|
---|
1213 |
|
---|
1214 | if __name__ == '__main__':
|
---|
1215 | main()
|
---|
1216 | sys.exit(1)
|
---|