Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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'])
Esempio n. 5
0
    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
        ]
Esempio n. 6
0
    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)
Esempio n. 7
0
    async def download(
        self,
        remote_path: Union[str, "os.PathLike[str]"],
        local_path: Union[str, "os.PathLike[str]"],
        progress: Optional[Callable[[int, int, Tuple], None]] = None,
        progress_args: Optional[Tuple] = ()
    ) -> None:
        """
        Download a remote resourse and put in local path.
        More information you can find by link http://webdav.org/specs/rfc4918.html#rfc.section.9.4

        WARNING: DESTRUCTIVE METHOD (This method can call `self.download_directory`)

        Parameters:    
            remote_path (``str``):
                The path to remote resource for downloading.

            local_path (``str``):
                The path to save resource locally.

            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.
        """

        urn = Urn(remote_path)
        if (await self.is_directory(urn.path())):
            await self.download_directory(local_path=local_path,
                                          remote_path=remote_path,
                                          progress=progress,
                                          progress_args=progress_args)
        else:
            await self.download_file(local_path=local_path,
                                     remote_path=remote_path,
                                     progress=progress,
                                     progress_args=progress_args)
Esempio n. 8
0
    async def move(self, path: Union[str, "os.PathLike[str]"]) -> None:
        """
        Move the resource to new path.

        Parameters:    
            path (``str``):
                New path of the resource.
        """
        new_urn = Urn(path)
        await self.client.move(source=self.urn.path(),
                               destination=new_urn.path())
        self.urn = new_urn
Esempio n. 9
0
    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)
Esempio n. 10
0
    async def upload_directory(
        self,
        remote_path: Union[str, "os.PathLike[str]"],
        local_path: Union[str, "os.PathLike[str]"],
        progress: Optional[Callable[[int, int, Tuple], None]] = None,
        progress_args: Optional[Tuple] = ()
    ) -> None:
        """
        Uploads directory to remote path on WebDAV server. In case directory is exist 
        on remote server it will delete it and then upload directory with nested files 
        and directories.
        More information you can find by link http://webdav.org/specs/rfc4918.html#METHOD_PUT

        WARNING: DESTRUCTIVE METHOD

        Parameters:    
            remote_path (``str``):
                The path to directory for uploading on WebDAV server.

            local_path (``str``):
                The path to local directory for uploading.

            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.
        """

        urn = Urn(remote_path, directory=True)
        if not urn.is_dir():
            raise OptionNotValid(name="remote_path", value=remote_path)

        if not os.path.isdir(local_path):
            raise OptionNotValid(name="local_path", value=local_path)

        if not os.path.exists(local_path):
            raise LocalResourceNotFound(local_path)

        if (await self.exists(urn.path())):
            await self.unlink(urn.path())

        await self.create_directory(remote_path)

        for resource_name in os.listdir(local_path):
            _remote_path = f"{urn.path()}{resource_name}".replace('\\', '')
            _local_path = os.path.join(local_path, resource_name)
            await self.upload(local_path=_local_path,
                              remote_path=_remote_path,
                              progress=progress,
                              progress_args=progress_args)
Esempio n. 11
0
    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)
Esempio n. 12
0
    async def download_directory(
        self,
        remote_path: Union[str, "os.PathLike[str]"],
        local_path: Union[str, "os.PathLike[str]"],
        progress: Optional[Callable[[int, int, Tuple], None]] = None,
        progress_args: Optional[Tuple] = ()
    ) -> None:
        """
        Downloads directory and downloads all nested files and directories from remote server to local.
        If there is something on local path it deletes directories and files then creates new.
        More information you can find by link http://webdav.org/specs/rfc4918.html#rfc.section.9.4

        WARNING: Destructive method

        Parameters:    
            remote_path (``str``):
                The path to directory for downloading form WebDAV server.

            local_path (``str``):
                The path to local directory for saving downloaded files and directories.

            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.
        """

        urn = Urn(remote_path, directory=True)

        if not (await self.is_directory(urn.path())):
            raise OptionNotValid(name="remote_path", value=remote_path)

        if os.path.exists(local_path):
            shutil.rmtree(local_path)

        os.makedirs(local_path)
        async for resource_name in self.list(urn.path()):
            if urn.path().endswith(resource_name):
                continue
            _remote_path = f"{urn.path()}{resource_name}"
            _local_path = os.path.join(local_path, resource_name)
            await self.download(local_path=_local_path,
                                remote_path=_remote_path,
                                progress=progress,
                                progress_args=progress_args)
Esempio n. 13
0
    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)
Esempio n. 14
0
 async def _check_remote_resource(self, path: Union[str,
                                                    "os.PathLike[str]"],
                                  urn: Urn) -> None:
     if not (await self.exists(urn.path())) and not (await self.exists(
             Urn(path, directory=True).path())):
         raise RemoteResourceNotFound(path)
Esempio n. 15
0
class Resource(object):
    """
    Remote resource.
    """
    def __init__(self, client: Client, urn: Urn) -> None:
        self.client = client
        self.urn = urn

    def __str__(self) -> str:
        return f"resource {self.urn.path()}"

    async def is_directory(self) -> bool:
        """
        Determine if the resource is a directory.

        Returns:
            :obj:`bool`: True if the resource is a directory or False else.
        """
        return await self.client.is_directory(self.urn.path())

    async def rename(self, name: str) -> None:
        """
        Rename the resource.

        Parameters:    
            name (``str``):
                New name to resource.
        """
        old_path = self.urn.path()
        parent_path = self.urn.parent()
        name = Urn(name).filename()
        new_path = f"{parent_path}{name}"

        await self.client.move(source=old_path, destination=new_path)
        self.urn = Urn(new_path)

    async def move(self, path: Union[str, "os.PathLike[str]"]) -> None:
        """
        Move the resource to new path.

        Parameters:    
            path (``str``):
                New path of the resource.
        """
        new_urn = Urn(path)
        await self.client.move(source=self.urn.path(),
                               destination=new_urn.path())
        self.urn = new_urn

    async def copy(self, path: Union[str, "os.PathLike[str]"]) -> "Resource":
        """
        Copy the resource to a another path.

        Parameters:    
            path (``str``):
                The path where resource will be copied.

        Returns:
            :obj:`Resource`: The value of property or None if property is not found.
        """
        urn = Urn(path)
        await self.client.copy(source=self.urn.path(), destination=path)
        return Resource(self.client, urn)

    async def info(self,
                   filter: Optional[Iterable[str]] = None) -> Dict[str, str]:
        """
        Get a dictionary with resource information.

        Parameters:    
            filter (``Iterable[str]``, *optional*):
                If filter is not `None` then only return properties
                contained in filter iterable.

        Returns:
            :obj:`Dict[str, str]`: Information about the resource
        """

        info = await self.client.info(self.urn.path())
        if not filter:
            return info
        return {key: value for (key, value) in info.items() if key in filter}

    async def unlink(self) -> None:
        """
        Delete the resource.
        """

        await self.client.unlink(self.urn.path())

    async def delete(self):
        """
        Delete the resource.
        """

        await self.unlink()

    async def exists(self) -> bool:
        """
        Determine if the resource exists in the remote WebDAV server

        Returns:
            :obj:`bool`: True if the resource exists else return False.
        """

        return await self.client.exists(self.urn.path())