Mercurial > gcp
comparison gcp @ 7:a110d31482f7
Journalisation + some additionnal try/except
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 27 Sep 2010 17:31:43 +0800 |
parents | 5f53dc5beec9 |
children | 144cb2669f21 |
comparison
equal
deleted
inserted
replaced
6:5f53dc5beec9 | 7:a110d31482f7 |
---|---|
70 | 70 |
71 const_DBUS_INTERFACE = "org.goffi.gcp" | 71 const_DBUS_INTERFACE = "org.goffi.gcp" |
72 const_DBUS_PATH = "/org/goffi/gcp" | 72 const_DBUS_PATH = "/org/goffi/gcp" |
73 const_BUFF_SIZE = 4096 | 73 const_BUFF_SIZE = 4096 |
74 const_PRESERVE = set(['mode','ownership','timestamps']) | 74 const_PRESERVE = set(['mode','ownership','timestamps']) |
75 const_JOURNAL_PATH = "~/.gcp_journal" | |
75 | 76 |
76 | 77 |
77 class DbusObject(dbus.service.Object): | 78 class DbusObject(dbus.service.Object): |
78 | 79 |
79 def __init__(self, gcp, bus, path): | 80 def __init__(self, gcp, bus, path): |
99 try: | 100 try: |
100 args = pickle.loads(str(args)) | 101 args = pickle.loads(str(args)) |
101 except TypeError, pickle.UnpicklingError: | 102 except TypeError, pickle.UnpicklingError: |
102 return (False, _("INTERNAL ERROR: invalid arguments")) | 103 return (False, _("INTERNAL ERROR: invalid arguments")) |
103 return self._gcp.parseArguments(args, source_path) | 104 return self._gcp.parseArguments(args, source_path) |
105 | |
106 class Journal(): | |
107 def __init__(self, path=const_JOURNAL_PATH): | |
108 self.journal_path = os.path.expanduser(path) | |
109 self.journal_fd = open(self.journal_path,'w') #TODO: check and maybe save previous journals | |
110 | |
111 def __del__(self): | |
112 self.journal_fd.flush() | |
113 self.journal_fd.close() | |
114 | |
115 def startFile(self, source_path): | |
116 """Start an entry in the journal""" | |
117 self.journal_fd.write(source_path+"\n") | |
118 self.journal_fd.flush() | |
119 self.success=True | |
120 self.errors=[] | |
121 | |
122 def closeFile(self): | |
123 """Close the entry in the journal""" | |
124 if not self.success: | |
125 status = "FAILED" | |
126 else: | |
127 status = "OK" if not self.errors else "PARTIAL" | |
128 self.journal_fd.write("%(status)s: %(errors)s\n" % {'status': status, 'errors': ', '.join(self.errors)}) | |
129 self.journal_fd.flush() | |
130 | |
131 def copyFailed(self): | |
132 """Must be called when something is wrong with the copy itself""" | |
133 self.success = False | |
134 | |
135 def error(self, name): | |
136 """Something went wrong""" | |
137 self.errors.append(name) | |
138 | |
104 | 139 |
105 class GCP(): | 140 class GCP(): |
106 | 141 |
107 def __init__(self): | 142 def __init__(self): |
108 try: | 143 try: |
217 def __copyNextFile(self): | 252 def __copyNextFile(self): |
218 """Take the last file in the list, and launch the copy using glib io_watch event | 253 """Take the last file in the list, and launch the copy using glib io_watch event |
219 @return: True a file was added, False else""" | 254 @return: True a file was added, False else""" |
220 if self.copy_list: | 255 if self.copy_list: |
221 source_file, dest_path, options = self.copy_list.pop() | 256 source_file, dest_path, options = self.copy_list.pop() |
257 self.journal.startFile(source_file) | |
222 source_fd = open(source_file, 'rb') | 258 source_fd = open(source_file, 'rb') |
223 filename = os.path.basename(source_file) | 259 filename = os.path.basename(source_file) |
224 assert(filename) | 260 assert(filename) |
225 dest_file = self.__filename_fix(os.path.join(dest_path,filename),options) | 261 dest_file = self.__filename_fix(os.path.join(dest_path,filename),options) |
226 if os.path.exists(dest_file) and not options.force: | 262 if os.path.exists(dest_file) and not options.force: |
227 warning (_("File [%s] already exists, skipping it !") % dest_file) | 263 warning (_("File [%s] already exists, skipping it !") % dest_file) |
228 return True | 264 return True |
229 dest_fd = open(dest_file, 'wb') | 265 try: |
266 dest_fd = open(dest_file, 'wb') | |
267 except: | |
268 self.journal.copyFailed() | |
269 self.journal.error("can't open dest") | |
270 self.journal.closeFile() | |
271 source_fd.close() | |
272 return True | |
230 | 273 |
231 gobject.io_add_watch(source_fd,gobject.IO_IN,self._copyFile, | 274 gobject.io_add_watch(source_fd,gobject.IO_IN,self._copyFile, |
232 (dest_fd, options), priority=gobject.PRIORITY_HIGH) | 275 (dest_fd, options), priority=gobject.PRIORITY_HIGH) |
233 if not self.progress: | 276 if not self.progress: |
234 info(_("COPYING %(source)s ==> %(dest)s") % {"source":source_path,"dest":dest_file}) | 277 info(_("COPYING %(source)s ==> %(dest)s") % {"source":source_path,"dest":dest_file}) |
237 #Nothing left to copy, we quit | 280 #Nothing left to copy, we quit |
238 if self.progress: | 281 if self.progress: |
239 self.__pbar_finish() | 282 self.__pbar_finish() |
240 self.loop.quit() | 283 self.loop.quit() |
241 | 284 |
285 def __copyFailed(self, reason, source_fd, dest_fd): | |
286 """Write the failure in the journal and close files descriptors""" | |
287 self.journal.copyFailed() | |
288 self.journal.error(reason) | |
289 self.journal.closeFile() | |
290 source_fd.close() | |
291 dest_fd.close() | |
292 | |
293 | |
294 | |
242 def _copyFile(self, source_fd, condition, data): | 295 def _copyFile(self, source_fd, condition, data): |
243 """Actually copy the file, callback used with io_add_watch | 296 """Actually copy the file, callback used with io_add_watch |
244 @param source_fd: file descriptor of the file to copy | 297 @param source_fd: file descriptor of the file to copy |
245 @param condition: condition which launched the callback (glib.IO_IN) | 298 @param condition: condition which launched the callback (glib.IO_IN) |
246 @param data: tuple with (destination file descriptor, copying options)""" | 299 @param data: tuple with (destination file descriptor, copying options)""" |
247 dest_fd,options = data | 300 dest_fd,options = data |
248 buff = source_fd.read(self.buffer_size) | 301 |
249 dest_fd.write(buff) | 302 try: |
303 buff = source_fd.read(self.buffer_size) | |
304 except: | |
305 self.__copyFailed("can't read source", source_fd, dest_fd) | |
306 return False | |
307 | |
308 try: | |
309 dest_fd.write(buff) | |
310 except: | |
311 self.__copyFailed("can't write to dest", source_fd, dest_fd) | |
312 return False | |
313 | |
250 self.bytes_copied += len(buff) | 314 self.bytes_copied += len(buff) |
251 if self.progress: | 315 if self.progress: |
252 self.__pbar_update() | 316 self.__pbar_update() |
253 | 317 |
254 if len(buff) != self.buffer_size: | 318 if len(buff) != self.buffer_size: |
255 source_fd.close() | 319 source_fd.close() |
256 dest_fd.close() | 320 dest_fd.close() |
257 self.__post_copy(source_fd.name, dest_fd.name, options) | 321 self.__post_copy(source_fd.name, dest_fd.name, options) |
322 self.journal.closeFile() | |
258 return False | 323 return False |
259 return True | 324 return True |
260 | 325 |
261 def __filename_fix(self, filename, options): | 326 def __filename_fix(self, filename, options): |
327 """Fix filenames incompatibilities/mistake according to options | |
328 @param filename: full path to the file | |
329 @param options: options as parsed on command line | |
330 @return: fixed filename""" | |
331 fixed_filename = filename | |
332 | |
262 if self.getFsType(filename) == 'vfat' and options.fs_fix: | 333 if self.getFsType(filename) == 'vfat' and options.fs_fix: |
263 filename = filename.replace('\\','_')\ | 334 fixed_filename = filename.replace('\\','_')\ |
264 .replace(':',';')\ | 335 .replace(':',';')\ |
265 .replace('*','+')\ | 336 .replace('*','+')\ |
266 .replace('?','')\ | 337 .replace('?','')\ |
267 .replace('"','\'')\ | 338 .replace('"','\'')\ |
268 .replace('<','[')\ | 339 .replace('<','[')\ |
269 .replace('>',']')\ | 340 .replace('>',']')\ |
270 .replace('|','!') | 341 .replace('|','!') |
271 return filename | 342 |
343 if fixed_filename != filename: | |
344 self.journal.error('filename fixed') | |
345 return fixed_filename | |
272 | 346 |
273 def __post_copy(self, source_file, dest_file, options): | 347 def __post_copy(self, source_file, dest_file, options): |
274 """Do post copy traitement (mainly managing --preserve option)""" | 348 """Do post copy traitement (mainly managing --preserve option)""" |
275 st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime = os.stat(source_file) | 349 st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime = os.stat(source_file) |
276 #TODO: complete log in case of errors | 350 #TODO: complete log in case of errors |
281 elif preserve == 'ownership': | 355 elif preserve == 'ownership': |
282 os.chown(dest_file, st_uid, st_gid) | 356 os.chown(dest_file, st_uid, st_gid) |
283 elif preserve == 'timestamps': | 357 elif preserve == 'timestamps': |
284 os.utime(dest_file, (st_atime, st_mtime)) | 358 os.utime(dest_file, (st_atime, st_mtime)) |
285 except OSError,e: | 359 except OSError,e: |
286 pass #TODO: complete log here | 360 self.journal.error("preserve-"+preserve) |
287 | 361 |
288 def __pbar_update(self): | 362 def __pbar_update(self): |
289 """Update progress bar position, create the bar if it doesn't exist""" | 363 """Update progress bar position, create the bar if it doesn't exist""" |
290 assert(self.progress) | 364 assert(self.progress) |
291 try: | 365 try: |
376 if len(args) < 2: | 450 if len(args) < 2: |
377 _error_msg = _("Wrong number of arguments") | 451 _error_msg = _("Wrong number of arguments") |
378 return (False, _error_msg) | 452 return (False, _error_msg) |
379 debug(_("adding args to gcp: %s"),args) | 453 debug(_("adding args to gcp: %s"),args) |
380 self.__checkArgs(options, source_path, args) | 454 self.__checkArgs(options, source_path, args) |
455 self.journal = Journal() | |
381 gobject.idle_add(self.__copyNextFile) | 456 gobject.idle_add(self.__copyNextFile) |
382 return (True,'') | 457 return (True,'') |
383 | 458 |
384 def go(self): | 459 def go(self): |
385 """Launch main loop""" | 460 """Launch main loop""" |