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__.sver, 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 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 try:
458 self.warning("Output directory does not exist. Attempting to create")
459 os.mkdir(options.output)
460 except IOError, e:
461 self.error(optparse.OptionValueError("Output directory does not exist, attempt to create failed"))
462 if isinstance(options.input, list):
463 inputfiles = self.recurseinputfilelist(options)
464 else:
465 inputfiles = self.recurseinputfiles(options)
466 else:
467 if options.input:
468 inputfiles = [os.path.basename(options.input)]
469 options.input = os.path.dirname(options.input)
470 else:
471 inputfiles = [options.input]
472 options.recursiveoutput = self.isrecursive(options.output, 'output') and getattr(options, "allowrecursiveoutput", True)
473 options.recursivetemplate = self.usetemplates and self.isrecursive(options.template, 'template') and getattr(options, "allowrecursivetemplate", True)
474 self.initprogressbar(inputfiles, options)
475 for inputpath in inputfiles:
476 try:
477 templatepath = self.gettemplatename(options, inputpath)
478
479
480 if options.recursivetemplate and templatepath is None and not self.allowmissingtemplate:
481 self.warning("No template at %s. Skipping %s." % (templatepath, inputpath))
482 continue
483 outputformat, fileprocessor = self.getoutputoptions(options, inputpath, templatepath)
484 fullinputpath = self.getfullinputpath(options, inputpath)
485 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
486 outputpath = self.getoutputname(options, inputpath, outputformat)
487 fulloutputpath = self.getfulloutputpath(options, outputpath)
488 if options.recursiveoutput and outputpath:
489 self.checkoutputsubdir(options, os.path.dirname(outputpath))
490 except Exception, error:
491 if isinstance(error, KeyboardInterrupt):
492 raise
493 self.warning("Couldn't handle input file %s" % inputpath, options, sys.exc_info())
494 continue
495 try:
496 success = self.processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
497 except Exception, error:
498 if isinstance(error, KeyboardInterrupt):
499 raise
500 self.warning("Error processing: input %s, output %s, template %s" % (fullinputpath, fulloutputpath, fulltemplatepath), options, sys.exc_info())
501 success = False
502 self.reportprogress(inputpath, success)
503 del self.progressbar
504
510
512 """opens the output file"""
513 if fulloutputpath is None:
514 return sys.stdout
515 return open(fulloutputpath, 'w')
516
518 """opens a temporary output file"""
519 return StringIO()
520
522 """write the temp outputfile to its final destination"""
523 outputfile.reset()
524 outputstring = outputfile.read()
525 outputfile = self.openoutputfile(options, fulloutputpath)
526 outputfile.write(outputstring)
527 outputfile.close()
528
530 """opens the template file (if required)"""
531 if fulltemplatepath is not None:
532 if os.path.isfile(fulltemplatepath):
533 return open(fulltemplatepath, 'r')
534 else:
535 self.warning("missing template file %s" % fulltemplatepath)
536 return None
537
538 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
539 """process an individual file"""
540 inputfile = self.openinputfile(options, fullinputpath)
541 if fulloutputpath and fulloutputpath in (fullinputpath, fulltemplatepath):
542 outputfile = self.opentempoutputfile(options, fulloutputpath)
543 tempoutput = True
544 else:
545 outputfile = self.openoutputfile(options, fulloutputpath)
546 tempoutput = False
547 templatefile = self.opentemplatefile(options, fulltemplatepath)
548 passthroughoptions = self.getpassthroughoptions(options)
549 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
550 if tempoutput:
551 self.warning("writing to temporary output...")
552 self.finalizetempoutputfile(options, outputfile, fulloutputpath)
553 return True
554 else:
555
556 if fulloutputpath and os.path.isfile(fulloutputpath):
557 outputfile.close()
558 os.unlink(fulloutputpath)
559 return False
560
565
566 - def mkdir(self, parent, subdir):
567 """makes a subdirectory (recursively if neccessary)"""
568 if not os.path.isdir(parent):
569 raise ValueError("cannot make child directory %r if parent %r does not exist" % (subdir, parent))
570 currentpath = parent
571 subparts = subdir.split(os.sep)
572 for part in subparts:
573 currentpath = os.path.join(currentpath, part)
574 if not os.path.isdir(currentpath):
575 os.mkdir(currentpath)
576
578 """checks to see if subdir under options.output needs to be created, creates if neccessary"""
579 fullpath = os.path.join(options.output, subdir)
580 if not os.path.isdir(fullpath):
581 self.mkdir(options.output, subdir)
582
584 """checks if this path has been excluded"""
585 basename = os.path.basename(inputpath)
586 for excludename in options.exclude:
587 if fnmatch.fnmatch(basename, excludename):
588 return True
589 return False
590
605
632
633 - def splitext(self, pathname):
634 """Splits L{pathname} into name and ext, and removes the extsep
635
636 @param pathname: A file path
637 @type pathname: string
638 @return: root, ext
639 @rtype: tuple
640 """
641 root, ext = os.path.splitext(pathname)
642 ext = ext.replace(os.extsep, "", 1)
643 return (root, ext)
644
646 """splits an inputpath into name and extension"""
647 return self.splitext(inputpath)
648
650 """splits a templatepath into name and extension"""
651 return self.splitext(templatepath)
652
654 """returns whether the given template exists..."""
655 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
656 return os.path.isfile(fulltemplatepath)
657
659 """gets an output filename based on the input filename"""
660 if not self.usetemplates:
661 return None
662 if not inputname or not options.recursivetemplate:
663 return options.template
664 inputbase, inputext = self.splitinputext(inputname)
665 if options.template:
666 for inputext1, templateext1 in options.outputoptions:
667 if inputext == inputext1:
668 if templateext1:
669 templatepath = inputbase + os.extsep + templateext1
670 if self.templateexists(options, templatepath):
671 return templatepath
672 if "*" in options.inputformats:
673 for inputext1, templateext1 in options.outputoptions:
674 if (inputext == inputext1) or (inputext1 == "*"):
675 if templateext1 == "*":
676 templatepath = inputname
677 if self.templateexists(options, templatepath):
678 return templatepath
679 elif templateext1:
680 templatepath = inputbase + os.extsep + templateext1
681 if self.templateexists(options, templatepath):
682 return templatepath
683 return None
684
686 """gets an output filename based on the input filename"""
687 if not inputname or not options.recursiveoutput:
688 return options.output
689 inputbase, inputext = self.splitinputext(inputname)
690 outputname = inputbase
691 if outputformat:
692 outputname += os.extsep + outputformat
693 return outputname
694
699