import functools import logging import re from io import BytesIO from asyncify import asyncify from cachetools import cachedmethod from redis import Redis from ..settings import SETTINGS from .helpers import RedisCache, WebDAVclient, davkey _logger = logging.getLogger(__name__) class WebDAV: _webdav_client = WebDAVclient( { "webdav_hostname": SETTINGS.webdav.url, "webdav_login": SETTINGS.webdav.username, "webdav_password": SETTINGS.webdav.password, } ) _cache = RedisCache( cache=Redis( host=SETTINGS.redis.host, port=SETTINGS.redis.port, db=SETTINGS.redis.db, protocol=SETTINGS.redis.protocol, ), ttl=SETTINGS.webdav.cache_ttl, ) @classmethod @asyncify @cachedmethod( cache=lambda cls: cls._cache, key=functools.partial(davkey, "list_files"), ) def list_files( cls, directory: str = "", *, regex: re.Pattern[str] = re.compile(""), ) -> list[str]: """ List files in directory `directory` matching RegEx `regex` """ _logger.debug(f"list_files {directory!r}") ls = cls._webdav_client.list(directory) return [path for path in ls if regex.search(path)] @classmethod @asyncify @cachedmethod( cache=lambda cls: cls._cache, key=functools.partial(davkey, "exists"), ) def exists(cls, path: str) -> bool: """ `True` iff there is a WebDAV resource at `path` """ _logger.debug(f"file_exists {path!r}") return cls._webdav_client.check(path) @classmethod @asyncify @cachedmethod( cache=lambda cls: cls._cache, key=functools.partial(davkey, "read_bytes"), ) def read_bytes(cls, path: str) -> bytes: """ Load WebDAV file from `path` as bytes """ _logger.debug(f"read_bytes {path!r}") buffer = BytesIO() cls._webdav_client.download_from(buffer, path) buffer.seek(0) return buffer.read() @classmethod async def read_str(cls, path: str, encoding="utf-8") -> str: """ Load WebDAV file from `path` as string """ _logger.debug(f"read_str {path!r}") return (await cls.read_bytes(path)).decode(encoding=encoding).strip() @classmethod @asyncify def write_bytes(cls, path: str, buffer: bytes) -> None: """ Write bytes from `buffer` into WebDAV file at `path` """ _logger.debug(f"write_bytes {path!r}") cls._webdav_client.upload_to(buffer, path) # invalidate cache entry cls._cache.pop(davkey("read_bytes", None, path)) @classmethod async def write_str(cls, path: str, content: str, encoding="utf-8") -> None: """ Write string from `content` into WebDAV file at `path` """ _logger.debug(f"write_str {path!r}") await cls.write_bytes(path, content.encode(encoding=encoding))