fix redis keying and de/serializing

This commit is contained in:
Jörn-Michael Miehe 2023-11-09 12:16:25 +01:00
parent 5524c4094b
commit 27de977bfe
3 changed files with 38 additions and 54 deletions

View file

@ -1,17 +1,16 @@
import functools
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import cast from typing import cast
from asyncify import asyncify from asyncify import asyncify
from cachetools import TTLCache, cachedmethod from cachetools import cachedmethod
from caldav import Calendar, DAVClient, Event, Principal from caldav import Calendar, DAVClient, Event, Principal
from vobject.base import Component, toVName from vobject.base import Component, toVName
from ..calevent import CalEvent from ..calevent import CalEvent
from ..config import Config from ..config import Config
from ..settings import SETTINGS from ..settings import SETTINGS
from .webdav import davkey from .helpers import REDIS, RedisCache, davkey
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -23,9 +22,9 @@ class CalDAV:
password=SETTINGS.caldav.password, password=SETTINGS.caldav.password,
) )
_cache = TTLCache( _cache = RedisCache(
cache=REDIS,
ttl=SETTINGS.caldav.cache_ttl, ttl=SETTINGS.caldav.cache_ttl,
maxsize=SETTINGS.caldav.cache_size,
) )
@classmethod @classmethod
@ -40,10 +39,7 @@ class CalDAV:
@classmethod @classmethod
@property @property
@asyncify @asyncify
@cachedmethod( @cachedmethod(cache=lambda cls: cls._cache, key=davkey("calendars"))
cache=lambda cls: cls._cache,
key=functools.partial(davkey, "calendars"),
)
def calendars(cls) -> list[str]: def calendars(cls) -> list[str]:
""" """
Asynchroneously lists all calendars using the main WebDAV client. Asynchroneously lists all calendars using the main WebDAV client.
@ -54,10 +50,7 @@ class CalDAV:
@classmethod @classmethod
@asyncify @asyncify
@cachedmethod( @cachedmethod(cache=lambda cls: cls._cache, key=davkey("get_calendar"))
cache=lambda cls: cls._cache,
key=functools.partial(davkey, "get_calendar"),
)
def get_calendar(cls, calendar_name: str) -> Calendar: def get_calendar(cls, calendar_name: str) -> Calendar:
""" """
Get a calendar by name using the CalDAV principal object. Get a calendar by name using the CalDAV principal object.
@ -67,10 +60,7 @@ class CalDAV:
@classmethod @classmethod
@asyncify @asyncify
@cachedmethod( @cachedmethod(cache=lambda cls: cls._cache, key=davkey("get_events", slice(1, 2)))
cache=lambda cls: cls._cache,
key=functools.partial(davkey, "get_events"),
)
def get_events(cls, calendar_name: str, cfg: Config) -> list[CalEvent]: def get_events(cls, calendar_name: str, cfg: Config) -> list[CalEvent]:
""" """
Get a sorted list of events by CalDAV calendar name. Get a sorted list of events by CalDAV calendar name.

View file

