1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import os
22 import shutil
23 import tempfile
24 from zipfile import ZipFile
25
26 from translate.storage.projstore import *
27
28 __all__ = ['BundleProjectStore', 'InvalidBundleError']
33
36 """Represents a translate project bundle (zip archive)."""
37
38
40 super(BundleProjectStore, self).__init__()
41 self._tempfiles = {}
42 if fname and os.path.isfile(fname):
43 self.load(fname)
44 else:
45 self.zip = ZipFile(fname, 'w')
46 self.save()
47 self.zip.close()
48 self.zip = ZipFile(fname, 'a')
49
50
51
52 @classmethod
67
68
69
70 - def append_file(self, afile, fname, ftype='trans', delete_orig=False):
71 """Append the given file to the project with the given filename, marked
72 to be of type C{ftype} ('src', 'trans', 'tgt').
73
74 @param delete_orig: If C{True}, as set by
75 L{project.convert_forward()}, C{afile} is
76 deleted after appending, if possible.
77 NOTE: For this implementation, the appended file will be deleted
78 from disk if C{delete_orig} is C{True}."""
79 if fname and fname in self.zip.namelist():
80 raise ValueError("File already in bundle archive: %s" % (fname))
81 if not fname and isinstance(afile, basestring) and afile in self.zip.namelist():
82 raise ValueError("File already in bundle archive: %s" % (afile))
83
84 afile, fname = super(BundleProjectStore, self).append_file(afile, fname, ftype)
85 self._zip_add(fname, afile)
86
87 if delete_orig and hasattr(afile, 'name') and afile.name not in self._tempfiles:
88 try:
89 os.unlink(afile.name)
90 except Exception:
91 pass
92
93 return self.get_file(fname), fname
94
96 """Remove the file with the given project name from the project."""
97 super(BundleProjectStore, self).remove_file(fname, ftype)
98 self._zip_delete([fname])
99 tempfiles = [tmpf for tmpf, prjf in self._tempfiles.iteritems() if prjf == fname]
100 if tempfiles:
101 for tmpf in tempfiles:
102 try:
103 os.unlink(tmpf)
104 except Exception:
105 pass
106 del self._tempfiles[tmpf]
107
112
114 """Clean up our mess: remove temporary files."""
115 for tempfname in self._tempfiles:
116 if os.path.isfile(tempfname):
117 os.unlink(tempfname)
118 self._tempfiles = {}
119
121 """Retrieve a project file (source, translation or target file) from the
122 project archive."""
123 retfile = None
124 if fname in self._files or fname in self.zip.namelist():
125
126 tempfname = [tfn for tfn in self._tempfiles if self._tempfiles[tfn] == fname]
127 if tempfname and os.path.isfile(tempfname[0]):
128 tempfname = tempfname[0]
129 else:
130 tempfname = ''
131 if not tempfname:
132
133 zfile = self.zip.open(fname)
134 tempfname = os.path.split(fname)[-1]
135 tempfd, tempfname = tempfile.mkstemp(suffix='_' + tempfname)
136 os.close(tempfd)
137 open(tempfname, 'w').write(zfile.read())
138 retfile = open(tempfname)
139 self._tempfiles[tempfname] = fname
140
141 if not retfile:
142 raise FileNotInProjectError(fname)
143 return retfile
144
146 """Try and find a project file name for the given real file name."""
147 try:
148 fname = super(BundleProjectStore, self).get_proj_filename(realfname)
149 except ValueError, ve:
150 fname = None
151 if fname:
152 return fname
153 if realfname in self._tempfiles:
154 return self._tempfiles[realfname]
155 raise ValueError('Real file not in project store: %s' % (realfname))
156
157 - def load(self, zipname):
158 """Load the bundle project from the zip file of the given name."""
159 self.zip = ZipFile(zipname, mode='a')
160 self._load_settings()
161
162 append_section = {
163 'sources': self._sourcefiles.append,
164 'targets': self._targetfiles.append,
165 'transfiles': self._transfiles.append,
166 }
167 for section in ('sources', 'targets', 'transfiles'):
168 if section in self.settings:
169 for fname in self.settings[section]:
170 append_section[section](fname)
171 self._files[fname] = None
172
173 - def save(self, filename=None):
174 """Save all project files to the bundle zip file."""
175 self._update_from_tempfiles()
176
177 if filename:
178 newzip = ZipFile(filename, 'w')
179 else:
180 newzip = self._create_temp_zipfile()
181
182
183 newzip.writestr('project.xtp', self._generate_settings())
184
185 project_files = self._sourcefiles + self._transfiles + self._targetfiles
186 for fname in project_files:
187 newzip.writestr(fname, self.get_file(fname).read())
188
189 for fname in self.zip.namelist():
190 if fname in project_files or fname == 'project.xtp':
191 continue
192 newzip.writestr(fname, self.zip.read(fname))
193
194 self._replace_project_zip(newzip)
195
197 """Updates the file with the given project file name with the contents
198 of C{infile}.
199
200 @returns: the results from L{self.append_file}."""
201 if pfname not in self._files:
202 raise FileNotInProjectError(pfname)
203
204 if pfname not in self.zip.namelist():
205 return super(BundleProjectStore, self).update_file(pfname, infile)
206
207 self._zip_delete([pfname])
208 self._zip_add(pfname, infile)
209
215
217 """Create a new zip file with a temporary file name (with mode 'w')."""
218 newzipfd, newzipfname = tempfile.mkstemp(prefix='translate_bundle', suffix='.zip')
219 os.close(newzipfd)
220 return ZipFile(newzipfname, 'w')
221
223 """Replace the currently used zip file (C{self.zip}) with the given zip
224 file. Basically, C{os.rename(zfile.filename, self.zip.filename)}."""
225 if not zfile.fp.closed:
226 zfile.close()
227 if not self.zip.fp.closed:
228 self.zip.close()
229 shutil.move(zfile.filename, self.zip.filename)
230 self.zip = ZipFile(self.zip.filename, mode='a')
231
233 """Update project files from temporary files."""
234 for tempfname in self._tempfiles:
235 tmp = open(tempfname)
236 self.update_file(self._tempfiles[tempfname], tmp)
237 if not tmp.closed:
238 tmp.close()
239
241 """Add the contents of C{infile} to the zip with file name C{pfname}."""
242 if hasattr(infile, 'seek'):
243 infile.seek(0)
244 self.zip.writestr(pfname, infile.read())
245 self._files[pfname] = None
246
247
249 """Delete the files with the given names from the zip file (C{self.zip})."""
250
251 if not isinstance(fnames, (list, tuple)):
252 raise ValueError("fnames must be list or tuple: %s" % (fnames))
253 if not self.zip:
254 raise ValueError("No zip file to work on")
255 zippedfiles = self.zip.namelist()
256 for fn in fnames:
257 if fn not in zippedfiles:
258 raise KeyError("File not in zip archive: %s" % (fn))
259
260 newzip = self._create_temp_zipfile()
261 newzip.writestr('project.xtp', self._generate_settings())
262
263 for fname in zippedfiles:
264
265
266
267 if fname in fnames or fname == 'project.xtp':
268 continue
269 newzip.writestr(fname, self.zip.read(fname))
270
271 self._replace_project_zip(newzip)
272