2022-08-31 10:20:54 +00:00
|
|
|
import asyncio
|
|
|
|
import functools
|
2022-08-29 11:27:18 +00:00
|
|
|
import logging
|
2022-09-02 12:20:29 +00:00
|
|
|
import time
|
2022-08-29 11:27:18 +00:00
|
|
|
from io import BytesIO
|
2022-09-02 12:20:29 +00:00
|
|
|
from typing import Any, Optional
|
2022-08-29 11:27:18 +00:00
|
|
|
|
2022-09-02 12:20:29 +00:00
|
|
|
from async_lru import alru_cache
|
2022-08-31 08:42:17 +00:00
|
|
|
from webdav3.client import Resource
|
2022-08-29 11:27:18 +00:00
|
|
|
|
2022-09-02 15:28:52 +00:00
|
|
|
from . import webdav_resource
|
2022-09-02 12:20:29 +00:00
|
|
|
|
2022-08-29 11:27:18 +00:00
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-09-02 12:20:29 +00:00
|
|
|
def _run_in_executor(f):
|
2022-08-31 10:20:54 +00:00
|
|
|
"""
|
|
|
|
Decorator to make blocking function call asyncio compatible
|
|
|
|
https://stackoverflow.com/questions/41063331/how-to-use-asyncio-with-existing-blocking-library/
|
|
|
|
"""
|
|
|
|
|
|
|
|
@functools.wraps(f)
|
|
|
|
def inner(*args, **kwargs):
|
|
|
|
loop = asyncio.get_running_loop()
|
2022-08-31 10:21:55 +00:00
|
|
|
return loop.run_in_executor(
|
|
|
|
None,
|
|
|
|
functools.partial(f, *args, **kwargs),
|
|
|
|
)
|
2022-08-31 10:20:54 +00:00
|
|
|
|
|
|
|
return inner
|
|
|
|
|
|
|
|
|
2022-09-02 12:20:29 +00:00
|
|
|
def _get_ttl_hash(seconds: int = 20) -> int:
|
|
|
|
"""
|
|
|
|
Return the same value within `seconds` time period
|
|
|
|
https://stackoverflow.com/a/55900800
|
|
|
|
"""
|
|
|
|
return round(time.time() / seconds)
|
|
|
|
|
|
|
|
|
|
|
|
@alru_cache(maxsize=20)
|
|
|
|
async def _get_buffer(
|
|
|
|
remote_path: Any,
|
|
|
|
ttl_hash: Optional[int] = None,
|
|
|
|
) -> BytesIO:
|
|
|
|
del ttl_hash
|
|
|
|
|
|
|
|
@_run_in_executor
|
|
|
|
def buffer_inner(resource: Resource) -> BytesIO:
|
|
|
|
_logger.info(f"updating {resource}")
|
|
|
|
print(f"updating {resource}")
|
|
|
|
buffer = BytesIO()
|
|
|
|
resource.write_to(buffer)
|
|
|
|
return buffer
|
|
|
|
|
2022-09-02 15:28:52 +00:00
|
|
|
resource = webdav_resource(remote_path)
|
2022-09-02 12:20:29 +00:00
|
|
|
return await buffer_inner(resource)
|
|
|
|
|
|
|
|
|
2022-08-29 11:27:18 +00:00
|
|
|
class DavFile:
|
2022-09-02 12:20:29 +00:00
|
|
|
def __init__(self, remote_path: Any) -> None:
|
|
|
|
self.__remote_path = remote_path
|
|
|
|
|
|
|
|
@property
|
|
|
|
async def __buffer(self) -> BytesIO:
|
|
|
|
return await _get_buffer(
|
|
|
|
remote_path=self.__remote_path,
|
|
|
|
ttl_hash=_get_ttl_hash(20),
|
2022-08-31 01:29:44 +00:00
|
|
|
)
|
2022-08-29 11:27:18 +00:00
|
|
|
|
|
|
|
@property
|
2022-09-02 12:20:29 +00:00
|
|
|
async def bytes(self) -> bytes:
|
|
|
|
buffer = await self.__buffer
|
|
|
|
|
|
|
|
buffer.seek(0)
|
|
|
|
return buffer.read()
|
2022-08-29 11:27:18 +00:00
|
|
|
|
2022-09-02 12:20:29 +00:00
|
|
|
@property
|
|
|
|
async def string(self) -> str:
|
|
|
|
bytes = await self.bytes
|
|
|
|
return bytes.decode(encoding="utf-8")
|