def prepare_copy(self, src_url, dst_url): src_scheme, src_path = splitscheme(src_url) dst_scheme, dst_path = splitscheme(dst_url) if src_scheme == self.scheme and dst_scheme == 'file://': zip_path, path_in_zip = self._split(src_path) dst_ospath = as_human_readable(dst_url) return [Extract(self._fs, zip_path, path_in_zip, dst_ospath)] elif src_scheme == 'file://' and dst_scheme == self.scheme: zip_path, path_in_zip = self._split(dst_path) src_ospath = as_human_readable(src_url) return [ AddToArchive(self, self._fs, src_ospath, zip_path, path_in_zip) ] elif src_scheme == dst_scheme: # Guaranteed by fman's file system implementation: assert src_scheme == self.scheme src_zip_path, path_in_src_zip = self._split(src_path) dst_zip_path, path_in_dst_zip = self._split(dst_path) return [ CopyBetweenArchives(self, self._fs, src_zip_path, path_in_src_zip, dst_zip_path, path_in_dst_zip) ] else: raise UnsupportedOperation()
def __call__(self): pane_path = self.pane.get_path() clipboard.clear() clipboard.set_text(pane_path) clipboard.set_text(splitscheme(pane_path)[1]) show_status_message('Copied ' + splitscheme(pane_path)[1] + ' to the clipboard', timeout_secs=3)
def _rename(self, src_url, dst_url): src_path = splitscheme(src_url)[1] os_src_path = self._url_to_os_path(src_path) dst_path = splitscheme(dst_url)[1] os_dst_path = self._url_to_os_path(dst_path) Path(os_src_path).replace(os_dst_path) self.notify_file_removed(src_path) self.notify_file_added(dst_path)
def _check_transfer_precnds(self, src_url, dst_url): src_scheme, src_path = splitscheme(src_url) dst_scheme, dst_path = splitscheme(dst_url) if src_scheme != self.scheme or dst_scheme != self.scheme: raise UnsupportedOperation() if not self._isabs(self._url_to_os_path(dst_path)): raise ValueError('Destination path must be absolute') return src_path, dst_path
def _get_dest_dir_url(self): try: splitscheme(self._dest_dir) except ValueError as not_a_url: is_url = False else: is_url = True return self._dest_dir if is_url else join(self._src_dir, self._dest_dir)
def _check_transfer_precnds(self, src_url, dst_url): src_scheme, src_path = splitscheme(src_url) dst_scheme, dst_path = splitscheme(dst_url) if src_scheme != self.scheme or dst_scheme != self.scheme: raise UnsupportedOperation( "%s only supports transferring to and from %s." % (self.__class__.__name__, self.scheme)) if not self._isabs(self._url_to_os_path(dst_path)): raise ValueError('Destination path must be absolute') return src_path, dst_path
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 move(self, src_url, dst_url): # Rename on same server src_scheme, src_path = splitscheme(src_url) dst_scheme, dst_path = splitscheme(dst_url) if src_scheme == dst_scheme and commonprefix([src_path, dst_path]): # TODO avoid second connection with FtpWrapper(src_url) as src_ftp, \ FtpWrapper(dst_url) as dst_ftp: src_ftp.conn.rename(src_ftp.path, dst_ftp.path) return fs.copy(src_url, dst_url) if fs.exists(src_url): fs.delete(src_url)
def __call__(self): show_status_message('Regular Expressions Selection') result = show_quicksearch(self._suggest_projects) if result: query, regexp = result try: pattern = re.compile(regexp) except Exception as e: show_alert('Your Regular Expression statement is not valid.' + str(e)) self.__call__() return used = False lines = [""] if os.path.isfile(REGULAREXPRESSIONHIST): with open(REGULAREXPRESSIONHIST, "r") as f: lines = f.readlines() for line in lines: if line.strip() == regexp: used = True if not used: with open(REGULAREXPRESSIONHIST, "a") as f: f.write(regexp + "\n") scheme, currentDir = splitscheme(self.pane.get_path()) for filep in iterdir(self.pane.get_path()): if pattern.search(filep): self.pane.toggle_selection( as_url(currentDir + os.sep + filep, scheme)) clear_status_message()
def test_delete_readonly_file(self): with TemporaryDirectory() as tmp_dir: path = Path(tmp_dir, 'file') path.touch() path.chmod(path.stat().st_mode ^ S_IWRITE) urlpath = splitscheme(as_url(path))[1] self._fs.delete(urlpath)
def on_command(self, command_name, args): if command_name != 'open_file': return url = args['url'] scheme, path = splitscheme(url) if not is_ftp(scheme): return tmp = NamedTemporaryFile(prefix=basename(path), delete=False) tmp_path = tmp.name tmp_url = 'file://' + tmp_path fs.copy(url, tmp_url) _open_local_file(tmp_path) choice = show_alert( 'Upload modified file?', buttons=YES | NO, default_button=YES ) if choice == YES: fs.move(tmp_url, url) return 'reload', {}
def __call__(self, url=None): if url is None: url = self.pane.get_file_under_cursor() or self.pane.get_path() if not is_dir(url): url = dirname(url) new_url = Flat.scheme + splitscheme(url)[1] self.pane.set_path(new_url)
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 test_mkdir_raises_filenotfounderror(self): with TemporaryDirectory() as tmp_dir: zip_path = os.path.join(tmp_dir, 'test.zip') self._create_empty_zip(zip_path) zip_url_path = splitscheme(as_url(zip_path, 'zip://'))[1] with self.assertRaises(OSError) as cm: self._fs.mkdir(zip_url_path + '/nonexistent/dir') self.assertEqual(ENOENT, cm.exception.errno)
def test_mkdir_raises_fileexistserror(self): with TemporaryDirectory() as tmp_dir: zip_path = os.path.join(tmp_dir, 'test.zip') self._create_empty_zip(zip_path) dir_url_path = splitscheme(as_url(zip_path, 'zip://'))[1] + '/dir' self._fs.mkdir(dir_url_path) with self.assertRaises(FileExistsError): self._fs.mkdir(dir_url_path)
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 test_delete_symlink_to_directory(self): with TemporaryDirectory() as tmp_dir: a = Path(tmp_dir, 'a') a.mkdir() b = Path(tmp_dir, 'b') b.symlink_to(a) self._fs.delete(splitscheme(as_url(b))[1]) self.assertFalse(b.exists(), 'Failed to delete symlink to folder')
def copy(self, src_url, dst_url): src_scheme, src_path = splitscheme(src_url) file_scheme = 'file://' if src_scheme == self.scheme and dst_scheme == file_scheme: fman.fs.copy(as_url(src_path, file_scheme), dst_url) else: raise UnsupportedOperation()
def __call__(self): scheme = splitscheme(self.pane.get_path())[0] if scheme not in ('file://', 'drives://', 'network://'): show_alert('Sorry, showing the properties of %s files is not ' 'yet supported.' % scheme) else: action = self._get_action() if action: action()
def __call__(self, *args, **kwargs): try: rmdir(as_human_readable(self._dir_url)) except FileNotFoundError: pass except OSError as e: if e.errno != errno.ENOTEMPTY: raise self._fs.notify_file_removed(splitscheme(self._dir_url)[1])
def on_command(self, command_name, args): if command_name == 'open_file': if 'url' in args: url = args['url'] scheme, path = splitscheme(url) if scheme == 'flat://': self.path = path self.pane.set_path('file://' + path.split(_SEPARATOR)[0], callback=self.callback) return 'open_directory', {'url': to_file_url(self.path)}
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 on_path_changed(self): url = self.pane.get_path() if not is_ftp(url): return scheme, path = splitscheme(url) if not path: # XXX avoid storing URLs with empty path return history = \ load_json('FTP History.json', default={}, save_on_quit=True) history[url] = history.get(url, 0) + 1
def __call__(self): url = self.pane.get_path() scheme, path = splitscheme(url) paths = [] paths.append(as_url(path)) if scheme != 'file://': show_alert('{} is not supported'.format(url)) return openCommand(" /command:repobrowser /path:", paths, path)
def __call__(self, url=None): text, ok = show_prompt('Enter Filter e.g. *.txt', default="*.txt", selection_start=2) if url is None: url = self.pane.get_file_under_cursor() or self.pane.get_path() if not is_dir(url): url = dirname(url) Flat.filtertext = text new_url = Flat.scheme + splitscheme(url)[1] # Not working yet if ok and text: self.pane.set_path(new_url + '?' + text)
def __call__(self): url = self.pane.get_path() scheme, path = splitscheme(url) if scheme != 'file://': show_alert('Not supported.') return local_path = as_human_readable(url) if platform.system() == 'Windows' and os.path.isfile(self.GIT_BASH): subprocess.call('{bash_exe} --cd="{cd}"'.format( bash_exe=self.GIT_BASH, cd=local_path)) else: self.pane.run_command('open_terminal')
def get_str(self, url): scheme, path = splitscheme(url) rootKey = ConnectRegistry(None, HKEY_CURRENT_USER) parent = dirname(path).replace("/", "\\") valueName = basename(path) try: key = OpenKey(rootKey, parent) except EnvironmentError: return None try: value = QueryValueEx(key, valueName) except EnvironmentError: return None return str(value[0])
def __call__(self): scheme, currentDir = splitscheme(self.pane.get_path()) currentDir = os.path.normpath(currentDir) global current_dir global current_parent_dir current_dir = currentDir current_parent_dir = os.path.normpath( os.path.join(current_dir, os.pardir)) self.searchDialog = SearchDialog(scheme, currentDir, self.pane, root_node) self.searchDialog.show()
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_str(self, url): scheme, path = splitscheme(url) if scheme != 'drives://': raise ValueError('Unsupported URL: %r' % url) if path == DrivesFileSystem.NETWORK: return path result = path try: vol_name = self._get_volume_name(path + '\\') except WindowsError: pass else: if vol_name: result += ' ' + vol_name return result