def smart_unsync_oid(self, remote_oid): ent: SyncEntry = self.state.lookup_oid(REMOTE, remote_oid) if not ent: raise ex.CloudFileNotFoundError(remote_oid) self._smart_unsync_ent(ent) ent = self.state.smart_unsync_oid(remote_oid) return ent[LOCAL].path
def _smart_unsync(self, ents, source_id) -> Optional[SyncEntry]: if not ents: raise ex.CloudFileNotFoundError(source_id) for ent in ents: if ent in self.requestset: self._smart_unsync_ent(ent) return ent return None
def smart_sync_path(self, remote_path) -> List[SyncEntry]: # We are automatically syncing all folders, so we don't need to worry about parent folders existing # although this could be a concern before the initial sync with a bed is complete, # parent_conflict handling should take care of it ents = self.lookup_path(REMOTE, remote_path) if not ents: raise ex.CloudFileNotFoundError(remote_path) for ent in ents: self._smart_sync_ent(ent) return ents
def upload(self, oid, file_like, metadata=None) -> OInfo: with self._api(): fpath = self._oid_to_fpath(oid) if os.path.isdir(fpath): raise ex.CloudFileExistsError() if not os.path.exists(fpath): raise ex.CloudFileNotFoundError(oid) tmpdir = tempfile.gettempdir() tmp_file = os.path.join(tmpdir, "tmp." + os.urandom(16).hex()) with open(tmp_file, "wb") as f: shutil.copyfileobj(file_like, f) try: with open(tmp_file, "rb") as src, open(fpath, "wb") as dest: shutil.copyfileobj(src, dest) finally: try: os.unlink(tmp_file) except Exception: log.debug("cannot remove temp %s", tmp_file) self._clear_hash_cache(fpath) return self.info_oid(oid)
def rename(self, oid, path) -> str: fpath = self.join(self.namespace_id, path) with self._api(): path_from = self._oid_to_fpath(oid) if not os.path.exists(path_from): raise ex.CloudFileNotFoundError parent = self.dirname(fpath) if not os.path.exists(parent): raise ex.CloudFileNotFoundError(parent) if not os.path.isdir(parent): raise ex.CloudFileExistsError(fpath) if not self.paths_match(path_from, fpath, for_display=True): from_dir = os.path.isdir(path_from) to_dir = os.path.isdir(fpath) has_contents = False if os.path.exists(fpath): if to_dir: has_contents = self._folder_path_has_contents(fpath) if (not to_dir or to_dir != from_dir or (to_dir and has_contents)): if not self.paths_match(path_from, fpath): raise ex.CloudFileExistsError(fpath) try: assert os.path.exists(path_from) assert os.path.exists(parent) log.info("rename %s -> %s", path_from, fpath) os.rename(path_from, fpath) except FileExistsError: if not has_contents and is_windows() and to_dir: # win32 doesn't allow this, so force it tmpname = fpath + os.urandom(16).hex() os.rename(fpath, tmpname) os.rename(path_from, fpath) self._rmdirs.append(tmpname) else: raise return self._fpath_to_oid(fpath)
def listdir(self, oid) -> typing.Generator[DirInfo, None, None]: with self._api(): fpath = self._oid_to_fpath(oid) try: with os.scandir(fpath) as it: for entry in it: entry_path = entry.path ohash = self._fast_hash_path(entry_path) otype = OType.DIRECTORY if entry.is_dir( ) else OType.FILE name = self.is_subpath(fpath, entry_path).lstrip("/") path = self._trim_ns(entry_path) stat_result = os.stat(entry_path) mtime = int(stat_result.st_mtime) size = stat_result.st_size if otype == OType.FILE else 0 yield DirInfo(otype=otype, oid=self._fpath_to_oid(entry_path), hash=ohash, path=path, name=name, size=size, mtime=mtime) except NotADirectoryError: raise ex.CloudFileNotFoundError("not a directory")