Package translate :: Package storage :: Module project
[hide private]
[frames] | no frames]

Source Code for Module translate.storage.project

  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  import os 
 22   
 23  from translate.convert import factory as convert_factory 
 24  from translate.storage.projstore import ProjectStore 
 25   
 26  __all__ = ['Project'] 
 27   
 28   
29 -def split_extensions(filename):
30 """Split the given filename into a name and extensions part. 31 The extensions part is defined by any sequence of extensions, where an 32 extension is a 3-letter, .-separated string or one of "po" or 33 "properties". If the file name consists entirely out of extensions, the 34 first part is assumed to be the file name and the rest extensions.""" 35 # FIXME: Isn't there a better place for this function? 36 # XXX: Make sure that all extensions supported in translate.convert.factory 37 # that are not 3 letters long are added to the first "if" statement in 38 # split_extensions() below. 39 filename_parts = filename.split(os.extsep) 40 extensions = [] 41 for part in reversed(filename_parts): 42 if len(part) != 3 and part not in ('po', 'properties'): 43 break 44 extensions.append(part) 45 if not extensions: 46 return filename, '' 47 extensions = [x for x in reversed(extensions)] 48 49 if len(extensions) == len(filename_parts): 50 extensions = extensions[1:] 51 return os.extsep.join(filename_parts[:-len(extensions)]), os.extsep.join(extensions)
52 53
54 -class Project(object):
55 """Manages a project store as well as the processes involved in a project 56 workflow.""" 57 58 # INITIALIZERS #
59 - def __init__(self, projstore=None):
60 if projstore is None: 61 projstore = ProjectStore() 62 self.store = projstore
63
64 - def __del__(self):
65 if self.store: 66 del self.store
67 68 # METHODS #
69 - def add_source(self, srcfile, src_fname=None):
70 """Proxy for C{self.store.append_sourcefile()}.""" 71 return self.store.append_sourcefile(srcfile, src_fname)
72
73 - def add_source_convert(self, srcfile, src_fname=None, convert_options=None, extension=None):
74 """Convenience method that calls L{add_source} and L{convert_forward} 75 and returns the results from both.""" 76 srcfile, srcfname = self.add_source(srcfile, src_fname) 77 transfile, transfname = self.convert_forward(srcfname, convert_options=convert_options) 78 return srcfile, srcfname, transfile, transfname
79
80 - def close(self):
81 """Proxy for C{self.store.close()}.""" 82 self.store.close()
83
84 - def convert_forward(self, input_fname, template=None, output_fname=None, **options):
85 """Convert the given input file to the next type in the process: 86 Source document (eg. ODT) -> Translation file (eg. XLIFF) -> 87 Translated document (eg. ODT). 88 89 @type input_fname: basestring 90 @param input_fname: The project name of the file to convert 91 @type convert_options: dict (optional) 92 @param convert_options: Passed as-is to 93 C{translate.convert.factory.convert()}. 94 @returns 2-tuple: the converted file object and it's project name.""" 95 inputfile = self.get_file(input_fname) 96 input_type = self.store.get_filename_type(input_fname) 97 98 if input_type == 'tgt': 99 raise ValueError('Cannot convert a target document further: %s' % (input_fname)) 100 101 templ_fname = None 102 if isinstance(template, basestring): 103 template, templ_fname = self.get_file(template) 104 105 if template and not templ_fname: 106 templ_fname = template.name 107 108 # Check if we can determine a template from the conversion map 109 if template is None: 110 convert_map = self.store.convert_map 111 if input_fname in convert_map: 112 templ_fname = convert_map[input_fname][1] 113 template = self.get_file(templ_fname) 114 elif input_type == 'trans': 115 # inputfile is a translatable file, so it needed to be converted 116 # from some input document. Let's try and use that document as a 117 # template for this conversion. 118 for in_name, (out_name, tmpl_name) in self.store.convert_map.items(): 119 if input_fname == out_name: 120 template, templ_fname = self.get_file(in_name), in_name 121 break 122 123 # Populate the conv_options dict with the options we can detect 124 conv_options = dict(in_fname=input_fname) 125 126 if input_fname in self.store.convert_map: 127 out_name, tmpl_name = self.store.convert_map[input_fname] 128 if out_name in self.store._files and options.get('overwrite_output', True): 129 self.remove_file(out_name) 130 131 converted_file, converted_ext = convert_factory.convert( 132 inputfile, 133 template=template, 134 options=conv_options, 135 convert_options=options.get('convert_options', None)) 136 137 # Determine the file name and path where the output should be moved. 138 if not output_fname: 139 _dir, fname = os.path.split(input_fname) 140 directory = '' 141 if hasattr(inputfile, 'name'): 142 # Prefer to put it in the same directory as the input file 143 directory, _fn = os.path.split(inputfile.name) 144 else: 145 # Otherwise put it in the current working directory 146 directory = os.getcwd() 147 output_fname = os.path.join(directory, fname) 148 output_fname, output_ext = split_extensions(output_fname) 149 output_ext_parts = output_ext.split(os.extsep) 150 151 # Add the output suffix, if supplied 152 if 'output_suffix' in options: 153 output_fname += options['output_suffix'] 154 155 # Check if we are in the situation where the output has an extension 156 # of, for example, .odt.xlf.odt. If so, we want to change that to only 157 # .odt. 158 if len(output_ext_parts) >= 2 and output_ext_parts[-2] == converted_ext: 159 output_ext_parts = output_ext_parts[:-1] 160 else: 161 output_ext_parts.append(converted_ext) 162 output_fname += os.extsep.join([''] + output_ext_parts) 163 164 if os.path.isfile(output_fname): 165 # If the output file already exist, we can't assume that it's safe 166 # to overwrite it. 167 os.unlink(converted_file.name) 168 raise IOError("Output file already exists: %s" % (output_fname)) 169 170 os.rename(converted_file.name, output_fname) 171 172 output_type = self.store.TYPE_INFO['next_type'][input_type] 173 outputfile, output_fname = self.store.append_file( 174 output_fname, None, ftype=output_type, delete_orig=True) 175 self.store.convert_map[input_fname] = (output_fname, templ_fname) 176 177 return outputfile, output_fname
178
179 - def export_file(self, fname, destfname):
180 """Export the file with the specified filename to the given destination. 181 This method will raise L{FileNotInProjectError} via the call to 182 L{ProjectStore.get_file()} if C{fname} is not found in the project.""" 183 open(destfname, 'w').write(self.store.get_file(fname).read())
184
185 - def get_file(self, fname):
186 """Proxy for C{self.store.get_file()}.""" 187 return self.store.get_file(fname)
188
189 - def get_proj_filename(self, realfname):
190 """Proxy for C{self.store.get_proj_filename()}.""" 191 return self.store.get_proj_filename(realfname)
192
193 - def get_real_filename(self, projfname):
194 """Try and find a real file name for the given project file name.""" 195 projfile = self.get_file(projfname) 196 rfname = getattr(projfile, 'name', getattr(projfile, 'filename', None)) 197 if rfname is None: 198 raise ValueError('Project file has no real file: %s' % (projfname)) 199 return rfname
200
201 - def remove_file(self, projfname, ftype=None):
202 """Proxy for C{self.store.remove_file()}.""" 203 self.store.remove_file(projfname, ftype)
204
205 - def save(self, filename=None):
206 """Proxy for C{self.store.save()}.""" 207 self.store.save(filename)
208
209 - def update_file(self, proj_fname, infile):
210 """Proxy for C{self.store.update_file()}.""" 211 self.store.update_file(proj_fname, infile)
212