""" Definition of an asyncio compatible WebDAV file. Caches files using `timed_alru_cache`. """ from dataclasses import dataclass from io import BytesIO from logging import getLogger from typing import Any from webdav3.client import Resource from .async_helpers import get_ttl_hash, run_in_executor, timed_alru_cache from .dav_common import webdav_resource from .settings import SETTINGS _logger = getLogger(__name__) @timed_alru_cache(maxsize=SETTINGS.cache_size) async def _get_buffer( remote_path: Any, ) -> BytesIO: """ Download file contents into a new `BytesIO` object. """ @run_in_executor def _inner() -> BytesIO: _logger.info(f"downloading {remote_path!r} ...") resource = webdav_resource(remote_path) buffer = BytesIO() resource.write_to(buffer) return buffer return await _inner() @dataclass(frozen=True) class DavFile: """ Object representation of a WebDAV file. """ remote_path: str @property def resource(self) -> Resource: """ WebDAV file handle. """ return webdav_resource(self.remote_path) @property async def __buffer(self) -> BytesIO: """ File contents as binary stream. """ return await _get_buffer( ttl_hash=get_ttl_hash(), # type: ignore remote_path=self.remote_path, ) @property async def as_bytes(self) -> bytes: """ File contents as binary data. """ buffer = await self.__buffer buffer.seek(0) return buffer.read() @property async def as_string(self) -> str: """ File contents as string. """ bytes = await self.as_bytes return bytes.decode(encoding="utf-8") async def write(self, content: bytes) -> None: """ Write bytes into file. """ @run_in_executor def _inner() -> None: buffer = BytesIO(content) self.resource.read_from(buffer) await _inner()