summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/litestar/stores
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/litestar/stores')
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__init__.py0
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__pycache__/__init__.cpython-311.pycbin199 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__pycache__/base.cpython-311.pycbin7661 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__pycache__/file.cpython-311.pycbin9799 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__pycache__/memory.cpython-311.pycbin7029 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__pycache__/redis.cpython-311.pycbin10088 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/__pycache__/registry.cpython-311.pycbin3347 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/base.py145
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/file.py170
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/memory.py115
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/redis.py204
-rw-r--r--venv/lib/python3.11/site-packages/litestar/stores/registry.py64
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
deleted file mode 100644
index 78604d0..0000000
--- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/__init__.cpython-311.pyc
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 21d2fb4..0000000
--- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/base.cpython-311.pyc
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index b39333c..0000000
--- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/file.cpython-311.pyc
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index c8e3b05..0000000
--- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/memory.cpython-311.pyc
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 21e7e2f..0000000
--- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/redis.cpython-311.pyc
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index c87c31a..0000000
--- a/venv/lib/python3.11/site-packages/litestar/stores/__pycache__/registry.cpython-311.pyc
+++ /dev/null
Binary files differ
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]