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 _get_action(self): file_under_cursor = self.pane.get_file_under_cursor() selected_files = self.pane.get_selected_files() chosen_files = selected_files or \ ([file_under_cursor] if file_under_cursor else []) location = self.pane.get_path() scheme, path = splitscheme(location) if scheme == 'file://': if file_under_cursor is None: # Either we're in an empty folder, or the user # right-clicked inside a directory. if self._is_drive(path): return lambda: _show_drive_properties(path) else: dir_ = as_human_readable(dirname(location)) filenames = [basename(location)] else: dir_ = as_human_readable(location) filenames = [basename(f) for f in chosen_files] return lambda: _show_file_properties(dir_, filenames) elif scheme == 'drives://': if file_under_cursor is None: # This usually happens when the user right-clicked in the drive # overview (but not _on_ a drive). return None drive = splitscheme(file_under_cursor)[1] if self._is_drive(drive): return lambda: _show_drive_properties(drive) elif scheme == 'network://': # We check `path` because when it's empty, we're at the # overview of network locations. Servers don't have a Properties # dialog. So we can't do anything there. if path: for f in chosen_files: try: f_fileurl = resolve(f) except OSError: continue if splitscheme(f_fileurl)[0] != 'file://': # Sanity check. We don't actually expect this. continue dir_ = as_human_readable(dirname(f_fileurl)) break else: return filenames = [basename(f) for f in chosen_files] return lambda: _show_file_properties(dir_, filenames)
def _get_title(self, descr_verb, files): verb = descr_verb.capitalize() result = (verb[:-1] if verb.endswith('e') else verb) + 'ing ' if len(files) == 1: result += basename(files[0]) else: result += '%d files' % len(files) return result
def __call__(self): self.set_text('Preparing...') with TemporaryDirectory() as tmp_dir: # Give temp dir the same name as the source file; This leads to the # correct name being displayed in the progress dialog: tmp_url = as_url(os.path.join(tmp_dir, basename(self._src_url))) tasks = list(self._fs.prepare_move(self._src_url, tmp_url)) tasks.extend(self._fs.prepare_move(tmp_url, self._dst_url)) for task in tasks: self.run(task)
def resolve(self, path): # path=remove_query_text(path) # Tell fman about the "real" URL of the given file. This is important # eg. when you press Enter on a file. In this case, we want fman to # open file://directory/file.txt, not example://directory/file.txt url = self.scheme + path final_component = basename(url) if _SEPARATOR in final_component: return to_file_url(path) return url
def __call__(self, method="move"): target_path = get_path_to_other_pane(self.pane) chosen_files = self.get_chosen_files() for filep in chosen_files: dest_url = join( target_path, basename(filep) ) if method == "move": move(filep, dest_url) elif method == "copy": copy(filep, dest_url) else: pass
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 get_sort_value(self, url, is_ascending): try: is_dir = self._fs.is_dir(url) except FileNotFoundError: raise except OSError: is_dir = False if is_dir: ord_ = ord if is_ascending else lambda c: -ord(c) minor = tuple(ord_(c) for c in basename(url).lower()) else: try: minor = self._get_size(url) except OSError: minor = 0 return is_dir ^ is_ascending, minor
def _should_overwrite(self, file_url): if self._override_all is None: choice = self.show_alert( "%s exists. Do you want to overwrite it?" % basename(file_url), YES | NO | YES_TO_ALL | NO_TO_ALL | ABORT, YES) if choice & YES: return YES elif choice & NO: return NO elif choice & YES_TO_ALL: self._override_all = True elif choice & NO_TO_ALL: self._override_all = False else: assert choice & ABORT, choice return ABORT return YES if self._override_all else NO
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 _get_dest_url(self, src_file): dest_name = self._dest_name or basename(src_file) if self._src_dir: try: rel_path = \ relpath(join(dirname(src_file), dest_name), self._src_dir) except ValueError as e: raise ValueError('Could not construct path. ' 'src_file: %r, dest_name: %r, src_dir: %r' % (src_file, dest_name, self._src_dir)) from e is_in_src_dir = not rel_path.startswith(pardir) if is_in_src_dir: try: splitscheme(self._dest_dir) except ValueError as no_scheme: return join(self._src_dir, self._dest_dir, rel_path) else: return join(self._dest_dir, rel_path) return join(self._dest_dir, dest_name)
def __call__(self): # get_chosen_files() returns the files selected by the user # (="red" files). If no files are selected, the file under the cursor # is returned: chosen_files = self.get_chosen_files() if chosen_files: # equivalent to `len(chosen_files) > 0` ndnam, okay = show_prompt("Directory Name?") # Check ndnam as well because it could be empty if ndnam and okay: numf = 0 newdirp = join(self.pane.get_path(), ndnam) if not self.isADir(newdirp): # Create directory and its parent directories: makedirs(newdirp) for filep in chosen_files: move(filep, join(newdirp, basename(filep))) numf += 1 show_alert('%d items were moved!' % numf) else: show_alert("No files or directories selected")
def get_str(self, url): # Turn example://C:/tmp/one/sub1|sub2 -> sub2: return basename(url).split(_SEPARATOR)[-1]
def __init__(self, fs, src_url, dst_url): super().__init__('Moving ' + basename(src_url), size=200) self._fs = fs self._src_url = src_url self._dst_url = dst_url
def __init__(self, fs, src_url, dst_url, size_bytes): super().__init__('Copying ' + basename(src_url), size=size_bytes) self._fs = fs self._src_url = src_url self._dst_url = dst_url
def _postprocess_directory(self, src_dir_path): return Task('Postprocessing ' + basename(src_dir_path), fn=self._do_postprocess_directory, args=(src_dir_path, ))
def __init__(self, fs, dir_url): super().__init__('Deleting ' + basename(dir_url), size=1) self._fs = fs self._dir_url = dir_url