def is_dir(self, existing_path): zip_path, path_in_zip = self._split(existing_path) if not path_in_zip: if Path(zip_path).exists(): return True raise filenotfounderror(existing_path) result = self._query_info_attr(existing_path, 'is_dir', True) if result is not None: return result raise filenotfounderror(existing_path)
def resolve(self, path): path = self._url_to_os_path(path) if not self._isabs(path): raise filenotfounderror(path) if PLATFORM == 'Windows': is_unc_server = path.startswith(r'\\') and not '\\' in path[2:] if is_unc_server: # Python can handle \\server\folder but not \\server. Defer to # the network:// file system. return 'network://' + path[2:] p = Path(path) try: try: path = p.resolve(strict=True) except TypeError: # fman's "production Python version" is 3.6 but we want to be # able to develop using 3.5 as well. So add this workaround for # Python < 3.6: path = p.resolve() except FileNotFoundError: if not p.exists(): raise except OSError as e: # We for instance get errno.EINVAL ("[WinError 1]") when trying to # resolve folders on Cryptomator drives on Windows. Ignore it: if e.errno != errno.EINVAL: raise return as_url(path)
def delete(self, path): if not self.exists(path): raise filenotfounderror(path) zip_path, path_in_zip = self._split(path) with self._preserve_empty_parent(zip_path, path_in_zip): _run_7zip(['d', zip_path, path_in_zip]) self.notify_file_removed(path)
def stat(self, path): os_path = self._url_to_os_path(path) if not self._isabs(os_path): raise filenotfounderror(path) try: return os.stat(os_path) except FileNotFoundError: return os.stat(os_path, follow_symlinks=False)
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 _split(self, path): for suffix in self._suffixes: try: split_point = path.lower().index(suffix) + len(suffix) except ValueError as suffix_not_found: continue else: return path[:split_point], path[split_point:].lstrip('/') raise filenotfounderror(self.scheme + path) from None
def resolve(self, path): if not path: # Showing the list of all drives: return self.scheme if path in self._get_drives(): return as_url(path + '\\') if path == self.NETWORK: return NetworkFileSystem.scheme raise filenotfounderror(path)
def mkdir(self, path): if self.exists(path): raise FileExistsError(path) zip_path, path_in_zip = self._split(path) if not path_in_zip: self._create_empty_archive(zip_path) elif not self.exists(str(PurePosixPath(path).parent)): raise filenotfounderror(path) else: with TemporaryDirectory() as tmp_dir: self.copy(as_url(tmp_dir), as_url(path, self.scheme))
def iterdir(self, path): if path: handle = NETRESOURCE() handle.lpRemoteName = r'\\' + path.replace('/', '\\') try: WNetGetResourceInformation(handle) except WNetError: raise filenotfounderror(path) else: handle = None for subpath in self._iter_handle(handle): if '/' in subpath: head, tail = subpath.rsplit('/', 1) if head == path: yield tail elif not path: yield subpath
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 resolve(self, path): path = self._url_to_os_path(path) if not self._isabs(path): raise filenotfounderror(path) if PLATFORM == 'Windows': is_unc_server = path.startswith(r'\\') and not '\\' in path[2:] if is_unc_server: # Python can handle \\server\folder but not \\server. Defer to # the network:// file system. return 'network://' + path[2:] try: path = Path(path).resolve() except FileNotFoundError: # TODO: Remove this except block once we upgraded to Python >= 3.6. # In Python 3.5 on Windows, Path#resolve(...) raises # FileNotFoundError for virtual drives such as for instance X: # created by BoxCryptor. if not Path(path).exists(): raise return as_url(path)
def resolve(self, path): path = self._url_to_os_path(path) if not self._isabs(path): raise filenotfounderror(path) if PLATFORM == 'Windows': is_unc_server = path.startswith(r'\\') and not '\\' in path[2:] if is_unc_server: # Python can handle \\server\folder but not \\server. Defer to # the network:// file system. return 'network://' + path[2:] p = Path(path) try: path = p.resolve(strict=True) except FileNotFoundError: if not p.exists(): raise except OSError as e: # We for instance get errno.EINVAL ("[WinError 1]") when trying to # resolve folders on Cryptomator drives on Windows. Ignore it: if e.errno != errno.EINVAL: raise return as_url(path)
def mkdir(self, path): os_path = Path(self._url_to_os_path(path)) if not os_path.is_absolute(): raise ValueError('Path must be absolute') try: os_path.mkdir() except FileNotFoundError: raise except IsADirectoryError: # macOS raise FileExistsError(path) except OSError as e: if e.errno == ENOENT: raise filenotfounderror(path) from e elif os_path.exists(): # On at least Windows, Path('Z:\\').mkdir() raises # PermissionError instead of FileExistsError. We want the latter # however because #makedirs(...) relies on it. So handle this # case generally here: raise FileExistsError(path) from e else: raise self.notify_file_added(path)
def _iter_infos(self, path): zip_path, path_in_zip = self._split(path) self._raise_filenotfounderror_if_not_exists(zip_path) args = ['l', '-ba', '-slt', zip_path] if path_in_zip: args.append(path_in_zip) # We can hugely improve performance by making 7-Zip exclude children of # the given directory. Unfortunately, this has a drawback: If you have # a/b.txt in an archive but no separate entry for a/, then excluding */* # filters out a/. We thus exclude */*/*/*. This works for all folders # that contain at least one subdirectory with a file. exclude = (path_in_zip + '/' if path_in_zip else '') + '*/*/*/*' args.append('-x!' + exclude) with _7zip(args, kill=True) as process: stdout_lines = process.stdout_lines file_info = self._read_file_info(stdout_lines) if path_in_zip and not file_info: raise filenotfounderror(self.scheme + path) while file_info: self._put_in_cache(zip_path, file_info) yield file_info file_info = self._read_file_info(stdout_lines)
def is_dir(self, existing_path): if self.exists(existing_path): return True raise filenotfounderror(existing_path)
def iterdir(self, path): if path: raise filenotfounderror(path) return self._get_drives() + [self.NETWORK]
def is_dir(self, url): try: file_info = self._get(url) except KeyError: raise filenotfounderror(url) from None return file_info['is_dir']
def iterdir(self, path): os_path = self._url_to_os_path(path) if not self._isabs(os_path): raise filenotfounderror(path) # Use os.listdir(...) instead of Path(...).iterdir() for performance: return os.listdir(os_path)
def listdir(self, path): try: return sorted(list(self._get_dir(path))) except KeyError as e: raise filenotfounderror(path) from e