Package translate :: Package convert :: Module factory

[hide private]
[frames] | no frames]

# Source Code for Module translate.convert.factory

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  #
      4  # Copyright 2010 Zuza Software Foundation
      5  #
      6  # This file is part of the Translate Toolkit.
      7  #
      8  # This program is free software; you can redistribute it and/or modify
      9  # it under the terms of the GNU General Public License as published by
     10  # the Free Software Foundation; either version 2 of the License, or
     11  # (at your option) any later version.
     12  #
     13  # This program is distributed in the hope that it will be useful,
     14  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  # GNU General Public License for more details.
     17  #
     18  # You should have received a copy of the GNU General Public License
     19  # along with this program; if not, see <http://www.gnu.org/licenses/>.
     20
     21  """Factory methods to convert supported input files to supported translatable files."""
     22
     23  import os
     24  import tempfile
     25
     26  from translate.convert import prop2po, po2prop, odf2xliff, xliff2odf
     27
     28
     29  __all__ = ['converters', 'convertfile', 'UnknownExtensionError', 'UnsupportedConversionError']
     30
     31  converters = {}
     32  for module in (prop2po, po2prop, odf2xliff, xliff2odf):
     33      if not hasattr(module, 'formats'):
     34          continue
     35      for extension in module.formats:
     36          if extension not in converters:
     37              converters[extension] = []
     38          converters[extension].append(module.formats[extension])
     39
     40
     41 -class UnknownExtensionError(Exception):
     42 -    def __init__(self, afile):
     43          self.file = afile
     44
     45 -    def __str__(self):
     46          return 'Unable to find extension for file: %s' % (self.file)
     47
     48 -    def __unicode__(self):
     49          return unicode(str(self))
     50
     51 -class UnsupportedConversionError(Exception):
     52 -    def __init__(self, in_ext=None, out_ext=None, templ_ext=None):
     53          self.in_ext = in_ext
     54          self.out_ext = out_ext
     55          self.templ_ext = templ_ext
     56
     57 -    def __str__(self):
     58          msg = "Unsupported conversion from %s to %s" % (self.in_ext, self.out_ext)
     59          if self.templ_ext:
     60              msg += ' with template %s' % (self.templ_ext)
     61          return msg
     62
     63 -    def __unicode__(self):
     64          return unicode(str(self))
     65
     66
     67 -def get_extension(filename):
     68      path, fname = os.path.split(filename)
     69      ext = fname.split(os.extsep)[-1]
     70      if ext == fname:
     71          return None
     72      return ext
     73
     74 -def get_converter(in_ext, out_ext=None, templ_ext=None):
     75      convert_candidates = None
     76      if templ_ext:
     77          if (in_ext, templ_ext) in converters:
     78              convert_candidates = converters[(in_ext, templ_ext)]
     79          else:
     80              raise UnsupportedConversionError(in_ext, out_ext, templ_ext)
     81      else:
     82          if in_ext in converters:
     83              convert_candidates = converters[in_ext]
     84          elif (in_ext,) in converters:
     85              convert_candidates = converters[(in_ext,)]
     86          else:
     87              raise UnsupportedConversionError(in_ext, out_ext)
     88
     89      convert_fn = None
     90      if not out_ext:
     91          out_ext, convert_fn = convert_candidates[0]
     92      else:
     93          for ext, func in convert_candidates:
     94              if ext == out_ext:
     95                  convert_fn = func
     96                  break
     97
     98      if not convert_fn:
     99          raise UnsupportedConversionError(in_ext, out_ext, templ_ext)
    100      return convert_fn
    101
    102 -def get_output_extensions(ext):
    103      """Compiles a list of possible output extensions for the given input extension."""
    104      out_exts = []
    105      for key in converters:
    106          in_ext = key
    107          if isinstance(key, tuple):
    108              in_ext = key[0]
    109          if in_ext == ext:
    110              for out_ext, convert_fn in converters[key]:
    111                  out_exts.append(out_ext)
    112      return out_exts
    113
    114 -def convert(inputfile, template=None, options=None, convert_options=None):
    115      """Convert the given input file to an appropriate output format, optionally
    116          using the given template file and further options.
    117
    118          If the output extension (format) cannot be inferred the first converter
    119          that can handle the input file (and the format/extension it gives as
    120          output) is used.
    121
    122          @type  inputfile: file
    123          @param inputfile: The input file to be converted
    124          @type  template: file
    125          @param template: Template file to use during conversion
    126          @type  options: dict (default: None)
    127          @param options: Valid options are:
    128              - in_ext: The extension (format) of the input file.
    129              - out_ext: The extension (format) to use for the output file.
    130              - templ_ext: The extension (format) of the template file.
    131              - in_fname: File name of the input file; used only to determine
    132                the input file extension (format).
    133              - templ_fname: File name of the template file; used only to
    134                determine the template file extension (format).
    135          @returns: a 2-tuple: The new output file (in a temporary directory) and
    136                    the extension (format) of the output file. The caller is
    137                    responsible for deleting the (temporary) output file."""
    138      in_ext, out_ext, templ_ext = None, None, None
    139
    140      # Get extensions from options
    141      if options is None:
    142          options = {}
    143      else:
    144          if 'in_ext' in options:
    145              in_ext = options['in_ext']
    146          if 'out_ext' in options:
    147              out_ext = options['out_ext']
    148          if template and 'templ_ext' in options:
    149              templ_ext = options['templ_ext']
    150
    151          # If we still do not have extensions, try and get it from the *_fname options
    152          if not in_ext and 'in_fname' in options:
    153              in_ext = get_extension(options['in_fname'])
    154          if template and not templ_ext and 'templ_fname' in options:
    155              templ_fname = get_extension(options['templ_fname'])
    156
    157      # If we still do not have extensions, get it from the file names
    158      if not in_ext and hasattr(inputfile, 'name'):
    159          in_ext = get_extension(inputfile.name)
    160      if template and not templ_ext and hasattr(template, 'name'):
    161          templ_ext = get_extension(template.name)
    162
    163      if not in_ext:
    164          raise UnknownExtensionError(inputfile)
    165      if template and not templ_ext:
    166          raise UnknownExtensionError(template)
    167
    168      out_ext_candidates = get_output_extensions(in_ext)
    169      if not out_ext_candidates:
    170          # No converters registered for the in_ext we have
    171          raise UnsupportedConversionError(in_ext=in_ext, templ_ext=templ_ext)
    172      if out_ext and out_ext not in out_ext_candidates:
    173          # If out_ext has a value at this point, it was given in options, so
    174          # we just take a second to make sure that the conversion is supported.
    175          raise UnsupportedConversionError(in_ext, out_ext, templ_ext)
    176
    177      if not out_ext and templ_ext in out_ext_candidates:
    178          # If we're using a template, chances are (pretty damn) good that the
    179          # output file will be of the same type
    180          out_ext = templ_ext
    181      else:
    182          # As a last resort, we'll just use the first possible output format
    183          out_ext = out_ext_candidates[0]
    184
    185      # XXX: We are abusing tempfile.mkstemp() below: we are only using it to
    186      #      obtain a temporary file name to use the normal open() with. This is
    187      #      done because a tempfile.NamedTemporaryFile simply gave too many
    188      #      issues when being closed (and deleted) by the rest of the toolkit
    189      #      (eg. TranslationStore.savefile()). Therefore none of mkstemp()'s
    190      #      security features are being utilised.
    191      tempfd, tempfname = tempfile.mkstemp(prefix='ttk_convert', suffix=os.extsep+out_ext)
    192      os.close(tempfd)
    193      outputfile = open(tempfname, 'w')
    194
    195      if convert_options is None:
    196          convert_options = {}
    197      get_converter(in_ext, out_ext, templ_ext)(inputfile, outputfile, template, **convert_options)
    198      if hasattr(outputfile, 'closed') and hasattr(outputfile, 'close') and not outputfile.closed:
    199          outputfile.close()
    200      return outputfile, out_ext
    201