diff options
author | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:17:55 -0400 |
---|---|---|
committer | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:17:55 -0400 |
commit | 12cf076118570eebbff08c6b3090e0d4798447a1 (patch) | |
tree | 3ba25e17e3c3a5e82316558ba3864b955919ff72 /venv/lib/python3.11/site-packages/litestar/stores | |
parent | c45662ff3923b34614ddcc8feb9195541166dcc5 (diff) |
no venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/litestar/stores')
12 files changed, 0 insertions, 698 deletions
diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__init__.py b/venv/lib/python3.11/site-packages/litestar/stores/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__init__.py +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/__init__.cpython-311.pyc Binary files differdeleted file mode 100644 index 78604d0..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/__init__.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/base.cpython-311.pyc b/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/base.cpython-311.pyc Binary files differdeleted file mode 100644 index 21d2fb4..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/base.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/file.cpython-311.pyc b/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/file.cpython-311.pyc Binary files differdeleted file mode 100644 index b39333c..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/file.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/memory.cpython-311.pyc b/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/memory.cpython-311.pyc Binary files differdeleted file mode 100644 index c8e3b05..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/memory.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/redis.cpython-311.pyc b/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/redis.cpython-311.pyc Binary files differdeleted file mode 100644 index 21e7e2f..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/redis.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/registry.cpython-311.pyc b/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/registry.cpython-311.pyc Binary files differdeleted file mode 100644 index c87c31a..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/registry.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/litestar/stores/base.py b/venv/lib/python3.11/site-packages/litestar/stores/base.py deleted file mode 100644 index 34aa514..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/base.py +++ /dev/null @@ -1,145 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from datetime import datetime, timedelta, timezone -from typing import TYPE_CHECKING, Optional - -from msgspec import Struct -from msgspec.msgpack import decode as msgpack_decode -from msgspec.msgpack import encode as msgpack_encode - -if TYPE_CHECKING: - from types import TracebackType - - from typing_extensions import Self - - -__all__ = ("Store", "NamespacedStore", "StorageObject") - - -class Store(ABC): - """Thread and process safe asynchronous key/value store.""" - - @abstractmethod - async def set(self, key: str, value: str | bytes, expires_in: int | timedelta | None = None) -> None: - """Set a value. - - Args: - key: Key to associate the value with - value: Value to store - expires_in: Time in seconds before the key is considered expired - - Returns: - ``None`` - """ - raise NotImplementedError - - @abstractmethod - async def get(self, key: str, renew_for: int | timedelta | None = None) -> bytes | None: - """Get a value. - - Args: - key: Key associated with the value - renew_for: If given and the value had an initial expiry time set, renew the - expiry time for ``renew_for`` seconds. If the value has not been set - with an expiry time this is a no-op - - Returns: - The value associated with ``key`` if it exists and is not expired, else - ``None`` - """ - raise NotImplementedError - - @abstractmethod - async def delete(self, key: str) -> None: - """Delete a value. - - If no such key exists, this is a no-op. - - Args: - key: Key of the value to delete - """ - raise NotImplementedError - - @abstractmethod - async def delete_all(self) -> None: - """Delete all stored values.""" - raise NotImplementedError - - @abstractmethod - async def exists(self, key: str) -> bool: - """Check if a given ``key`` exists.""" - raise NotImplementedError - - @abstractmethod - async def expires_in(self, key: str) -> int | None: - """Get the time in seconds ``key`` expires in. If no such ``key`` exists or no - expiry time was set, return ``None``. - """ - raise NotImplementedError - - async def __aenter__(self) -> None: # noqa: B027 - pass - - async def __aexit__( # noqa: B027 - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - pass - - -class NamespacedStore(Store): - """A subclass of :class:`Store`, offering hierarchical namespacing. - - Bulk actions on a parent namespace should affect all child namespaces, whereas other operations on all namespaces - should be isolated. - """ - - @abstractmethod - def with_namespace(self, namespace: str) -> Self: - """Return a new instance of :class:`NamespacedStore`, which exists in a child namespace of the current namespace. - Bulk actions on the parent namespace should affect all child namespaces, whereas other operations on all - namespaces should be isolated. - """ - - -class StorageObject(Struct): - """:class:`msgspec.Struct` to store serialized data alongside with their expiry time.""" - - expires_at: Optional[datetime] # noqa: UP007 - data: bytes - - @classmethod - def new(cls, data: bytes, expires_in: int | timedelta | None) -> StorageObject: - """Construct a new :class:`StorageObject` instance.""" - if expires_in is not None and not isinstance(expires_in, timedelta): - expires_in = timedelta(seconds=expires_in) - return cls( - data=data, - expires_at=(datetime.now(tz=timezone.utc) + expires_in) if expires_in else None, - ) - - @property - def expired(self) -> bool: - """Return if the :class:`StorageObject` is expired""" - return self.expires_at is not None and datetime.now(tz=timezone.utc) >= self.expires_at - - @property - def expires_in(self) -> int: - """Return the expiry time of this ``StorageObject`` in seconds. If no expiry time - was set, return ``-1``. - """ - if self.expires_at: - return int(self.expires_at.timestamp() - datetime.now(tz=timezone.utc).timestamp()) - return -1 - - def to_bytes(self) -> bytes: - """Encode the instance to bytes""" - return msgpack_encode(self) - - @classmethod - def from_bytes(cls, raw: bytes) -> StorageObject: - """Load a previously encoded with :meth:`StorageObject.to_bytes`""" - return msgpack_decode(raw, type=cls) diff --git a/venv/lib/python3.11/site-packages/litestar/stores/file.py b/venv/lib/python3.11/site-packages/litestar/stores/file.py deleted file mode 100644 index 25c52eb..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/file.py +++ /dev/null @@ -1,170 +0,0 @@ -from __future__ import annotations - -import os -import shutil -import unicodedata -from tempfile import mkstemp -from typing import TYPE_CHECKING - -from anyio import Path - -from litestar.concurrency import sync_to_thread - -from .base import NamespacedStore, StorageObject - -__all__ = ("FileStore",) - - -if TYPE_CHECKING: - from datetime import timedelta - from os import PathLike - - -def _safe_file_name(name: str) -> str: - name = unicodedata.normalize("NFKD", name) - return "".join(c if c.isalnum() else str(ord(c)) for c in name) - - -class FileStore(NamespacedStore): - """File based, thread and process safe, asynchronous key/value store.""" - - __slots__ = {"path": "file path"} - - def __init__(self, path: PathLike[str]) -> None: - """Initialize ``FileStorage``. - - Args: - path: Path to store data under - """ - self.path = Path(path) - - def with_namespace(self, namespace: str) -> FileStore: - """Return a new instance of :class:`FileStore`, using a sub-path of the current store's path.""" - if not namespace.isalnum(): - raise ValueError(f"Invalid namespace: {namespace!r}") - return FileStore(self.path / namespace) - - def _path_from_key(self, key: str) -> Path: - return self.path / _safe_file_name(key) - - @staticmethod - async def _load_from_path(path: Path) -> StorageObject | None: - try: - data = await path.read_bytes() - return StorageObject.from_bytes(data) - except FileNotFoundError: - return None - - def _write_sync(self, target_file: Path, storage_obj: StorageObject) -> None: - try: - tmp_file_fd, tmp_file_name = mkstemp(dir=self.path, prefix=f"{target_file.name}.tmp") - renamed = False - try: - try: - os.write(tmp_file_fd, storage_obj.to_bytes()) - finally: - os.close(tmp_file_fd) - - os.replace(tmp_file_name, target_file) # noqa: PTH105 - renamed = True - finally: - if not renamed: - os.unlink(tmp_file_name) # noqa: PTH108 - except OSError: - pass - - async def _write(self, target_file: Path, storage_obj: StorageObject) -> None: - await sync_to_thread(self._write_sync, target_file, storage_obj) - - async def set(self, key: str, value: str | bytes, expires_in: int | timedelta | None = None) -> None: - """Set a value. - - Args: - key: Key to associate the value with - value: Value to store - expires_in: Time in seconds before the key is considered expired - - Returns: - ``None`` - """ - - await self.path.mkdir(exist_ok=True) - path = self._path_from_key(key) - if isinstance(value, str): - value = value.encode("utf-8") - storage_obj = StorageObject.new(data=value, expires_in=expires_in) - await self._write(path, storage_obj) - - async def get(self, key: str, renew_for: int | timedelta | None = None) -> bytes | None: - """Get a value. - - Args: - key: Key associated with the value - renew_for: If given and the value had an initial expiry time set, renew the - expiry time for ``renew_for`` seconds. If the value has not been set - with an expiry time this is a no-op - - Returns: - The value associated with ``key`` if it exists and is not expired, else - ``None`` - """ - path = self._path_from_key(key) - storage_obj = await self._load_from_path(path) - - if not storage_obj: - return None - - if storage_obj.expired: - await path.unlink(missing_ok=True) - return None - - if renew_for and storage_obj.expires_at: - await self.set(key, value=storage_obj.data, expires_in=renew_for) - - return storage_obj.data - - async def delete(self, key: str) -> None: - """Delete a value. - - If no such key exists, this is a no-op. - - Args: - key: Key of the value to delete - """ - path = self._path_from_key(key) - await path.unlink(missing_ok=True) - - async def delete_all(self) -> None: - """Delete all stored values. - - Note: - This deletes and recreates :attr:`FileStore.path` - """ - - await sync_to_thread(shutil.rmtree, self.path) - await self.path.mkdir(exist_ok=True) - - async def delete_expired(self) -> None: - """Delete expired items. - - Since expired items are normally only cleared on access (i.e. when calling - :meth:`.get`), this method should be called in regular intervals - to free disk space. - """ - async for file in self.path.iterdir(): - wrapper = await self._load_from_path(file) - if wrapper and wrapper.expired: - await file.unlink(missing_ok=True) - - async def exists(self, key: str) -> bool: - """Check if a given ``key`` exists.""" - path = self._path_from_key(key) - return await path.exists() - - async def expires_in(self, key: str) -> int | None: - """Get the time in seconds ``key`` expires in. If no such ``key`` exists or no - expiry time was set, return ``None``. - """ - if storage_obj := await self._load_from_path(self._path_from_key(key)): - return storage_obj.expires_in - return None diff --git a/venv/lib/python3.11/site-packages/litestar/stores/memory.py b/venv/lib/python3.11/site-packages/litestar/stores/memory.py deleted file mode 100644 index 1da8931..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/memory.py +++ /dev/null @@ -1,115 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import anyio -from anyio import Lock - -from .base import StorageObject, Store - -__all__ = ("MemoryStore",) - - -if TYPE_CHECKING: - from datetime import timedelta - - -class MemoryStore(Store): - """In memory, atomic, asynchronous key/value store.""" - - __slots__ = ("_store", "_lock") - - def __init__(self) -> None: - """Initialize :class:`MemoryStore`""" - self._store: dict[str, StorageObject] = {} - self._lock = Lock() - - async def set(self, key: str, value: str | bytes, expires_in: int | timedelta | None = None) -> None: - """Set a value. - - Args: - key: Key to associate the value with - value: Value to store - expires_in: Time in seconds before the key is considered expired - - Returns: - ``None`` - """ - if isinstance(value, str): - value = value.encode("utf-8") - async with self._lock: - self._store[key] = StorageObject.new(data=value, expires_in=expires_in) - - async def get(self, key: str, renew_for: int | timedelta | None = None) -> bytes | None: - """Get a value. - - Args: - key: Key associated with the value - renew_for: If given and the value had an initial expiry time set, renew the - expiry time for ``renew_for`` seconds. If the value has not been set - with an expiry time this is a no-op - - Returns: - The value associated with ``key`` if it exists and is not expired, else - ``None`` - """ - async with self._lock: - storage_obj = self._store.get(key) - - if not storage_obj: - return None - - if storage_obj.expired: - self._store.pop(key) - return None - - if renew_for and storage_obj.expires_at: - # don't use .set() here, so we can hold onto the lock for the whole operation - storage_obj = StorageObject.new(data=storage_obj.data, expires_in=renew_for) - self._store[key] = storage_obj - - return storage_obj.data - - async def delete(self, key: str) -> None: - """Delete a value. - - If no such key exists, this is a no-op. - - Args: - key: Key of the value to delete - """ - async with self._lock: - self._store.pop(key, None) - - async def delete_all(self) -> None: - """Delete all stored values.""" - async with self._lock: - self._store.clear() - - async def delete_expired(self) -> None: - """Delete expired items. - - Since expired items are normally only cleared on access (i.e. when calling - :meth:`.get`), this method should be called in regular intervals - to free memory. - """ - async with self._lock: - new_store = {} - for i, (key, storage_obj) in enumerate(self._store.items()): - if not storage_obj.expired: - new_store[key] = storage_obj - if i % 1000 == 0: - await anyio.sleep(0) - self._store = new_store - - async def exists(self, key: str) -> bool: - """Check if a given ``key`` exists.""" - return key in self._store - - async def expires_in(self, key: str) -> int | None: - """Get the time in seconds ``key`` expires in. If no such ``key`` exists or no - expiry time was set, return ``None``. - """ - if storage_obj := self._store.get(key): - return storage_obj.expires_in - return None diff --git a/venv/lib/python3.11/site-packages/litestar/stores/redis.py b/venv/lib/python3.11/site-packages/litestar/stores/redis.py deleted file mode 100644 index 6697962..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/redis.py +++ /dev/null @@ -1,204 +0,0 @@ -from __future__ import annotations - -from datetime import timedelta -from typing import TYPE_CHECKING, cast - -from redis.asyncio import Redis -from redis.asyncio.connection import ConnectionPool - -from litestar.exceptions import ImproperlyConfiguredException -from litestar.types import Empty, EmptyType -from litestar.utils.empty import value_or_default - -from .base import NamespacedStore - -__all__ = ("RedisStore",) - -if TYPE_CHECKING: - from types import TracebackType - - -class RedisStore(NamespacedStore): - """Redis based, thread and process safe asynchronous key/value store.""" - - __slots__ = ("_redis",) - - def __init__( - self, redis: Redis, namespace: str | None | EmptyType = Empty, handle_client_shutdown: bool = False - ) -> None: - """Initialize :class:`RedisStore` - - Args: - redis: An :class:`redis.asyncio.Redis` instance - namespace: A key prefix to simulate a namespace in redis. If not given, - defaults to ``LITESTAR``. Namespacing can be explicitly disabled by passing - ``None``. This will make :meth:`.delete_all` unavailable. - handle_client_shutdown: If ``True``, handle the shutdown of the `redis` instance automatically during the store's lifespan. Should be set to `True` unless the shutdown is handled externally - """ - self._redis = redis - self.namespace: str | None = value_or_default(namespace, "LITESTAR") - self.handle_client_shutdown = handle_client_shutdown - - # script to get and renew a key in one atomic step - self._get_and_renew_script = self._redis.register_script( - b""" - local key = KEYS[1] - local renew = tonumber(ARGV[1]) - - local data = redis.call('GET', key) - local ttl = redis.call('TTL', key) - - if ttl > 0 then - redis.call('EXPIRE', key, renew) - end - - return data - """ - ) - - # script to delete all keys in the namespace - self._delete_all_script = self._redis.register_script( - b""" - local cursor = 0 - - repeat - local result = redis.call('SCAN', cursor, 'MATCH', ARGV[1]) - for _,key in ipairs(result[2]) do - redis.call('UNLINK', key) - end - cursor = tonumber(result[1]) - until cursor == 0 - """ - ) - - async def _shutdown(self) -> None: - if self.handle_client_shutdown: - await self._redis.aclose(close_connection_pool=True) # type: ignore[attr-defined] - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self._shutdown() - - @classmethod - def with_client( - cls, - url: str = "redis://localhost:6379", - *, - db: int | None = None, - port: int | None = None, - username: str | None = None, - password: str | None = None, - namespace: str | None | EmptyType = Empty, - ) -> RedisStore: - """Initialize a :class:`RedisStore` instance with a new class:`redis.asyncio.Redis` instance. - - Args: - url: Redis URL to connect to - db: Redis database to use - port: Redis port to use - username: Redis username to use - password: Redis password to use - namespace: Virtual key namespace to use - """ - pool = ConnectionPool.from_url( - url=url, - db=db, - decode_responses=False, - port=port, - username=username, - password=password, - ) - return cls( - redis=Redis(connection_pool=pool), - namespace=namespace, - handle_client_shutdown=True, - ) - - def with_namespace(self, namespace: str) -> RedisStore: - """Return a new :class:`RedisStore` with a nested virtual key namespace. - The current instances namespace will serve as a prefix for the namespace, so it - can be considered the parent namespace. - """ - return type(self)( - redis=self._redis, - namespace=f"{self.namespace}_{namespace}" if self.namespace else namespace, - handle_client_shutdown=self.handle_client_shutdown, - ) - - def _make_key(self, key: str) -> str: - prefix = f"{self.namespace}:" if self.namespace else "" - return prefix + key - - async def set(self, key: str, value: str | bytes, expires_in: int | timedelta | None = None) -> None: - """Set a value. - - Args: - key: Key to associate the value with - value: Value to store - expires_in: Time in seconds before the key is considered expired - - Returns: - ``None`` - """ - if isinstance(value, str): - value = value.encode("utf-8") - await self._redis.set(self._make_key(key), value, ex=expires_in) - - async def get(self, key: str, renew_for: int | timedelta | None = None) -> bytes | None: - """Get a value. - - Args: - key: Key associated with the value - renew_for: If given and the value had an initial expiry time set, renew the - expiry time for ``renew_for`` seconds. If the value has not been set - with an expiry time this is a no-op. Atomicity of this step is guaranteed - by using a lua script to execute fetch and renewal. If ``renew_for`` is - not given, the script will be bypassed so no overhead will occur - - Returns: - The value associated with ``key`` if it exists and is not expired, else - ``None`` - """ - key = self._make_key(key) - if renew_for: - if isinstance(renew_for, timedelta): - renew_for = renew_for.seconds - data = await self._get_and_renew_script(keys=[key], args=[renew_for]) - return cast("bytes | None", data) - return await self._redis.get(key) - - async def delete(self, key: str) -> None: - """Delete a value. - - If no such key exists, this is a no-op. - - Args: - key: Key of the value to delete - """ - await self._redis.delete(self._make_key(key)) - - async def delete_all(self) -> None: - """Delete all stored values in the virtual key namespace. - - Raises: - ImproperlyConfiguredException: If no namespace was configured - """ - if not self.namespace: - raise ImproperlyConfiguredException("Cannot perform delete operation: No namespace configured") - - await self._delete_all_script(keys=[], args=[f"{self.namespace}*:*"]) - - async def exists(self, key: str) -> bool: - """Check if a given ``key`` exists.""" - return await self._redis.exists(self._make_key(key)) == 1 - - async def expires_in(self, key: str) -> int | None: - """Get the time in seconds ``key`` expires in. If no such ``key`` exists or no - expiry time was set, return ``None``. - """ - ttl = await self._redis.ttl(self._make_key(key)) - return None if ttl == -2 else ttl diff --git a/venv/lib/python3.11/site-packages/litestar/stores/registry.py b/venv/lib/python3.11/site-packages/litestar/stores/registry.py deleted file mode 100644 index 11a08c2..0000000 --- a/venv/lib/python3.11/site-packages/litestar/stores/registry.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable - -if TYPE_CHECKING: - from .base import Store - - -from .memory import MemoryStore - -__all__ = ("StoreRegistry",) - - -def default_default_factory(name: str) -> Store: - return MemoryStore() - - -class StoreRegistry: - """Registry for :class:`Store <.base.Store>` instances.""" - - __slots__ = ("_stores", "_default_factory") - - def __init__( - self, stores: dict[str, Store] | None = None, default_factory: Callable[[str], Store] = default_default_factory - ) -> None: - """Initialize ``StoreRegistry``. - - Args: - stores: A dictionary mapping store names to stores, used to initialize the registry - default_factory: A callable used by :meth:`StoreRegistry.get` to provide a store, if the requested name hasn't - been registered yet. This callable receives the requested name and should return a - :class:`Store <.base.Store>` instance. - """ - self._stores = stores or {} - self._default_factory = default_factory - - def register(self, name: str, store: Store, allow_override: bool = False) -> None: - """Register a new :class:`Store <.base.Store>`. - - Args: - name: Name to register the store under - store: The store to register - allow_override: Whether to allow overriding an existing store of the same name - - Raises: - ValueError: If a store is already registered under this name and ``override`` is not ``True`` - """ - if not allow_override and name in self._stores: - raise ValueError(f"Store with the name {name!r} already exists") - self._stores[name] = store - - def get(self, name: str) -> Store: - """Get a store registered under ``name``. If no such store is registered, create a store using the default - factory with ``name`` and register the returned store under ``name``. - - Args: - name: Name of the store - - Returns: - A :class:`Store <.base.Store>` - """ - if not self._stores.get(name): - self._stores[name] = self._default_factory(name) - return self._stores[name] |