async def copy(self, source: Union[str, "os.PathLike[str]"], destination: Union[str, "os.PathLike[str]"], depth: Optional[int] = 1) -> None: """ Copies resource from one place to another on WebDAV server. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_COPY Parameters: source (``str``): The path to resource which will be copied. destination (``str``): the path where resource will be copied. depth (``int``, *optional*): Folder depth to copy. Default is 1 """ urn_from = Urn(source) if not (await self.exists(urn_from.path())): raise RemoteResourceNotFound(urn_from.path()) urn_to = Urn(destination) if not (await self.exists(urn_to.parent())): raise RemoteParentNotFound(urn_to.path()) headers = {"Destination": self._get_url(urn_to.quote())} if (await self.is_directory(urn_from.path())): headers["Depth"] = depth await self._execute_request(action='copy', path=urn_from.quote(), headers_ext=headers)
async def move(self, source: Union[str, "os.PathLike[str]"], destination: Union[str, "os.PathLike[str]"], overwrite: Optional[bool] = False) -> None: """ Moves resource from one place to another on WebDAV server. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_MOVE Parameters: source (``str``): The path to resource which will be moved. destination (``str``): the path where resource will be moved. overwrite (``bool``, *optional*): The flag, overwrite file if it exists. Defaults is False. """ urn_from = Urn(source) if not (await self.exists(urn_from.path())): raise RemoteResourceNotFound(urn_from.path()) urn_to = Urn(destination) if not (await self.exists(urn_to.parent())): raise RemoteParentNotFound(urn_to.path()) headers = { 'Destination': self._get_url(urn_to.quote()), 'Overwrite': ('T' if overwrite else 'F') } await self._execute_request(action='move', path=urn_from.quote(), headers_ext=headers)
async def download_iter( self, path: Union[str, "os.PathLike[str]"]) -> Generator[bytes, None, None]: """ Downloads file from server and return content in generator. More information you can find by link http://webdav.org/specs/rfc4918.html#rfc.section.9.4 Parameters: path (``str``): The path to remote resource Returns: :obj:`Generator[bytes]`: Return a generator to get file data Example: .. code-block:: python ... async for chunk in client.download_iter('/path/to/file.zip'): file.write(chunk) ... """ urn = Urn(path) if (await self.is_directory(urn.path())): raise OptionNotValid(name="path", value=path) if not (await self.exists(urn.path())): raise RemoteResourceNotFound(urn.path()) response = await self._execute_request(action='download', path=urn.quote()) return response.content.iter_chunked(self._chunk_size)
async def create_directory(self, path: Union[str, "os.PathLike[str]"]) -> bool: """ Makes new directory on WebDAV server. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_MKCOL Parameters: path (``str``): Path to remote directory. Returns: :obj:`bool`: True if request executed with code 200 or 201 and False otherwise. """ directory_urn = Urn(path, directory=True) if not (await self.exists(directory_urn.parent())): raise RemoteParentNotFound(directory_urn.path()) try: response = await self._execute_request(action='mkdir', path=directory_urn.quote()) except MethodNotSupported: # Yandex WebDAV returns 405 status code when directory already exists return True return response.status in (200, 201)
async def info(self, path: Union[str, "os.PathLike[str]"]) -> Dict[str, str]: """ Gets information about resource on WebDAV. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PROPFIND Parameters: path (``str``): Path to remote resource. Returns: :obj:`Dict[str, str]`: a dictionary of information attributes and them values with following keys: `created`: date of resource creation, `name`: name of resource, `size`: size of resource, `modified`: date of resource modification, `etag`: etag of resource. """ urn = Urn(path) await self._check_remote_resource(path, urn) response = await self._execute_request(action='info', path=urn.quote()) text = await response.text() path = self.get_full_path(urn) return WebDavXmlUtils.parse_info_response(content=text, path=path, hostname=self._hostname)
async def get_property(self, path: Union[str, "os.PathLike[str]"], option: Dict[str, str]) -> Union[str, None]: """ Gets metadata property of remote resource on WebDAV server. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PROPFIND Parameters: path (``str``): Path to remote directory. option (``Dict[str, str]``): the property attribute as dictionary with following keys: `namespace`: (optional) the namespace for XML property which will be set, `name`: the name of property which will be set. Returns: :obj:`str` | None: The value of property or None if property is not found. """ urn = Urn(path) if not (await self.exists(urn.path())): raise RemoteResourceNotFound(urn.path()) data = WebDavXmlUtils.create_get_property_request_content(option) response = await self._execute_request(action='get_property', path=urn.quote(), data=data) text = await response.text() return WebDavXmlUtils.parse_get_property_response(text, option['name'])
async def unlink(self, path: Union[str, "os.PathLike[str]"]) -> None: """ Cleans (Deletes) a remote resource on WebDAV server. The name of method is not changed for back compatibility with original library. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_DELETE Parameters: path (``str``): Path to remote resource. """ urn = Urn(path) await self._execute_request(action='clean', path=urn.quote())
async def list( self, path: Optional[Union[str, "os.PathLike[str]"]] = ROOT, get_info: Optional[bool] = False ) -> Union[List[str], List[Dict[str, str]]]: """ Returns list of nested files and directories for remote WebDAV directory by path. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PROPFIN Parameters: path (``str``): Path to remote directory. get_info (``bool``, *optional*): Set true to get more information like cmd 'ls -l'. Returns: List of :obj:`str` | List of :obj:`Dict[str, str]`: On success, if get_info=False it returns list of nested file or directory names, otherwise it returns list of information, the information is a dictionary and it values with following keys: `created`: date of resource creation, `name`: name of resource, `size`: size of resource, `modified`: date of resource modification, `etag`: etag of resource, `isdir`: type of resource, `path`: path of resource. """ directory_urn = Urn(path, directory=True) if directory_urn.path() != Client.ROOT and not (await self.exists( directory_urn.path())): raise RemoteResourceNotFound(directory_urn.path()) path = Urn.normalize_path(self.get_full_path(directory_urn)) response = await self._execute_request(action='list', path=directory_urn.quote()) text = await response.text() if get_info: subfiles = WebDavXmlUtils.parse_get_list_info_response(text) return [ subfile for subfile in subfiles if Urn.compare_path(path, subfile.get('path')) is False ] urns = WebDavXmlUtils.parse_get_list_response(text) return [ urn.filename() for urn in urns if Urn.compare_path(path, urn.path()) is False ]
async def exists(self, path: Union[str, "os.PathLike[str]"]) -> bool: """ Checks an existence of remote resource on WebDAV server by remote path. More information you can find by link http://webdav.org/specs/rfc4918.html#rfc.section.9.4 Parameters: path (``str``): Path to remote resource. Returns: :obj:`bool`: True if resource is exist or False otherwise. """ urn = Urn(path) try: response = await self._execute_request(action='check', path=urn.quote()) except RemoteResourceNotFound: return False except ResponseErrorCode: return False return (int(response.status) == 200)
async def is_directory(self, path: Union[str, "os.PathLike[str]"]) -> bool: """ Checks is the remote resource directory. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PROPFINDL Parameters: path (``str``): Path to remote directory. Returns: :obj:`bool`: True in case the remote resource is directory and False otherwise. """ urn = Urn(path) parent_urn = Urn(urn.parent()) await self._check_remote_resource(path, urn) response = await self._execute_request(action='info', path=parent_urn.quote()) text = await response.text() path = self.get_full_path(urn) return WebDavXmlUtils.parse_is_dir_response(content=text, path=path, hostname=self._hostname)
async def set_property(self, path: Union[str, "os.PathLike[str]"], option: Dict[str, str]) -> None: """ Sets metadata property of remote resource on WebDAV server. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PROPPATCH Parameters: path (``str``): Path to remote directory. option (``Dict[str, str]``): the property attribute as dictionary with following keys: `namespace`: (optional) the namespace for XML property which will be set, `name`: the name of property which will be set, `value`: (optional) the value of property which will be set. Defaults is empty string. """ urn = Urn(path) if not (await self.check(urn.path())): raise RemoteResourceNotFound(urn.path()) data = WebDavXmlUtils.create_set_property_batch_request_content(option) await self._execute_request(action='set_property', path=urn.quote(), data=data)
async def upload_to( self, path: Union[str, "os.PathLike[str]"], buffer: Union[IO, AsyncGenerator[bytes, None]], buffer_size: Optional[int] = None, progress: Optional[Callable[[int, int, Tuple], None]] = None, progress_args: Optional[Tuple] = () ) -> None: """ Uploads file from buffer to remote path on WebDAV server. More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PUT Parameters: path (``str``): The path to remote resource buffer (``IO``) IO like object to read the data or a asynchronous generator to get buffer data. In order do you select use a async generator `progress` callback cannot be called. progress (``callable``, *optional*): Pass a callback function to view the file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total size of the file. *args (``tuple``, *optional*): Extra custom arguments as defined in the ``progress_args`` parameter. You can either keep ``*args`` or add every single extra argument in your function signature. Example: .. code-block:: python ... # Keep track of the progress while uploading def progress(current, total): print(f"{current * 100 / total:.1f}%") async with aiofiles.open('file.zip', 'rb') as file: await client.upload_to('/path/to/file.zip', file, progress=progress) ... """ urn = Urn(path) if urn.is_dir(): raise OptionNotValid(name="path", value=path) if not (await self.exists(urn.parent())): raise RemoteParentNotFound(urn.path()) if callable(progress) and not asyncio.iscoroutinefunction(buffer): async def file_sender(buff: IO): current = 0 if asyncio.iscoroutinefunction(progress): await progress(current, buffer_size, *progress_args) else: progress(current, buffer_size, *progress_args) while current < buffer_size: chunk = await buffer.read(self._chunk_size) if isinstance(buffer, AsyncBufferedIOBase) \ else buffer.read(self._chunk_size) if not chunk: break current += len(chunk) if asyncio.iscoroutinefunction(progress): await progress(current, buffer_size, *progress_args) else: progress(current, buffer_size, *progress_args) yield chunk await self._execute_request(action='upload', path=urn.quote(), data=file_sender(buffer)) else: await self._execute_request(action='upload', path=urn.quote(), data=buffer)
async def download_to( self, path: Union[str, "os.PathLike[str]"], buffer: IO, progress: Optional[Callable[[int, int, Tuple], None]] = None, progress_args: Optional[Tuple] = () ) -> None: """ Downloads file from server and writes it in buffer. More information you can find by link http://webdav.org/specs/rfc4918.html#rfc.section.9.4 Parameters: path (``str``): The path to remote resource buffer (``IO``) IO like object to write the data of remote file. progress (``callable``, *optional*): Pass a callback function to view the file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total size of the file. *args (``tuple``, *optional*): Extra custom arguments as defined in the ``progress_args`` parameter. You can either keep ``*args`` or add every single extra argument in your function signature. Example: .. code-block:: python ... # Keep track of the progress while downloading def progress(current, total): print(f"{current * 100 / total:.1f}%") async with aiofiles.open('file.zip', 'wb') as file: await client.download_to('/path/to/file.zip', file, progress=progress) ... """ urn = Urn(path) if (await self.is_directory(urn.path())): raise OptionNotValid(name="path", value=path) if not (await self.exists(urn.path())): raise RemoteResourceNotFound(urn.path()) response = await self._execute_request('download', urn.quote()) total = int(response.headers['content-length']) current = 0 if callable(progress): if asyncio.iscoroutinefunction(progress): await progress(current, total, *progress_args) else: progress(current, total, *progress_args) async for block in response.content.iter_chunked(self._chunk_size): if isinstance(buffer, AsyncBufferedIOBase): await buffer.write(block) else: buffer.write(block) current += len(block) if callable(progress): if asyncio.iscoroutinefunction(progress): await progress(current, total, *progress_args) else: progress(current, total, *progress_args)