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