@ -1,19 +1,29 @@
from json import JSONDecodeError import pickle
from typing import Callable, Hashable
import requests import requests
from cachetools.keys import hashkey from cachetools.keys import hashkey
from CacheToolsUtils import RedisCache as __RedisCache from CacheToolsUtils import RedisCache as __RedisCache
from redis import Redis
from redis.commands.core import ResponseT from redis.commands.core import ResponseT
from redis.typing import EncodableT from redis.typing import EncodableT
from webdav3.client import Client as __WebDAVclient from webdav3.client import Client as __WebDAVclient
from ..settings import SETTINGS
def davkey(name, _, *args, **kwargs):
def davkey(
name: str,
slice: slice = slice(1, None),
) -> Callable[..., tuple[Hashable, ...]]:
def func(*args, **kwargs) -> tuple[Hashable, ...]:
"""Return a cache key for use with cached methods.""" """Return a cache key for use with cached methods."""
key = hashkey(name, *args, **kwargs) key = hashkey(name, *args[slice], **kwargs)
return hashkey(*(str(key_item) for key_item in key)) return hashkey(*(str(key_item) for key_item in key))
return func
class WebDAVclient(__WebDAVclient): class WebDAVclient(__WebDAVclient):
def execute_request( def execute_request(
@ -39,16 +49,16 @@ class RedisCache(__RedisCache):
""" """
def _serialize(self, s) -> EncodableT: def _serialize(self, s) -> EncodableT:
if isinstance(s, bytes): return pickle.dumps(s)
return s
else:
return super()._serialize(s)
def _deserialize(self, s: ResponseT): def _deserialize(self, s: ResponseT):
try:
return super()._deserialize(s)
except (UnicodeDecodeError, JSONDecodeError):
assert isinstance(s, bytes) assert isinstance(s, bytes)
return s return pickle.loads(s)
REDIS = Redis(
host=SETTINGS.redis.host,
port=SETTINGS.redis.port,
db=SETTINGS.redis.db,
protocol=SETTINGS.redis.protocol,
)

View file

@ -1,14 +1,12 @@
import functools
import logging import logging
import re import re
from io import BytesIO from io import BytesIO
from asyncify import asyncify from asyncify import asyncify
from cachetools import cachedmethod from cachetools import cachedmethod
from redis import Redis
from ..settings import SETTINGS from ..settings import SETTINGS
from .helpers import RedisCache, WebDAVclient, davkey from .helpers import REDIS, RedisCache, WebDAVclient, davkey
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -23,21 +21,13 @@ class WebDAV:
) )
_cache = RedisCache( _cache = RedisCache(
cache=Redis( cache=REDIS,
host=SETTINGS.redis.host,
port=SETTINGS.redis.port,
db=SETTINGS.redis.db,
protocol=SETTINGS.redis.protocol,
),
ttl=SETTINGS.webdav.cache_ttl, ttl=SETTINGS.webdav.cache_ttl,
) )
@classmethod @classmethod
@asyncify @asyncify
@cachedmethod( @cachedmethod(cache=lambda cls: cls._cache, key=davkey("list_files"))
cache=lambda cls: cls._cache,
key=functools.partial(davkey, "list_files"),
)
def list_files( def list_files(
cls, cls,
directory: str = "", directory: str = "",
@ -55,10 +45,7 @@ class WebDAV:
@classmethod @classmethod
@asyncify @asyncify
@cachedmethod( @cachedmethod(cache=lambda cls: cls._cache, key=davkey("exists"))
cache=lambda cls: cls._cache,
key=functools.partial(davkey, "exists"),
)
def exists(cls, path: str) -> bool: def exists(cls, path: str) -> bool:
""" """
`True` iff there is a WebDAV resource at `path` `True` iff there is a WebDAV resource at `path`
@ -69,10 +56,7 @@ class WebDAV:
@classmethod @classmethod
@asyncify @asyncify
@cachedmethod( @cachedmethod(cache=lambda cls: cls._cache, key=davkey("read_bytes"))
cache=lambda cls: cls._cache,
key=functools.partial(davkey, "read_bytes"),
)
def read_bytes(cls, path: str) -> bytes: def read_bytes(cls, path: str) -> bytes:
""" """
Load WebDAV file from `path` as bytes Load WebDAV file from `path` as bytes
@ -105,7 +89,7 @@ class WebDAV:
cls._webdav_client.upload_to(buffer, path) cls._webdav_client.upload_to(buffer, path)
# invalidate cache entry # invalidate cache entry
cls._cache.pop(davkey("read_bytes", None, path)) cls._cache.pop(davkey("read_bytes")(path))
@classmethod @classmethod
async def write_str(cls, path: str, content: str, encoding="utf-8") -> None: async def write_str(cls, path: str, content: str, encoding="utf-8") -> None: