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