async def copy(self, destination: str) -> 'AbstractResource': new_resource = self.__class__(self.prefix, destination, root_dir=self._root_dir) if not self._stat: await self.populate_props() if not self.is_collection: try: shutil.copy(str(self.absolute), str(new_resource.absolute)) except shutil.SameFileError: raise errors.ResourceAlreadyExists( "destination file already exists") except FileNotFoundError: raise errors.ResourceDoesNotExist( "destination dir does not exist") await new_resource.populate_props() else: try: shutil.copytree(str(self.absolute), str(new_resource.absolute)) except FileExistsError: raise errors.InvalidResourceType("collection expected") await new_resource.populate_props() await new_resource.populate_collection() return new_resource
async def put_content(self, read_some: typing.Awaitable[bytes]) -> bool: created = not self.absolute.exists() mode = 'wb' if created else 'r+b' parent_exists = self.absolute.parent.exists() if not parent_exists: raise errors.ResourceDoesNotExist("parent resource does not exist") try: with self.absolute.open(mode) as f: if not read_some: return created while True: buffer = await read_some() f.write(buffer) if not buffer: return created except NotADirectoryError: raise errors.InvalidResourceType( "parent resource is not a collection") except IsADirectoryError: raise errors.InvalidResourceType("file resource expected")
def _touch_file(self): # noinspection PyProtectedMember if not self.parent or not self.parent._exists: raise errors.ResourceDoesNotExist("Parent resource does not exist") if not self.parent.is_collection: raise errors.InvalidResourceType("Collection expected") # noinspection PyProtectedMember self._parent._resources[self.name] = self self._exists = True self._content = BytesIO() self._is_directory = False
async def make_collection(self, collection: str) -> 'AbstractResource': new_path = self.absolute / collection if new_path.exists(): raise errors.ResourceAlreadyExists() try: new_path.mkdir(exist_ok=True) except NotADirectoryError: raise errors.InvalidResourceType("collection expected") path = str(new_path.relative_to(self._root_dir)) return self.__class__(self.prefix, path, root_dir=self._root_dir)
async def get_content(self, write: typing.Callable[[bytes], typing.Any], *, offset: int = 0, limit: int = None): if self._is_directory: raise errors.InvalidResourceType("file resource expected") self._content.seek(offset) buffer = self._content.read(limit) if asyncio.iscoroutinefunction(write): await write(buffer) else: write(buffer)
async def move(self, destination: str) -> bool: old_name = self.name dest_resource = self._root.with_relative(destination) if not dest_resource._exists: self._path = dest_resource.path dest_resource = dest_resource._parent await dest_resource.populate_props() if not dest_resource.is_collection: raise errors.InvalidResourceType("collection expected") del self._parent._resources[old_name] self._parent = dest_resource self._path = os.path.join(self._parent.path, self.name) dest_resource._resources[self.name] = self return True
async def put_content(self, read_some: typing.Awaitable[bytes]) -> bool: if self._exists: created = False if self.is_collection: raise errors.InvalidResourceType("file resource expected") else: created = True self._touch_file() self._content.seek(0) self._content.truncate() if not read_some: return created while True: buffer = await read_some() self._content.write(buffer) if not buffer: return created
async def copy(self, destination: str) -> 'AbstractResource': if not self.is_collection: return self._copy_file(destination) dest = self._root / destination.strip('/') if not dest.is_collection: raise errors.InvalidResourceType("collection expected") if not dest._exists: name = dest.name dest = dest._parent else: name = self.name new_dir = self._clone() new_dir._parent = dest dest._resources[name] = new_dir new_dir._path = os.path.join(dest.path, name) for res in self.collection: await res.copy(new_dir.path) return new_dir
def with_relative(self, relative) -> 'AbstractResource': if relative == '/': return self res = self parts = relative.strip('/').split('/') for part in parts[:-1]: try: res = res._resources[part] except KeyError: raise errors.ResourceDoesNotExist( "one of parent resources does not exist") part = parts[-1] try: if not res.is_collection: raise errors.InvalidResourceType("collection expected") res = res._resources[part] except KeyError: res = DummyResource(self.prefix, os.path.join(res.path, part), parent=res, exists=False) return res
async def make_collection(self, collection: str) -> 'AbstractResource': collection = collection.strip('/') parent = os.path.dirname(collection) name = os.path.basename(collection) if not parent: parent = self else: parent = self._root.with_relative(parent) if not parent.is_collection: raise errors.InvalidResourceType("collection expected") # noinspection PyProtectedMember if name in parent._resources: raise errors.ResourceAlreadyExists("collection already exists") collection = DummyResource(prefix=self.prefix, path=os.path.join(parent.path, collection), parent=parent, is_collection=True, exists=True) # noinspection PyProtectedMember parent._resources[name] = collection return collection
async def get_content(self, write: typing.Callable[[bytes], typing.Any], *, offset: int = None, limit: int = None): try: with self.absolute.open('rb') as f: if offset: f.seek(offset) block_size = 1024**2 if not limit: limit = None while True: if limit is not None: buffer = f.read(min(block_size, limit)) else: buffer = f.read(block_size) await write(buffer) if limit is not None: limit -= len(buffer) if len(buffer) < block_size: break except IsADirectoryError: raise errors.InvalidResourceType("file resource expected")