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):
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 try:
139 import psyco
140 except Exception:
141 return
142 psycomodes = ["none", "full", "profile"]
143 psycooption = optparse.Option(None, "--psyco", dest="psyco", default=None,
144 choices=psycomodes, metavar="MODE",
145 help="use psyco to speed up the operation, modes: %s" % (", ".join(psycomodes)))
146 self.define_option(psycooption)
147
149
150
151 if getattr(options, "psyco", "none") == "none":
152 return
153 try:
154 import psyco
155 except Exception:
156 if options.psyco is not None:
157 self.warning("psyco unavailable", options, sys.exc_info())
158 return
159 if options.psyco is None:
160 options.psyco = "full"
161 if options.psyco == "full":
162 psyco.full()
163 elif options.psyco == "profile":
164 psyco.profile()
165
166 import encodings
167 psyco.cannotcompile(encodings.search_function)
168
170 """sets the usage string - if usage not given, uses getusagestring for each option"""
171 if usage is None:
172 self.usage = "%prog " + " ".join([self.getusagestring(option) for option in self.option_list])
173 else:
174 super(RecursiveOptionParser, self).set_usage(usage)
175
176 - def warning(self, msg, options=None, exc_info=None):
177 """Print a warning message incorporating 'msg' to stderr and exit."""
178 if options:
179 if options.errorlevel == "traceback":
180 errorinfo = "\n".join(traceback.format_exception(exc_info[0], exc_info[1], exc_info[2]))
181 elif options.errorlevel == "exception":
182 errorinfo = "\n".join(traceback.format_exception_only(exc_info[0], exc_info[1]))
183 elif options.errorlevel == "message":
184 errorinfo = str(exc_info[1])
185 else:
186 errorinfo = ""
187 if errorinfo:
188 msg += ": " + errorinfo
189 print >> sys.stderr, "\n%s: warning: %s" % (self.get_prog_name(), msg)
190
192 """returns the usage string for the given option"""
193 optionstring = "|".join(option._short_opts + option._long_opts)
194 if getattr(option, "optionalswitch", False):
195 optionstring = "[%s]" % optionstring
196 if option.metavar:
197 optionstring += " " + option.metavar
198 if getattr(option, "required", False):
199 return optionstring
200 else:
201 return "[%s]" % optionstring
202
204 """returns the usage string for the given option"""
205 optionstring = "\\fR|\\fP".join(option._short_opts + option._long_opts)
206 if getattr(option, "optionalswitch", False):
207 optionstring = "\\fR[\\fP%s\\fR]\\fP" % optionstring
208 if option.metavar:
209 optionstring += " \\fI%s\\fP" % option.metavar
210 if getattr(option, "required", False):
211 return optionstring
212 else:
213 return "\\fR[\\fP%s\\fR]\\fP" % optionstring
214
216 """defines the given option, replacing an existing one of the same short name if neccessary..."""
217 for short_opt in option._short_opts:
218 if self.has_option(short_opt):
219 self.remove_option(short_opt)
220 for long_opt in option._long_opts:
221 if self.has_option(long_opt):
222 self.remove_option(long_opt)
223 self.add_option(option)
224
285
287 """sets the progress options"""
288 self.progresstypes = {
289 "none": progressbar.NoProgressBar,
290 "bar": progressbar.HashProgressBar,
291 "dots": progressbar.DotsProgressBar,
292 "names": progressbar.MessageProgressBar,
293 "verbose": progressbar.VerboseProgressBar
294 }
295 progressoption = optparse.Option(None, "--progress", dest="progress", default="bar",
296 choices = self.progresstypes.keys(), metavar="PROGRESS",
297 help="show progress as: %s" % (", ".join(self.progresstypes)))
298 self.define_option(progressoption)
299
301 """sets the errorlevel options"""
302 self.errorleveltypes = ["none", "message", "exception", "traceback"]
303 errorleveloption = optparse.Option(None, "--errorlevel", dest="errorlevel", default="message",
304 choices = self.errorleveltypes, metavar="ERRORLEVEL",
305 help="show errorlevel as: %s" % (", ".join(self.errorleveltypes)))
306 self.define_option(errorleveloption)
307
318
319 - def isrecursive(self, fileoption, filepurpose='input'):
320 """checks if fileoption is a recursive file"""
321 if fileoption is None:
322 return False
323 elif isinstance(fileoption, list):
324 return True
325 else:
326 return os.path.isdir(fileoption)
327
329 """parses the command line options, handling implicit input/output args"""
330 (options, args) = super(RecursiveOptionParser, self).parse_args(args, values)
331
332 if args and not options.input:
333 if len(args) > 1:
334 options.input = args[:-1]
335 args = args[-1:]
336 else:
337 options.input = args[0]
338 args = []
339 if args and not options.output:
340 options.output = args[-1]
341 args = args[:-1]
342 if args:
343 self.error("You have used an invalid combination of --input, --output and freestanding args")
344 if isinstance(options.input, list) and len(options.input) == 1:
345 options.input = options.input[0]
346 if options.input is None:
347 self.error("You need to give an inputfile or use - for stdin ; use --help for full usage instructions")
348 elif options.input == '-':
349 options.input = None
350 return (options, args)
351
353 """get the options required to pass to the filtermethod..."""
354 passthroughoptions = {}
355 for optionname in dir(options):
356 if optionname in self.passthrough:
357 passthroughoptions[optionname] = getattr(options, optionname)
358 return passthroughoptions
359
361 """works out which output format and processor method to use..."""
362 if inputpath:
363 inputbase, inputext = self.splitinputext(inputpath)
364 else:
365 inputext = None
366 if templatepath:
367 templatebase, templateext = self.splittemplateext(templatepath)
368 else:
369 templateext = None
370 if (inputext, templateext) in options.outputoptions:
371 return options.outputoptions[inputext, templateext]
372 elif (inputext, "*") in options.outputoptions:
373 outputformat, fileprocessor = options.outputoptions[inputext, "*"]
374 elif ("*", templateext) in options.outputoptions:
375 outputformat, fileprocessor = options.outputoptions["*", templateext]
376 elif ("*", "*") in options.outputoptions:
377 outputformat, fileprocessor = options.outputoptions["*", "*"]
378 elif (inputext, None) in options.outputoptions:
379 return options.outputoptions[inputext, None]
380 elif (None, templateext) in options.outputoptions:
381 return options.outputoptions[None, templateext]
382 elif ("*", None) in options.outputoptions:
383 outputformat, fileprocessor = options.outputoptions["*", None]
384 elif (None, "*") in options.outputoptions:
385 outputformat, fileprocessor = options.outputoptions[None, "*"]
386 else:
387 if self.usetemplates:
388 if inputext is None:
389 raise ValueError("don't know what to do with input format (no file extension), no template file")
390 elif templateext is None:
391 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
392 else:
393 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
394 else:
395 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
396 if outputformat == "*":
397 if inputext:
398 outputformat = inputext
399 elif templateext:
400 outputformat = templateext
401 elif ("*", "*") in options.outputoptions:
402 outputformat = None
403 else:
404 if self.usetemplates:
405 if templateext is None:
406 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
407 else:
408 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
409 else:
410 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
411 return outputformat, fileprocessor
412
414 """sets up a progress bar appropriate to the options and files"""
415 if options.progress in ('bar', 'verbose'):
416 self.progressbar = self.progresstypes[options.progress](0, len(allfiles))
417 print >> sys.stderr, "processing %d files..." % len(allfiles)
418 else:
419 self.progressbar = self.progresstypes[options.progress]()
420
427
429 """gets the absolute path to an output file"""
430 if options.recursiveoutput and options.output:
431 return os.path.join(options.output, outputpath)
432 else:
433 return outputpath
434
436 """gets the absolute path to a template file"""
437 if not options.recursivetemplate:
438 return templatepath
439 elif templatepath is not None and self.usetemplates and options.template:
440 return os.path.join(options.template, templatepath)
441 else:
442 return None
443
452
454 """recurse through directories and process files"""
455 if self.isrecursive(options.input, 'input') and getattr(options, "allowrecursiveinput", True):
456 if not self.isrecursive(options.output, 'output'):
457 if not options.output:
458 self.error(optparse.OptionValueError("No output directory given"))
459 try:
460 self.warning("Output directory does not exist. Attempting to create")
461 os.mkdir(options.output)
462 except IOError, e:
463 self.error(optparse.OptionValueError("Output directory does not exist, attempt to create failed"))
464 if isinstance(options.input, list):
465 inputfiles = self.recurseinputfilelist(options)
466 else:
467 inputfiles = self.recurseinputfiles(options)
468 else:
469 if options.input:
470 inputfiles = [os.path.basename(options.input)]
471 options.input = os.path.dirname(options.input)
472 else:
473 inputfiles = [options.input]
474 options.recursiveoutput = self.isrecursive(options.output, 'output') and getattr(options, "allowrecursiveoutput", True)
475 options.recursivetemplate = self.usetemplates and self.isrecursive(options.template, 'template') and getattr(options, "allowrecursivetemplate", True)
476 self.initprogressbar(inputfiles, options)
477 for inputpath in inputfiles:
478 try:
479 templatepath = self.gettemplatename(options, inputpath)
480
481
482 if options.recursivetemplate and templatepath is None and not self.allowmissingtemplate:
483 self.warning("No template at %s. Skipping %s." % (templatepath, inputpath))
484 continue
485 outputformat, fileprocessor = self.getoutputoptions(options, inputpath, templatepath)
486 fullinputpath = self.getfullinputpath(options, inputpath)
487 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
488 outputpath = self.getoutputname(options, inputpath, outputformat)
489 fulloutputpath = self.getfulloutputpath(options, outputpath)
490 if options.recursiveoutput and outputpath:
491 self.checkoutputsubdir(options, os.path.dirname(outputpath))
492 except Exception, error:
493 if isinstance(error, KeyboardInterrupt):
494 raise
495 self.warning("Couldn't handle input file %s" % inputpath, options, sys.exc_info())
496 continue
497 try:
498 success = self.processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
499 except Exception, error:
500 if isinstance(error, KeyboardInterrupt):
501 raise
502 self.warning("Error processing: input %s, output %s, template %s" % (fullinputpath, fulloutputpath, fulltemplatepath), options, sys.exc_info())
503 success = False
504 self.reportprogress(inputpath, success)
505 del self.progressbar
506
512
514 """opens the output file"""
515 if fulloutputpath is None:
516 return sys.stdout
517 return open(fulloutputpath, 'w')
518
520 """opens a temporary output file"""
521 return StringIO()
522
524 """write the temp outputfile to its final destination"""
525 outputfile.reset()
526 outputstring = outputfile.read()
527 outputfile = self.openoutputfile(options, fulloutputpath)
528 outputfile.write(outputstring)
529 outputfile.close()
530
532 """opens the template file (if required)"""
533 if fulltemplatepath is not None:
534 if os.path.isfile(fulltemplatepath):
535 return open(fulltemplatepath, 'r')
536 else:
537 self.warning("missing template file %s" % fulltemplatepath)
538 return None
539
540 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
541 """process an individual file"""
542 inputfile = self.openinputfile(options, fullinputpath)
543 if fulloutputpath and fulloutputpath in (fullinputpath, fulltemplatepath):
544 outputfile = self.opentempoutputfile(options, fulloutputpath)
545 tempoutput = True
546 else:
547 outputfile = self.openoutputfile(options, fulloutputpath)
548 tempoutput = False
549 templatefile = self.opentemplatefile(options, fulltemplatepath)
550 passthroughoptions = self.getpassthroughoptions(options)
551 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
552 if tempoutput:
553 self.warning("writing to temporary output...")
554 self.finalizetempoutputfile(options, outputfile, fulloutputpath)
555 return True
556 else:
557
558 if fulloutputpath and os.path.isfile(fulloutputpath):
559 outputfile.close()
560 os.unlink(fulloutputpath)
561 return False
562
567
568 - def mkdir(self, parent, subdir):
569 """makes a subdirectory (recursively if neccessary)"""
570 if not os.path.isdir(parent):
571 raise ValueError("cannot make child directory %r if parent %r does not exist" % (subdir, parent))
572 currentpath = parent
573 subparts = subdir.split(os.sep)
574 for part in subparts:
575 currentpath = os.path.join(currentpath, part)
576 if not os.path.isdir(currentpath):
577 os.mkdir(currentpath)
578
580 """checks to see if subdir under options.output needs to be created, creates if neccessary"""
581 fullpath = os.path.join(options.output, subdir)
582 if not os.path.isdir(fullpath):
583 self.mkdir(options.output, subdir)
584
586 """checks if this path has been excluded"""
587 basename = os.path.basename(inputpath)
588 for excludename in options.exclude:
589 if fnmatch.fnmatch(basename, excludename):
590 return True
591 return False
592
607
634
635 - def splitext(self, pathname):
636 """Splits L{pathname} into name and ext, and removes the extsep
637
638 @param pathname: A file path
639 @type pathname: string
640 @return: root, ext
641 @rtype: tuple
642 """
643 root, ext = os.path.splitext(pathname)
644 ext = ext.replace(os.extsep, "", 1)
645 return (root, ext)
646
648 """splits an inputpath into name and extension"""
649 return self.splitext(inputpath)
650
652 """splits a templatepath into name and extension"""
653 return self.splitext(templatepath)
654
656 """returns whether the given template exists..."""
657 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
658 return os.path.isfile(fulltemplatepath)
659
661 """gets an output filename based on the input filename"""
662 if not self.usetemplates:
663 return None
664 if not inputname or not options.recursivetemplate:
665 return options.template
666 inputbase, inputext = self.splitinputext(inputname)
667 if options.template:
668 for inputext1, templateext1 in options.outputoptions:
669 if inputext == inputext1:
670 if templateext1:
671 templatepath = inputbase + os.extsep + templateext1
672 if self.templateexists(options, templatepath):
673 return templatepath
674 if "*" in options.inputformats:
675 for inputext1, templateext1 in options.outputoptions:
676 if (inputext == inputext1) or (inputext1 == "*"):
677 if templateext1 == "*":
678 templatepath = inputname
679 if self.templateexists(options, templatepath):
680 return templatepath
681 elif templateext1:
682 templatepath = inputbase + os.extsep + templateext1
683 if self.templateexists(options, templatepath):
684 return templatepath
685 return None
686
688 """gets an output filename based on the input filename"""
689 if not inputname or not options.recursiveoutput:
690 return options.output
691 inputbase, inputext = self.splitinputext(inputname)
692 outputname = inputbase
693 if outputformat:
694 outputname += os.extsep + outputformat
695 return outputname
696
701