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 try:
28 from cStringIO import StringIO
29 except ImportError:
30 from StringIO import StringIO
31
32 from translate.misc import progressbar
33 from translate import __version__
34
35
36 -class ManPageOption(optparse.Option, object):
37 ACTIONS = optparse.Option.ACTIONS + ("manpage",)
38
39 - def take_action(self, action, dest, opt, value, values, parser):
40 """take_action that can handle manpage as well as standard actions"""
41 if action == "manpage":
42 parser.print_manpage()
43 sys.exit(0)
44 return super(ManPageOption, self).take_action(action, dest, opt, value,
45 values, parser)
46
47
75
76
78 """A specialized Option Parser for recursing through directories."""
79
80 - def __init__(self, formats, usetemplates=False, allowmissingtemplate=False,
81 description=None):
99
101 return os.path.basename(sys.argv[0])
102
104 """creates a manpage option that allows the optionparser to generate a
105 manpage"""
106 manpageoption = ManPageOption(None, "--manpage", dest="manpage",
107 default=False, action="manpage",
108 help="output a manpage based on the help")
109 self.define_option(manpageoption)
110
112 """returns a formatted manpage"""
113 result = []
114 prog = self.get_prog_name()
115 formatprog = lambda x: x.replace("%prog", prog)
116 formatToolkit = lambda x: x.replace("%prog", "Translate Toolkit")
117 result.append('.\\" Autogenerated manpage\n')
118 result.append('.TH %s 1 "%s" "" "%s"\n' % (prog,
119 formatToolkit(self.version),
120 formatToolkit(self.version)))
121 result.append('.SH NAME\n')
122 result.append('%s \\- %s\n' % (self.get_prog_name(),
123 self.description.split('\n\n')[0]))
124 result.append('.SH SYNOPSIS\n')
125 result.append('.PP\n')
126 usage = "\\fB%prog "
127 usage += " ".join([self.getusageman(option) for option in self.option_list])
128 usage += "\\fP"
129 result.append('%s\n' % formatprog(usage))
130 description_lines = self.description.split('\n\n')[1:]
131 if description_lines:
132 result.append('.SH DESCRIPTION\n')
133 result.append('\n'.join(description_lines))
134 result.append('.SH OPTIONS\n')
135 ManHelpFormatter().store_option_strings(self)
136 result.append('.PP\n')
137 for option in self.option_list:
138 result.append('.TP\n')
139 result.append('%s\n' % option)
140 result.append('%s\n' % option.help)
141 return "".join(result)
142
143 - def print_manpage(self, file=None):
144 """outputs a manpage for the program using the help information"""
145 if file is None:
146 file = sys.stdout
147 file.write(self.format_manpage())
148
150 try:
151 import psyco
152 except ImportError:
153 return
154 psycomodes = ["none", "full", "profile"]
155 psycooption = optparse.Option(None, "--psyco", dest="psyco",
156 default=None, choices=psycomodes, metavar="MODE",
157 help="use psyco to speed up the operation, modes: %s" % (", ".join(psycomodes)))
158 self.define_option(psycooption)
159
161
162
163
164 if getattr(options, "psyco", "none") == "none":
165 return
166 try:
167 import psyco
168 except ImportError:
169 if options.psyco is not None:
170 self.warning("psyco unavailable", options, sys.exc_info())
171 return
172 if options.psyco is None:
173 options.psyco = "full"
174 if options.psyco == "full":
175 psyco.full()
176 elif options.psyco == "profile":
177 psyco.profile()
178
179 import encodings
180 psyco.cannotcompile(encodings.search_function)
181
183 """sets the usage string - if usage not given, uses getusagestring for
184 each option"""
185 if usage is None:
186 self.usage = "%prog " + " ".join([self.getusagestring(option) for option in self.option_list])
187 else:
188 super(RecursiveOptionParser, self).set_usage(usage)
189
190 - def warning(self, msg, options=None, exc_info=None):
191 """Print a warning message incorporating 'msg' to stderr and exit."""
192 if options:
193 if options.errorlevel == "traceback":
194 errorinfo = "\n".join(traceback.format_exception(exc_info[0],
195 exc_info[1], exc_info[2]))
196 elif options.errorlevel == "exception":
197 errorinfo = "\n".join(traceback.format_exception_only(exc_info[0], exc_info[1]))
198 elif options.errorlevel == "message":
199 errorinfo = str(exc_info[1])
200 else:
201 errorinfo = ""
202 if errorinfo:
203 msg += ": " + errorinfo
204 print >> sys.stderr, "\n%s: warning: %s" % (self.get_prog_name(), msg)
205
207 """returns the usage string for the given option"""
208 optionstring = "|".join(option._short_opts + option._long_opts)
209 if getattr(option, "optionalswitch", False):
210 optionstring = "[%s]" % optionstring
211 if option.metavar:
212 optionstring += " " + option.metavar
213 if getattr(option, "required", False):
214 return optionstring
215 else:
216 return "[%s]" % optionstring
217
219 """returns the usage string for the given option"""
220 optionstring = "\\fR|\\fP".join(option._short_opts + option._long_opts)
221 if getattr(option, "optionalswitch", False):
222 optionstring = "\\fR[\\fP%s\\fR]\\fP" % optionstring
223 if option.metavar:
224 optionstring += " \\fI%s\\fP" % option.metavar
225 if getattr(option, "required", False):
226 return optionstring
227 else:
228 return "\\fR[\\fP%s\\fR]\\fP" % optionstring
229
231 """defines the given option, replacing an existing one of the same short
232 name if neccessary..."""
233 for short_opt in option._short_opts:
234 if self.has_option(short_opt):
235 self.remove_option(short_opt)
236 for long_opt in option._long_opts:
237 if self.has_option(long_opt):
238 self.remove_option(long_opt)
239 self.add_option(option)
240
307
309 """sets the progress options"""
310 self.progresstypes = {
311 "none": progressbar.NoProgressBar,
312 "bar": progressbar.HashProgressBar,
313 "dots": progressbar.DotsProgressBar,
314 "names": progressbar.MessageProgressBar,
315 "verbose": progressbar.VerboseProgressBar,
316 }
317 progressoption = optparse.Option(None, "--progress", dest="progress",
318 default="bar",
319 choices=self.progresstypes.keys(), metavar="PROGRESS",
320 help="show progress as: %s" % (", ".join(self.progresstypes)))
321 self.define_option(progressoption)
322
324 """sets the errorlevel options"""
325 self.errorleveltypes = ["none", "message", "exception", "traceback"]
326 errorleveloption = optparse.Option(None, "--errorlevel",
327 dest="errorlevel", default="message",
328 choices=self.errorleveltypes, metavar="ERRORLEVEL",
329 help="show errorlevel as: %s" % (", ".join(self.errorleveltypes)))
330 self.define_option(errorleveloption)
331
342
343 - def isrecursive(self, fileoption, filepurpose='input'):
344 """checks if fileoption is a recursive file"""
345 if fileoption is None:
346 return False
347 elif isinstance(fileoption, list):
348 return True
349 else:
350 return os.path.isdir(fileoption)
351
353 """parses the command line options, handling implicit input/output
354 args"""
355 (options, args) = super(RecursiveOptionParser, self).parse_args(args, values)
356
357
358 if args and not options.input:
359 if len(args) > 1:
360 options.input = args[:-1]
361 args = args[-1:]
362 else:
363 options.input = args[0]
364 args = []
365 if args and not options.output:
366 options.output = args[-1]
367 args = args[:-1]
368 if args:
369 self.error("You have used an invalid combination of --input, --output and freestanding args")
370 if isinstance(options.input, list) and len(options.input) == 1:
371 options.input = options.input[0]
372 if options.input is None:
373 self.error("You need to give an inputfile or use - for stdin ; use --help for full usage instructions")
374 elif options.input == '-':
375 options.input = None
376 return (options, args)
377
379 """get the options required to pass to the filtermethod..."""
380 passthroughoptions = {}
381 for optionname in dir(options):
382 if optionname in self.passthrough:
383 passthroughoptions[optionname] = getattr(options, optionname)
384 return passthroughoptions
385
387 """works out which output format and processor method to use..."""
388 if inputpath:
389 inputbase, inputext = self.splitinputext(inputpath)
390 else:
391 inputext = None
392 if templatepath:
393 templatebase, templateext = self.splittemplateext(templatepath)
394 else:
395 templateext = None
396 if (inputext, templateext) in options.outputoptions:
397 return options.outputoptions[inputext, templateext]
398 elif (inputext, "*") in options.outputoptions:
399 outputformat, fileprocessor = options.outputoptions[inputext, "*"]
400 elif ("*", templateext) in options.outputoptions:
401 outputformat, fileprocessor = options.outputoptions["*", templateext]
402 elif ("*", "*") in options.outputoptions:
403 outputformat, fileprocessor = options.outputoptions["*", "*"]
404 elif (inputext, None) in options.outputoptions:
405 return options.outputoptions[inputext, None]
406 elif (None, templateext) in options.outputoptions:
407 return options.outputoptions[None, templateext]
408 elif ("*", None) in options.outputoptions:
409 outputformat, fileprocessor = options.outputoptions["*", None]
410 elif (None, "*") in options.outputoptions:
411 outputformat, fileprocessor = options.outputoptions[None, "*"]
412 else:
413 if self.usetemplates:
414 if inputext is None:
415 raise ValueError("don't know what to do with input format (no file extension), no template file")
416 elif templateext is None:
417 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
418 else:
419 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
420 else:
421 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
422 if outputformat == "*":
423 if inputext:
424 outputformat = inputext
425 elif templateext:
426 outputformat = templateext
427 elif ("*", "*") in options.outputoptions:
428 outputformat = None
429 else:
430 if self.usetemplates:
431 if templateext is None:
432 raise ValueError("don't know what to do with input format %s, no template file" % (os.extsep + inputext))
433 else:
434 raise ValueError("don't know what to do with input format %s, template format %s" % (os.extsep + inputext, os.extsep + templateext))
435 else:
436 raise ValueError("don't know what to do with input format %s" % os.extsep + inputext)
437 return outputformat, fileprocessor
438
440 """sets up a progress bar appropriate to the options and files"""
441 if options.progress in ('bar', 'verbose'):
442 self.progressbar = self.progresstypes[options.progress](0, len(allfiles))
443 print >> sys.stderr, "processing %d files..." % len(allfiles)
444 else:
445 self.progressbar = self.progresstypes[options.progress]()
446
453
455 """gets the absolute path to an output file"""
456 if options.recursiveoutput and options.output:
457 return os.path.join(options.output, outputpath)
458 else:
459 return outputpath
460
462 """gets the absolute path to a template file"""
463 if not options.recursivetemplate:
464 return templatepath
465 elif templatepath is not None and self.usetemplates and options.template:
466 return os.path.join(options.template, templatepath)
467 else:
468 return None
469
480
482 """recurse through directories and process files"""
483 if self.isrecursive(options.input, 'input') and getattr(options, "allowrecursiveinput", True):
484 if not self.isrecursive(options.output, 'output'):
485 if not options.output:
486 self.error(optparse.OptionValueError("No output directory given"))
487 try:
488 self.warning("Output directory does not exist. Attempting to create")
489 os.mkdir(options.output)
490 except IOError, e:
491 self.error(optparse.OptionValueError("Output directory does not exist, attempt to create failed"))
492 if isinstance(options.input, list):
493 inputfiles = self.recurseinputfilelist(options)
494 else:
495 inputfiles = self.recurseinputfiles(options)
496 else:
497 if options.input:
498 inputfiles = [os.path.basename(options.input)]
499 options.input = os.path.dirname(options.input)
500 else:
501 inputfiles = [options.input]
502 options.recursiveoutput = self.isrecursive(options.output, 'output') and getattr(options, "allowrecursiveoutput", True)
503 options.recursivetemplate = self.usetemplates and self.isrecursive(options.template, 'template') and getattr(options, "allowrecursivetemplate", True)
504 self.initprogressbar(inputfiles, options)
505 for inputpath in inputfiles:
506 try:
507 templatepath = self.gettemplatename(options, inputpath)
508
509
510 if options.recursivetemplate and templatepath is None and not self.allowmissingtemplate:
511 self.warning("No template at %s. Skipping %s." % (templatepath, inputpath))
512 continue
513 outputformat, fileprocessor = self.getoutputoptions(options, inputpath, templatepath)
514 fullinputpath = self.getfullinputpath(options, inputpath)
515 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
516 outputpath = self.getoutputname(options, inputpath, outputformat)
517 fulloutputpath = self.getfulloutputpath(options, outputpath)
518 if options.recursiveoutput and outputpath:
519 self.checkoutputsubdir(options, os.path.dirname(outputpath))
520 except Exception, error:
521 if isinstance(error, KeyboardInterrupt):
522 raise
523 self.warning("Couldn't handle input file %s" % inputpath, options, sys.exc_info())
524 continue
525 try:
526 success = self.processfile(fileprocessor, options,
527 fullinputpath, fulloutputpath,
528 fulltemplatepath)
529 except Exception, error:
530 if isinstance(error, KeyboardInterrupt):
531 raise
532 self.warning("Error processing: input %s, output %s, template %s" % (fullinputpath, fulloutputpath, fulltemplatepath), options, sys.exc_info())
533 success = False
534 self.reportprogress(inputpath, success)
535 del self.progressbar
536
542
544 """opens the output file"""
545 if fulloutputpath is None:
546 return sys.stdout
547 return open(fulloutputpath, 'w')
548
550 """opens a temporary output file"""
551 return StringIO()
552
554 """write the temp outputfile to its final destination"""
555 outputfile.reset()
556 outputstring = outputfile.read()
557 outputfile = self.openoutputfile(options, fulloutputpath)
558 outputfile.write(outputstring)
559 outputfile.close()
560
562 """opens the template file (if required)"""
563 if fulltemplatepath is not None:
564 if os.path.isfile(fulltemplatepath):
565 return open(fulltemplatepath, 'r')
566 else:
567 self.warning("missing template file %s" % fulltemplatepath)
568 return None
569
570 - def processfile(self, fileprocessor, options, fullinputpath,
571 fulloutputpath, fulltemplatepath):
572 """process an individual file"""
573 inputfile = self.openinputfile(options, fullinputpath)
574 if fulloutputpath and fulloutputpath in (fullinputpath, fulltemplatepath):
575 outputfile = self.opentempoutputfile(options, fulloutputpath)
576 tempoutput = True
577 else:
578 outputfile = self.openoutputfile(options, fulloutputpath)
579 tempoutput = False
580 templatefile = self.opentemplatefile(options, fulltemplatepath)
581 passthroughoptions = self.getpassthroughoptions(options)
582 if fileprocessor(inputfile, outputfile, templatefile,
583 **passthroughoptions):
584 if tempoutput:
585 self.warning("writing to temporary output...")
586 self.finalizetempoutputfile(options, outputfile, fulloutputpath)
587 return True
588 else:
589
590 if fulloutputpath and os.path.isfile(fulloutputpath):
591 outputfile.close()
592 os.unlink(fulloutputpath)
593 return False
594
599
600 - def mkdir(self, parent, subdir):
601 """makes a subdirectory (recursively if neccessary)"""
602 if not os.path.isdir(parent):
603 raise ValueError("cannot make child directory %r if parent %r does not exist" % (subdir, parent))
604 currentpath = parent
605 subparts = subdir.split(os.sep)
606 for part in subparts:
607 currentpath = os.path.join(currentpath, part)
608 if not os.path.isdir(currentpath):
609 os.mkdir(currentpath)
610
612 """checks to see if subdir under options.output needs to be created,
613 creates if neccessary"""
614 fullpath = os.path.join(options.output, subdir)
615 if not os.path.isdir(fullpath):
616 self.mkdir(options.output, subdir)
617
619 """checks if this path has been excluded"""
620 basename = os.path.basename(inputpath)
621 for excludename in options.exclude:
622 if fnmatch.fnmatch(basename, excludename):
623 return True
624 return False
625
640
667
668 - def splitext(self, pathname):
669 """Splits L{pathname} into name and ext, and removes the extsep
670
671 @param pathname: A file path
672 @type pathname: string
673 @return: root, ext
674 @rtype: tuple
675 """
676 root, ext = os.path.splitext(pathname)
677 ext = ext.replace(os.extsep, "", 1)
678 return (root, ext)
679
681 """splits an inputpath into name and extension"""
682 return self.splitext(inputpath)
683
685 """splits a templatepath into name and extension"""
686 return self.splitext(templatepath)
687
689 """returns whether the given template exists..."""
690 fulltemplatepath = self.getfulltemplatepath(options, templatepath)
691 return os.path.isfile(fulltemplatepath)
692
694 """gets an output filename based on the input filename"""
695 if not self.usetemplates:
696 return None
697 if not inputname or not options.recursivetemplate:
698 return options.template
699 inputbase, inputext = self.splitinputext(inputname)
700 if options.template:
701 for inputext1, templateext1 in options.outputoptions:
702 if inputext == inputext1:
703 if templateext1:
704 templatepath = inputbase + os.extsep + templateext1
705 if self.templateexists(options, templatepath):
706 return templatepath
707 if "*" in options.inputformats:
708 for inputext1, templateext1 in options.outputoptions:
709 if (inputext == inputext1) or (inputext1 == "*"):
710 if templateext1 == "*":
711 templatepath = inputname
712 if self.templateexists(options, templatepath):
713 return templatepath
714 elif templateext1:
715 templatepath = inputbase + os.extsep + templateext1
716 if self.templateexists(options, templatepath):
717 return templatepath
718 return None
719
721 """gets an output filename based on the input filename"""
722 if not inputname or not options.recursiveoutput:
723 return options.output
724 inputbase, inputext = self.splitinputext(inputname)
725 outputname = inputbase
726 if outputformat:
727 outputname += os.extsep + outputformat
728 return outputname
729
734