def _prepare_move(self, src_url, dst_url, measure_size=False, use_rename=True, expected_st_dev=None): if expected_st_dev is None: expected_st_dev = {} src_path, dst_path = self._check_transfer_precnds(src_url, dst_url) if use_rename: src_stat = self.stat(src_path) dst_par_path = splitscheme(dirname(dst_url))[1] try: dst_par_dev = expected_st_dev[dst_par_path] except KeyError: dst_par_dev = self.stat(dst_par_path).st_dev if src_stat.st_dev == dst_par_dev: yield Task('Moving ' + basename(src_url), size=1, fn=self._rename, args=(src_url, dst_url)) return src_is_dir = self.is_dir(src_path) if src_is_dir: yield Task('Creating ' + basename(dst_url), fn=self.mkdir, args=(dst_path, )) dst_par_path = splitscheme(dirname(dst_url))[1] # Expect `dst_path` to inherit .st_dev from its parent: try: expected_st_dev[dst_path] = expected_st_dev[dst_par_path] except KeyError: expected_st_dev[dst_path] = self.stat(dst_par_path).st_dev for name in self.iterdir(src_path): try: yield from self._prepare_move(join(src_url, name), join(dst_url, name), measure_size, use_rename, expected_st_dev) except FileNotFoundError: pass yield DeleteIfEmpty(self, src_url) else: size = self.size_bytes(src_path) if measure_size else 0 # It's tempting to "yield copy" then "yield delete" here. But this # can lead to data loss: fman / the user may choose to ignore errors # in a particular subtask. (Eg. moving file1.txt failed but the user # wants to go on to move file2.txt.) If we ignored a failure in # "yield copy" but then execute the "yield delete", we would delete # a file that has not been copied! # To avoid this, we "copy and delete" as a single, atomic task: yield MoveByCopying(self, src_url, dst_url, size)
def _prepare_move(self, src_url, dst_url, measure_size=False): src_path, dst_path = self._check_transfer_precnds(src_url, dst_url) src_stat = self.stat(src_path) dst_dir_dev = self.stat(splitscheme(dirname(dst_url))[1]).st_dev if src_stat.st_dev == dst_dir_dev: yield Task('Moving ' + basename(src_url), size=1, fn=self._rename, args=(src_url, dst_url)) return yield from self._prepare_copy(src_url, dst_url, measure_size) yield Task('Postprocessing ' + basename(src_url), fn=self.delete, args=(src_path, ))
def prepare_delete(self, path): return [ Task('Deleting ' + path.rsplit('/', 1)[-1], fn=self.delete, args=(path, ), size=1) ]
def prepare_trash(self, path): os_path = self._url_to_os_path(path) if not self._isabs(os_path): raise filenotfounderror(path) yield Task('Deleting ' + path.rsplit('/', 1)[-1], size=1, fn=self._do_trash, args=(path, os_path))
def prepare_delete(self, path): if self.is_dir(path): for name in self.iterdir(path): try: yield from self.prepare_delete(path + '/' + name) except FileNotFoundError: pass delete_fn = rmdir else: delete_fn = remove yield Task('Deleting ' + path.rsplit('/', 1)[-1], size=1, fn=self._do_delete, args=(path, delete_fn))
def _gather_files(self): dest_dir_url = self._get_dest_dir_url() self._enqueue([ Task('Preparing ' + basename(dest_dir_url), fn=self._fs.makedirs, args=(dest_dir_url, ), kwargs={'exist_ok': True}) ]) for i, src in enumerate(self._iter(self._files)): is_last = i == len(self._files) - 1 dest = self._get_dest_url(src) if is_parent(src, dest, self._fs): if src != dest: try: is_samefile = self._fs.samefile(src, dest) except OSError: is_samefile = False if is_samefile: if self._can_transfer_samefile(): self._enqueue(self._prepare_transfer(src, dest)) continue self.show_alert("You cannot %s a file to itself." % self._descr_verb) return False try: is_dir = self._fs.is_dir(src) except OSError as e: error_message = 'Could not %s %s' % \ (self._descr_verb, as_human_readable(src)) if self._handle_exception(error_message, is_last, e): continue return False if is_dir: if self._fs.exists(dest): if not self._merge_directory(src): return False else: self._enqueue(self._prepare_transfer(src, dest)) else: if self._fs.exists(dest): should_overwrite = self._should_overwrite(dest) if should_overwrite == NO: continue elif should_overwrite == ABORT: return False else: assert should_overwrite == YES, should_overwrite self._enqueue(self._prepare_transfer(src, dest)) return True
def prepare_move(self, src_url, dst_url): src_scheme, src_path = splitscheme(src_url) dst_scheme, dst_path = splitscheme(dst_url) if src_scheme == dst_scheme: # Guaranteed by fman's file system implementation: assert src_scheme == self.scheme src_zip, src_pth_in_zip = self._split(src_path) dst_zip, dst_pth_in_zip = self._split(dst_path) if src_zip == dst_zip: return [Rename(self, src_zip, src_pth_in_zip, dst_pth_in_zip)] else: return [MoveBetweenArchives(self, src_url, dst_url)] else: result = list(self.prepare_copy(src_url, dst_url)) title = 'Cleaning up ' + basename(src_url) result.append(Task(title, fn=self._fs.delete, args=(src_url, ))) return result
def _prepare_copy(self, src_url, dst_url, measure_size=False): src_path, dst_path = self._check_transfer_precnds(src_url, dst_url) src_is_dir = self.is_dir(src_path) if src_is_dir: yield Task('Creating ' + basename(dst_url), fn=self.mkdir, args=(dst_path, )) for name in self.iterdir(src_path): try: yield from self._prepare_copy(join(src_url, name), join(dst_url, name), measure_size) except FileNotFoundError: pass else: size = self.size_bytes(src_path) if measure_size else 0 yield CopyFile(self, src_url, dst_url, size)
def prepare_delete(self, path): os_path = self._url_to_os_path(path) if not self._isabs(os_path): raise filenotfounderror(path) # is_dir(...) follows symlinks. But if `path` is a symlink, we need to # use remove(...) instead of rmdir(...) to avoid NotADirectoryError. # So check if `path` is a symlink: if self.is_dir(path) and not islink(os_path): for name in self.iterdir(path): try: yield from self.prepare_delete(path + '/' + name) except FileNotFoundError: pass delete_fn = rmdir else: delete_fn = remove yield Task('Deleting ' + path.rsplit('/', 1)[-1], size=1, fn=self._do_delete, args=(path, delete_fn))
def _postprocess_directory(self, src_dir_path): return Task('Postprocessing ' + basename(src_dir_path), fn=self._do_postprocess_directory, args=(src_dir_path, ))