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 = {
282 "none": progressbar.NoProgressBar,
283 "bar": progressbar.HashProgressBar,
284 "dots": progressbar.DotsProgressBar,
285 "names": progressbar.MessageProgressBar,
286 "verbose": progressbar.VerboseProgressBar
287 }
288 progressoption = optparse.Option(None, "--progress", dest="progress", default="bar",
289 choices = self.progresstypes.keys(), metavar="PROGRESS",
290 help="show progress as: %s" % (", ".join(self.progresstypes)))
291 self.define_option(progressoption)
292
294 """sets the errorlevel options"""
295 self.errorleveltypes = ["none", "message", "exception", "traceback"]
296 errorleveloption = optparse.Option(None, "--errorlevel", dest="errorlevel", default="message",
297 choices = self.errorleveltypes, metavar="ERRORLEVEL",
298 help="show errorlevel as: %s" % (", ".join(self.errorleveltypes)))
299 self.define_option(errorleveloption)
300
311
312 - def isrecursive(self, fileoption, filepurpose='input'):
313 """checks if fileoption is a recursive file"""
314 if fileoption is None:
315 return False
316 elif isinstance(fileoption, list):
317 return True
318 else:
319 return os.path.isdir(fileoption)
320
322 """parses the command line options, handling implicit input/output args"""
323 (options, args) = super(RecursiveOptionParser, self).parse_args(args, values)
324
325 if args and not options.input:
326 if len(args) > 1:
327 options.input = args[:-1]
328 args = args[-1:]
329 else:
330 options.input = args[0]
331 args = []
332 if args and not options.output:
333 options.output = args[-1]
334 args = args[:-1]
335 if args:
336 self.error("You have used an invalid combination of --input, --output and freestanding args")
337 if isinstance(options.input, list) and len(options.input) == 1:
338 options.input = options.input[0]
339 if options.input is None:
340 self.error("You need to give an inputfile or use - for stdin ; use --help for full usage instructions")
341 elif options.input == '-':
342 options.input = None
343 return (options, args)
344
346 """get the options required to pass to the filtermethod..."""
347 passthroughoptions = {}
348 for optionname in dir(options):
349 if optionname in self.passthrough:
350 passthroughoptions[optionname] = getattr(options, optionname)
351 return passthroughoptions
352
354 """works out which output format and processor method to use..."""
355 if inputpath:
356 inputbase, inputext = self.splitinputext(inputpath)
357 else:
358 inputext = None
359 if templatepath:
360 templatebase, templateext = self.splittemplateext(templatepath)
361 else:
362 templateext = None
363 if (inputext, templateext) in options.outputoptions:
364 return options.outputoptions[inputext, templateext]
365 elif (inputext, "*") in options.outputoptions:
366 outputformat, fileprocessor = options.outputoptions[inputext, "*"]
367 elif ("*", templateext) in options.outputoptions:
368 outputformat, fileprocessor = options.outputoptions["*", templateext]
369 elif ("*", "*") in options.outputoptions:
370 outputformat, fileprocessor = options.outputoptions["*", "*"]
371 elif (inputext, None) in options.outputoptions:
372 return options.outputoptions[inputext, None]
373 elif (None, templateext) in options.outputoptions:
374 return options.outputoptions[None, templateext]
375 elif ("*", None) in options.outputoptions:
376 outputformat, fileprocessor = options.outputoptions["*", None]
377 elif (None, "*") in options.outputoptions:
378 outputformat, fileprocessor = options.outputoptions[None, "*"]
379 else:
380 if self.usetemplates:
381 if inputext is None:
382 raise ValueError("don't know what to do with input format (no file extension), no template file")
383 elif templateext is None:
384 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
385 else:
386 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
387 else:
388 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
389 if outputformat == "*":
390 if inputext:
391 outputformat = inputext
392 elif templateext:
393 outputformat = templateext
394 elif ("*", "*") in options.outputoptions:
395 outputformat = None
396 else:
397 if self.usetemplates:
398 if templateext is None:
399 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
400 else:
401 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
402 else:
403 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
404 return outputformat, fileprocessor
405
407 """sets up a progress bar appropriate to the options and files"""
408 if options.progress in ('bar', 'verbose'):
409 self.progressbar = self.progresstypes[options.progress](0, len(allfiles))
410 print >> sys.stderr, "processing %d files..." % len(allfiles)
411 else:
412 self.progressbar = self.progresstypes[options.progress]()
413
420
422 """gets the absolute path to an output file"""
423 if options.recursiveoutput and options.output:
424 return os.path.join(options.output, outputpath)
425 else:
426 return outputpath
427
429 """gets the absolute path to a template file"""
430 if not options.recursivetemplate:
431 return templatepath
432 elif templatepath is not None and self.usetemplates and options.template:
433 return os.path.join(options.template, templatepath)
434 else:
435 return None
436
445
447 """recurse through directories and process files"""
448 if self.isrecursive(options.input, 'input') and getattr(options, "allowrecursiveinput", True):
449 if not self.isrecursive(options.output, 'output'):
450 try:
451 self.warning("Output directory does not exist. Attempting to create")
452 os.mkdir(options.output)
453 except IOError, e:
454 self.error(optparse.OptionValueError("Output directory does not exist, attempt to create failed"))
455 if isinstance(options.input, list):
456 inputfiles = self.recurseinputfilelist(options)
457 else:
458 inputfiles = self.recurseinputfiles(options)
459 else:
460 if options.input:
461 inputfiles = [os.path.basename(options.input)]
462 options.input = os.path.dirname(options.input)
463 else:
464 inputfiles = [options.input]
465 options.recursiveoutput = self.isrecursive(options.output, 'output') and getattr(options, "allowrecursiveoutput", True)
466 options.recursivetemplate = self.usetemplates and self.isrecursive(options.template, 'template') and getattr(options, "allowrecursivetemplate", True)
467 self.initprogressbar(inputfiles, options)
468 for inputpath in inputfiles:
469 try:
470 templatepath = self.gettemplatename(options, inputpath)
471
472
473 if options.recursivetemplate and templatepath is None and not self.allowmissingtemplate:
474 self.warning("No template at %s. Skipping %s." % (templatepath, inputpath))
475 continue
476 outputformat, fileprocessor = self.getoutputoptions(options, inputpath, templatepath)
477 fullinputpath = self.getfullinputpath(options, inputpath)
478 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
479 outputpath = self.getoutputname(options, inputpath, outputformat)
480 fulloutputpath = self.getfulloutputpath(options, outputpath)
481 if options.recursiveoutput and outputpath:
482 self.checkoutputsubdir(options, os.path.dirname(outputpath))
483 except Exception, error:
484 if isinstance(error, KeyboardInterrupt):
485 raise
486 self.warning("Couldn't handle input file %s" % inputpath, options, sys.exc_info())
487 continue
488 try:
489 success = self.processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
490 except Exception, error:
491 if isinstance(error, KeyboardInterrupt):
492 raise
493 self.warning("Error processing: input %s, output %s, template %s" % (fullinputpath, fulloutputpath, fulltemplatepath), options, sys.exc_info())
494 success = False
495 self.reportprogress(inputpath, success)
496 del self.progressbar
497
503
505 """opens the output file"""
506 if fulloutputpath is None:
507 return sys.stdout
508 return open(fulloutputpath, 'w')
509
511 """opens a temporary output file"""
512 return StringIO()
513
515 """write the temp outputfile to its final destination"""
516 outputfile.reset()
517 outputstring = outputfile.read()
518 outputfile = self.openoutputfile(options, fulloutputpath)
519 outputfile.write(outputstring)
520 outputfile.close()
521
523 """opens the template file (if required)"""
524 if fulltemplatepath is not None:
525 if os.path.isfile(fulltemplatepath):
526 return open(fulltemplatepath, 'r')
527 else:
528 self.warning("missing template file %s" % fulltemplatepath)
529 return None
530
531 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
532 """process an individual file"""
533 inputfile = self.openinputfile(options, fullinputpath)
534 if fulloutputpath and fulloutputpath in (fullinputpath, fulltemplatepath):
535 outputfile = self.opentempoutputfile(options, fulloutputpath)
536 tempoutput = True
537 else:
538 outputfile = self.openoutputfile(options, fulloutputpath)
539 tempoutput = False
540 templatefile = self.opentemplatefile(options, fulltemplatepath)
541 passthroughoptions = self.getpassthroughoptions(options)
542 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
543 if tempoutput:
544 self.warning("writing to temporary output...")
545 self.finalizetempoutputfile(options, outputfile, fulloutputpath)
546 return True
547 else:
548
549 if fulloutputpath and os.path.isfile(fulloutputpath):
550 outputfile.close()
551 os.unlink(fulloutputpath)
552 return False
553
558
559 - def mkdir(self, parent, subdir):
560 """makes a subdirectory (recursively if neccessary)"""
561 if not os.path.isdir(parent):
562 raise ValueError("cannot make child directory %r if parent %r does not exist" % (subdir, parent))
563 currentpath = parent
564 subparts = subdir.split(os.sep)
565 for part in subparts:
566 currentpath = os.path.join(currentpath, part)
567 if not os.path.isdir(currentpath):
568 os.mkdir(currentpath)
569
571 """checks to see if subdir under options.output needs to be created, creates if neccessary"""
572 fullpath = os.path.join(options.output, subdir)
573 if not os.path.isdir(fullpath):
574 self.mkdir(options.output, subdir)
575
577 """checks if this path has been excluded"""
578 basename = os.path.basename(inputpath)
579 for excludename in options.exclude:
580 if fnmatch.fnmatch(basename, excludename):
581 return True
582 return False
583
598
625
626 - def splitext(self, pathname):
627 """splits into name and ext, and removes the extsep"""
628 root, ext = os.path.splitext(pathname)
629 ext = ext.replace(os.extsep, "", 1)
630 return (root, ext)
631
633 """splits an inputpath into name and extension"""
634 return self.splitext(inputpath)
635
637 """splits a templatepath into name and extension"""
638 return self.splitext(templatepath)
639
641 """returns whether the given template exists..."""
642 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
643 return os.path.isfile(fulltemplatepath)
644
646 """gets an output filename based on the input filename"""
647 if not self.usetemplates: return None
648 if not inputname or not options.recursivetemplate: return options.template
649 inputbase, inputext = self.splitinputext(inputname)
650 if options.template:
651 for inputext1, templateext1 in options.outputoptions:
652 if inputext == inputext1:
653 if templateext1:
654 templatepath = inputbase + os.extsep + templateext1
655 if self.templateexists(options, templatepath):
656 return templatepath
657 if "*" in options.inputformats:
658 for inputext1, templateext1 in options.outputoptions:
659 if (inputext == inputext1) or (inputext1 == "*"):
660 if templateext1 == "*":
661 templatepath = inputname
662 if self.templateexists(options, templatepath):
663 return templatepath
664 elif templateext1:
665 templatepath = inputbase + os.extsep + templateext1
666 if self.templateexists(options, templatepath):
667 return templatepath
668 return None
669
671 """gets an output filename based on the input filename"""
672 if not inputname or not options.recursiveoutput: return options.output
673 inputbase, inputext = self.splitinputext(inputname)
674 outputname = inputbase
675 if outputformat:
676 outputname += os.extsep + outputformat
677 return outputname
678
683