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
     25  #from translate.convert import prop2po, po2prop, odf2xliff, xliff2odf
     26
     27
     28  __all__ = ['converters', 'convertfile', 'UnknownExtensionError', 'UnsupportedConversionError']
     29
     30  # Turn into property to support lazy loading of things?
     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
     43 -    def __init__(self, afile):
     44          self.file = afile
     45
     46 -    def __str__(self):
     47          return 'Unable to find extension for file: %s' % (self.file)
     48
     49 -    def __unicode__(self):
     50          return unicode(str(self))
     51
     52
     53 -class UnsupportedConversionError(Exception):
     54
     55 -    def __init__(self, in_ext=None, out_ext=None, templ_ext=None):
     56          self.in_ext = in_ext
     57          self.out_ext = out_ext
     58          self.templ_ext = templ_ext
     59
     60 -    def __str__(self):
     61          msg = "Unsupported conversion from %s to %s" % (self.in_ext, self.out_ext)
     62          if self.templ_ext:
     63              msg += ' with template %s' % (self.templ_ext)
     64          return msg
     65
     66 -    def __unicode__(self):
     67          return unicode(str(self))
     68
     69
     70 -def get_extension(filename):
     71      path, fname = os.path.split(filename)
     72      ext = fname.split(os.extsep)[-1]
     73      if ext == fname:
     74          return None
     75      return ext
     76
     77
     78 -def get_converter(in_ext, out_ext=None, templ_ext=None):
     79      convert_candidates = None
     80      if templ_ext:
     81          if (in_ext, templ_ext) in converters:
     82              convert_candidates = converters[(in_ext, templ_ext)]
     83          else:
     84              raise UnsupportedConversionError(in_ext, out_ext, templ_ext)
     85      else:
     86          if in_ext in converters:
     87              convert_candidates = converters[in_ext]
     88          elif (in_ext,) in converters:
     89              convert_candidates = converters[(in_ext,)]
     90          else:
     91              raise UnsupportedConversionError(in_ext, out_ext)
     92
     93      convert_fn = None
     94      if not out_ext:
     95          out_ext, convert_fn = convert_candidates[0]
     96      else:
     97          for ext, func in convert_candidates:
     98              if ext == out_ext:
     99                  convert_fn = func
    100                  break
    101
    102      if not convert_fn:
    103          raise UnsupportedConversionError(in_ext, out_ext, templ_ext)
    104      return convert_fn
    105
    106
    107 -def get_output_extensions(ext):
    108      """Compiles a list of possible output extensions for the given input extension."""
    109      out_exts = []
    110      for key in converters:
    111          in_ext = key
    112          if isinstance(key, tuple):
    113              in_ext = key[0]
    114          if in_ext == ext:
    115              for out_ext, convert_fn in converters[key]:
    116                  out_exts.append(out_ext)
    117      return out_exts
    118
    119
    120 -def convert(inputfile, template=None, options=None, convert_options=None):
    121      """Convert the given input file to an appropriate output format, optionally
    122          using the given template file and further options.
    123
    124          If the output extension (format) cannot be inferred the first converter
    125          that can handle the input file (and the format/extension it gives as
    126          output) is used.
    127
    128          @type  inputfile: file
    129          @param inputfile: The input file to be converted
    130          @type  template: file
    131          @param template: Template file to use during conversion
    132          @type  options: dict (default: None)
    133          @param options: Valid options are:
    134              - in_ext: The extension (format) of the input file.
    135              - out_ext: The extension (format) to use for the output file.
    136              - templ_ext: The extension (format) of the template file.
    137              - in_fname: File name of the input file; used only to determine
    138                the input file extension (format).
    139              - templ_fname: File name of the template file; used only to
    140                determine the template file extension (format).
    141          @returns: a 2-tuple: The new output file (in a temporary directory) and
    142                    the extension (format) of the output file. The caller is
    143                    responsible for deleting the (temporary) output file."""
    144      in_ext, out_ext, templ_ext = None, None, None
    145
    146      # Get extensions from options
    147      if options is None:
    148          options = {}
    149      else:
    150          if 'in_ext' in options:
    151              in_ext = options['in_ext']
    152          if 'out_ext' in options:
    153              out_ext = options['out_ext']
    154          if template and 'templ_ext' in options:
    155              templ_ext = options['templ_ext']
    156
    157          # If we still do not have extensions, try and get it from the *_fname options
    158          if not in_ext and 'in_fname' in options:
    159              in_ext = get_extension(options['in_fname'])
    160          if template and not templ_ext and 'templ_fname' in options:
    161              templ_fname = get_extension(options['templ_fname'])
    162
    163      # If we still do not have extensions, get it from the file names
    164      if not in_ext and hasattr(inputfile, 'name'):
    165          in_ext = get_extension(inputfile.name)
    166      if template and not templ_ext and hasattr(template, 'name'):
    167          templ_ext = get_extension(template.name)
    168
    169      if not in_ext:
    170          raise UnknownExtensionError(inputfile)
    171      if template and not templ_ext:
    172          raise UnknownExtensionError(template)
    173
    174      out_ext_candidates = get_output_extensions(in_ext)
    175      if not out_ext_candidates:
    176          # No converters registered for the in_ext we have
    177          raise UnsupportedConversionError(in_ext=in_ext, templ_ext=templ_ext)
    178      if out_ext and out_ext not in out_ext_candidates:
    179          # If out_ext has a value at this point, it was given in options, so
    180          # we just take a second to make sure that the conversion is supported.
    181          raise UnsupportedConversionError(in_ext, out_ext, templ_ext)
    182
    183      if not out_ext and templ_ext in out_ext_candidates:
    184          # If we're using a template, chances are (pretty damn) good that the
    185          # output file will be of the same type
    186          out_ext = templ_ext
    187      else:
    188          # As a last resort, we'll just use the first possible output format
    189          out_ext = out_ext_candidates[0]
    190
    191      # XXX: We are abusing tempfile.mkstemp() below: we are only using it to
    192      #      obtain a temporary file name to use the normal open() with. This is
    193      #      done because a tempfile.NamedTemporaryFile simply gave too many
    194      #      issues when being closed (and deleted) by the rest of the toolkit
    195      #      (eg. TranslationStore.savefile()). Therefore none of mkstemp()'s
    196      #      security features are being utilised.
    197      import tempfile
    198      tempfd, tempfname = tempfile.mkstemp(prefix='ttk_convert', suffix=os.extsep + out_ext)
    199      os.close(tempfd)
    200      outputfile = open(tempfname, 'w')
    201
    202      if convert_options is None:
    203          convert_options = {}
    204      get_converter(in_ext, out_ext, templ_ext)(inputfile, outputfile, template, **convert_options)
    205      if hasattr(outputfile, 'closed') and hasattr(outputfile, 'close') and not outputfile.closed:
    206          outputfile.close()
    207      return outputfile, out_ext
    208