PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/translate/convert/factory.py

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