1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import sys
23 import os.path
24 import fnmatch
25 import traceback
26 import optparse
27 from translate.misc import progressbar
28 from translate import __version__
29 try:
30 from cStringIO import StringIO
31 except ImportError:
32 from StringIO import StringIO
33
34 -class ManPageOption(optparse.Option, object):
35 ACTIONS = optparse.Option.ACTIONS + ("manpage",)
36
37 - def take_action(self, action, dest, opt, value, values, parser):
38 """take_action that can handle manpage as well as standard actions"""
39 if action == "manpage":
40 parser.print_manpage()
41 sys.exit(0)
42 return super(ManPageOption, self).take_action(action, dest, opt, value, values, parser)
43
70
72 """A specialized Option Parser for recursing through directories."""
73
74 - def __init__(self, formats, usetemplates=False, allowmissingtemplate=False, description=None):
75 """Construct the specialized Option Parser.
76
77 @type formats: Dictionary
78 @param formats: See L{setformats()} for an explanation of the formats parameter.
79
80 """
81
82 optparse.OptionParser.__init__(self, version="%prog "+__version__.ver, description=description)
83 self.setmanpageoption()
84 self.setprogressoptions()
85 self.seterrorleveloptions()
86 self.setformats(formats, usetemplates)
87 self.setpsycooption()
88 self.passthrough = []
89 self.allowmissingtemplate = allowmissingtemplate
90
92 return os.path.basename(sys.argv[0])
93
95 """creates a manpage option that allows the optionparser to generate a manpage"""
96 manpageoption = ManPageOption(None, "--manpage", dest="manpage", default=False, action="manpage",
97 help="output a manpage based on the help")
98 self.define_option(manpageoption)
99
101 """returns a formatted manpage"""
102 result = []
103 prog = self.get_prog_name()
104 formatprog = lambda x: x.replace("%prog", prog)
105 formatToolkit = lambda x: x.replace("%prog", "Translate Toolkit")
106 result.append('.\\" Autogenerated manpage\n')
107 result.append('.TH %s 1 "%s" "" "%s"\n' % (prog,
108 formatToolkit(self.version),
109 formatToolkit(self.version)))
110 result.append('.SH NAME\n')
111 result.append('%s \\- %s\n' % (self.get_prog_name(), self.description.split('\n\n')[0]))
112 result.append('.SH SYNOPSIS\n')
113 result.append('.PP\n')
114 usage = "\\fB%prog "
115 usage += " ".join([self.getusageman(option) for option in self.option_list])
116 usage += "\\fP"
117 result.append('%s\n' % formatprog(usage))
118 description_lines = self.description.split('\n\n')[1:]
119 if description_lines:
120 result.append('.SH DESCRIPTION\n')
121 result.append('\n'.join(description_lines))
122 result.append('.SH OPTIONS\n')
123 ManHelpFormatter().store_option_strings(self)
124 result.append('.PP\n')
125 for option in self.option_list:
126 result.append('.TP\n')
127 result.append('%s\n'%option)
128 result.append('%s\n'%option.help)
129 return "".join(result)
130
131 - def print_manpage(self, file=None):
132 """outputs a manpage for the program using the help information"""
133 if file is None:
134 file = sys.stdout
135 file.write(self.format_manpage())
136
138 psycomodes = ["none", "full", "profile"]
139 psycooption = optparse.Option(None, "--psyco", dest="psyco", default=None,
140 choices=psycomodes, metavar="MODE",
141 help="use psyco to speed up the operation, modes: %s" % (", ".join(psycomodes)))
142 self.define_option(psycooption)
143
145
146
147 if options.psyco == "none":
148 return
149 try:
150 import psyco
151 except Exception:
152 if options.psyco is not None:
153 self.warning("psyco unavailable", options, sys.exc_info())
154 return
155 if options.psyco is None:
156 options.psyco = "full"
157 if options.psyco == "full":
158 psyco.full()
159 elif options.psyco == "profile":
160 psyco.profile()
161
162 import encodings
163 psyco.cannotcompile(encodings.search_function)
164
166 """sets the usage string - if usage not given, uses getusagestring for each option"""
167 if usage is None:
168 self.usage = "%prog " + " ".join([self.getusagestring(option) for option in self.option_list])
169 else:
170 super(RecursiveOptionParser, self).set_usage(usage)
171
172 - def warning(self, msg, options=None, exc_info=None):
173 """Print a warning message incorporating 'msg' to stderr and exit."""
174 if options:
175 if options.errorlevel == "traceback":
176 errorinfo = "\n".join(traceback.format_exception(exc_info[0], exc_info[1], exc_info[2]))
177 elif options.errorlevel == "exception":
178 errorinfo = "\n".join(traceback.format_exception_only(exc_info[0], exc_info[1]))
179 elif options.errorlevel == "message":
180 errorinfo = str(exc_info[1])
181 else:
182 errorinfo = ""
183 if errorinfo:
184 msg += ": " + errorinfo
185 print >> sys.stderr, "\n%s: warning: %s" % (self.get_prog_name(), msg)
186
188 """returns the usage string for the given option"""
189 optionstring = "|".join(option._short_opts + option._long_opts)
190 if getattr(option, "optionalswitch", False):
191 optionstring = "[%s]" % optionstring
192 if option.metavar:
193 optionstring += " " + option.metavar
194 if getattr(option, "required", False):
195 return optionstring
196 else:
197 return "[%s]" % optionstring
198
200 """returns the usage string for the given option"""
201 optionstring = "\\fR|\\fP".join(option._short_opts + option._long_opts)
202 if getattr(option, "optionalswitch", False):
203 optionstring = "\\fR[\\fP%s\\fR]\\fP" % optionstring
204 if option.metavar:
205 optionstring += " \\fI%s\\fP" % option.metavar
206 if getattr(option, "required", False):
207 return optionstring
208 else:
209 return "\\fR[\\fP%s\\fR]\\fP" % optionstring
210
212 """defines the given option, replacing an existing one of the same short name if neccessary..."""
213 for short_opt in option._short_opts:
214 if self.has_option(short_opt):
215 self.remove_option(short_opt)
216 for long_opt in option._long_opts:
217 if self.has_option(long_opt):
218 self.remove_option(long_opt)
219 self.add_option(option)
220
278
280 """sets the progress options"""
281 self.progresstypes = {"none": progressbar.NoProgressBar,
282 "bar": progressbar.HashProgressBar,
283 "dots": progressbar.DotsProgressBar,
284 "names": progressbar.MessageProgressBar,
285 "verbose": progressbar.VerboseProgressBar}
286 progressoption = optparse.Option(None, "--progress", dest="progress", default="bar",
287 choices = self.progresstypes.keys(), metavar="PROGRESS",
288 help="show progress as: %s" % (", ".join(self.progresstypes)))
289 self.define_option(progressoption)
290
292 """sets the errorlevel options"""
293 self.errorleveltypes = ["none", "message", "exception", "traceback"]
294 errorleveloption = optparse.Option(None, "--errorlevel", dest="errorlevel", default="message",
295 choices = self.errorleveltypes, metavar="ERRORLEVEL",
296 help="show errorlevel as: %s" % (", ".join(self.errorleveltypes)))
297 self.define_option(errorleveloption)
298
309
310 - def isrecursive(self, fileoption, filepurpose='input'):
311 """checks if fileoption is a recursive file"""
312 if fileoption is None:
313 return False
314 elif isinstance(fileoption, list):
315 return True
316 else:
317 return os.path.isdir(fileoption)
318
320 """parses the command line options, handling implicit input/output args"""
321 (options, args) = super(RecursiveOptionParser, self).parse_args(args, values)
322
323 if args and not options.input:
324 if len(args) > 1:
325 options.input = args[:-1]
326 args = args[-1:]
327 else:
328 options.input = args[0]
329 args = []
330 if args and not options.output:
331 options.output = args[-1]
332 args = args[:-1]
333 if args:
334 self.error("You have used an invalid combination of --input, --output and freestanding args")
335 if isinstance(options.input, list) and len(options.input) == 1:
336 options.input = options.input[0]
337 if options.input is None:
338 self.error("You need to give an inputfile or use - for stdin ; use --help for full usage instructions")
339 elif options.input == '-':
340 options.input = None
341 return (options, args)
342
344 """get the options required to pass to the filtermethod..."""
345 passthroughoptions = {}
346 for optionname in dir(options):
347 if optionname in self.passthrough:
348 passthroughoptions[optionname] = getattr(options, optionname)
349 return passthroughoptions
350
352 """works out which output format and processor method to use..."""
353 if inputpath:
354 inputbase, inputext = self.splitinputext(inputpath)
355 else:
356 inputext = None
357 if templatepath:
358 templatebase, templateext = self.splittemplateext(templatepath)
359 else:
360 templateext = None
361 if (inputext, templateext) in options.outputoptions:
362 return options.outputoptions[inputext, templateext]
363 elif (inputext, "*") in options.outputoptions:
364 outputformat, fileprocessor = options.outputoptions[inputext, "*"]
365 elif ("*", templateext) in options.outputoptions:
366 outputformat, fileprocessor = options.outputoptions["*", templateext]
367 elif ("*", "*") in options.outputoptions:
368 outputformat, fileprocessor = options.outputoptions["*", "*"]
369 elif (inputext, None) in options.outputoptions:
370 return options.outputoptions[inputext, None]
371 elif (None, templateext) in options.outputoptions:
372 return options.outputoptions[None, templateext]
373 elif ("*", None) in options.outputoptions:
374 outputformat, fileprocessor = options.outputoptions["*", None]
375 elif (None, "*") in options.outputoptions:
376 outputformat, fileprocessor = options.outputoptions[None, "*"]
377 else:
378 if self.usetemplates:
379 if inputext is None:
380 raise ValueError("don't know what to do with input format (no file extension), no template file")
381 elif templateext is None:
382 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
383 else:
384 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
385 else:
386 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
387 if outputformat == "*":
388 if inputext:
389 outputformat = inputext
390 elif templateext:
391 outputformat = templateext
392 elif ("*", "*") in options.outputoptions:
393 outputformat = None
394 else:
395 if self.usetemplates:
396 if templateext is None:
397 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
398 else:
399 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
400 else:
401 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
402 return outputformat, fileprocessor
403
405 """sets up a progress bar appropriate to the options and files"""
406 if options.progress in ('bar', 'verbose'):
407 self.progressbar = self.progresstypes[options.progress](0, len(allfiles))
408 print >> sys.stderr, "processing %d files..." % len(allfiles)
409 else:
410 self.progressbar = self.progresstypes[options.progress]()
411
418
420 """gets the absolute path to an output file"""
421 if options.recursiveoutput and options.output:
422 return os.path.join(options.output, outputpath)
423 else:
424 return outputpath
425
427 """gets the absolute path to a template file"""
428 if not options.recursivetemplate:
429 return templatepath
430 elif templatepath is not None and self.usetemplates and options.template:
431 return os.path.join(options.template, templatepath)
432 else:
433 return None
434
443
445 """recurse through directories and process files"""
446 if self.isrecursive(options.input, 'input') and getattr(options, "allowrecursiveinput", True):
447 if not self.isrecursive(options.output, 'output'):
448 try:
449 self.warning("Output directory does not exist. Attempting to create")
450 os.mkdir(options.output)
451 except IOError, e:
452 self.error(optparse.OptionValueError("Output directory does not exist, attempt to create failed"))
453 if isinstance(options.input, list):
454 inputfiles = self.recurseinputfilelist(options)
455 else:
456 inputfiles = self.recurseinputfiles(options)
457 else:
458 if options.input:
459 inputfiles = [os.path.basename(options.input)]
460 options.input = os.path.dirname(options.input)
461 else:
462 inputfiles = [options.input]
463 options.recursiveoutput = self.isrecursive(options.output, 'output') and getattr(options, "allowrecursiveoutput", True)
464 options.recursivetemplate = self.usetemplates and self.isrecursive(options.template, 'template') and getattr(options, "allowrecursivetemplate", True)
465 self.initprogressbar(inputfiles, options)
466 for inputpath in inputfiles:
467 try:
468 templatepath = self.gettemplatename(options, inputpath)
469
470
471 if options.recursivetemplate and templatepath is None and not self.allowmissingtemplate:
472 self.warning("No template at %s. Skipping %s." % (templatepath, inputpath))
473 continue
474 outputformat, fileprocessor = self.getoutputoptions(options, inputpath, templatepath)
475 fullinputpath = self.getfullinputpath(options, inputpath)
476 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
477 outputpath = self.getoutputname(options, inputpath, outputformat)
478 fulloutputpath = self.getfulloutputpath(options, outputpath)
479 if options.recursiveoutput and outputpath:
480 self.checkoutputsubdir(options, os.path.dirname(outputpath))
481 except Exception, error:
482 if isinstance(error, KeyboardInterrupt):
483 raise
484 self.warning("Couldn't handle input file %s" % inputpath, options, sys.exc_info())
485 continue
486 try:
487 success = self.processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
488 except Exception, error:
489 if isinstance(error, KeyboardInterrupt):
490 raise
491 self.warning("Error processing: input %s, output %s, template %s" % (fullinputpath, fulloutputpath, fulltemplatepath), options, sys.exc_info())
492 success = False
493 self.reportprogress(inputpath, success)
494 del self.progressbar
495
501
503 """opens the output file"""
504 if fulloutputpath is None:
505 return sys.stdout
506 return open(fulloutputpath, 'w')
507
509 """opens a temporary output file"""
510 return StringIO()
511
513 """write the temp outputfile to its final destination"""
514 outputfile.reset()
515 outputstring = outputfile.read()
516 outputfile = self.openoutputfile(options, fulloutputpath)
517 outputfile.write(outputstring)
518 outputfile.close()
519
521 """opens the template file (if required)"""
522 if fulltemplatepath is not None:
523 if os.path.isfile(fulltemplatepath):
524 return open(fulltemplatepath, 'r')
525 else:
526 self.warning("missing template file %s" % fulltemplatepath)
527 return None
528
529 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
530 """process an individual file"""
531 inputfile = self.openinputfile(options, fullinputpath)
532 if fulloutputpath and fulloutputpath in (fullinputpath, fulltemplatepath):
533 outputfile = self.opentempoutputfile(options, fulloutputpath)
534 tempoutput = True
535 else:
536 outputfile = self.openoutputfile(options, fulloutputpath)
537 tempoutput = False
538 templatefile = self.opentemplatefile(options, fulltemplatepath)
539 passthroughoptions = self.getpassthroughoptions(options)
540 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
541 if tempoutput:
542 self.warning("writing to temporary output...")
543 self.finalizetempoutputfile(options, outputfile, fulloutputpath)
544 return True
545 else:
546
547 if fulloutputpath and os.path.isfile(fulloutputpath):
548 outputfile.close()
549 os.unlink(fulloutputpath)
550 return False
551
556
557 - def mkdir(self, parent, subdir):
558 """makes a subdirectory (recursively if neccessary)"""
559 if not os.path.isdir(parent):
560 raise ValueError("cannot make child directory %r if parent %r does not exist" % (subdir, parent))
561 currentpath = parent
562 subparts = subdir.split(os.sep)
563 for part in subparts:
564 currentpath = os.path.join(currentpath, part)
565 if not os.path.isdir(currentpath):
566 os.mkdir(currentpath)
567
569 """checks to see if subdir under options.output needs to be created, creates if neccessary"""
570 fullpath = os.path.join(options.output, subdir)
571 if not os.path.isdir(fullpath):
572 self.mkdir(options.output, subdir)
573
575 """checks if this path has been excluded"""
576 basename = os.path.basename(inputpath)
577 for excludename in options.exclude:
578 if fnmatch.fnmatch(basename, excludename):
579 return True
580 return False
581
596
623
624 - def splitext(self, pathname):
625 """splits into name and ext, and removes the extsep"""
626 root, ext = os.path.splitext(pathname)
627 ext = ext.replace(os.extsep, "", 1)
628 return (root, ext)
629
631 """splits an inputpath into name and extension"""
632 return self.splitext(inputpath)
633
635 """splits a templatepath into name and extension"""
636 return self.splitext(templatepath)
637
639 """returns whether the given template exists..."""
640 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
641 return os.path.isfile(fulltemplatepath)
642
644 """gets an output filename based on the input filename"""
645 if not self.usetemplates: return None
646 if not inputname or not options.recursivetemplate: return options.template
647 inputbase, inputext = self.splitinputext(inputname)
648 if options.template:
649 for inputext1, templateext1 in options.outputoptions:
650 if inputext == inputext1:
651 if templateext1:
652 templatepath = inputbase + os.extsep + templateext1
653 if self.templateexists(options, templatepath):
654 return templatepath
655 if "*" in options.inputformats:
656 for inputext1, templateext1 in options.outputoptions:
657 if (inputext == inputext1) or (inputext1 == "*"):
658 if templateext1 == "*":
659 templatepath = inputname
660 if self.templateexists(options, templatepath):
661 return templatepath
662 elif templateext1:
663 templatepath = inputbase + os.extsep + templateext1
664 if self.templateexists(options, templatepath):
665 return templatepath
666 return None
667
669 """gets an output filename based on the input filename"""
670 if not inputname or not options.recursiveoutput: return options.output
671 inputbase, inputext = self.splitinputext(inputname)
672 outputname = inputbase
673 if outputformat:
674 outputname += os.extsep + outputformat
675 return outputname
676
681