Store
persista.store ¶
Contain stores.
persista.store.OnConflict
module-attribute
¶
OnConflict = Literal['raise', 'skip', 'overwrite', 'merge']
Strategy for handling keys that already exist in the store.
Used by :meth:BaseStore.set, :meth:BaseStore.set_many, and
:meth:BaseStore.set_batches to control what happens when a key being
written already has a value in the store.
persista.store.AsyncBasePostgresStore ¶
Bases: AsyncBaseStore, MultilineDisplayMixin
Define a base class for Postgres-backed asynchronous key-value stores.
Mirrors :class:~persista.store.postgres.BasePostgresStore, but
runs every query through :class:psycopg.AsyncConnection instead
of the blocking :class:psycopg.Connection, so it can be awaited
from an async application without stalling the event loop on
network I/O.
A single table (named by the table argument, "store" by
default) backs every value; the primary key column is named by
:attr:_key_column. :meth:get, :meth:get_many, :meth:filter,
and :meth:iter_batches all query the full row and hand it to
:meth:_row_to_value to turn it back into a value dict, which is
what lets subclasses differ in how a value is laid out across
columns (a single JSONB column vs. typed columns plus a JSONB
overflow column) without duplicating any of the surrounding query
logic.
Subclasses only need to implement :meth:_create_table_sql,
:meth:_row_to_value, :meth:_build_filter_condition, and
:meth:_set_many (see :class:AsyncPostgresStore for a
JSONB-only layout and :class:AsyncTypedPostgresStore for an
optionally typed one).
The table schema is created lazily, on the first query, rather
than in the constructor -- __init__ cannot await, so
there's no synchronous point at which the schema could be
created eagerly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conninfo
|
str
|
The connection string/DSN passed to
|
required |
table
|
str
|
The name of the table backing this store. Must be a valid SQL identifier (letters, digits, underscores, not starting with a digit). |
'store'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.AsyncBaseRedisStore ¶
Bases: AsyncBaseStore, MultilineDisplayMixin
Define a base class for asynchronous Redis-backed key-value stores.
Mirrors :class:~persista.store.redis.BaseRedisStore, but runs
every command through :mod:redis.asyncio instead of the blocking
:mod:redis client, so it can be awaited from an async
application without stalling the event loop on network I/O. A
Redis set at __keys__ tracks the keys currently in the store,
which allows :meth:count, :meth:keys, and
:meth:contains_many to avoid scanning the whole keyspace.
Unlike the SQL-backed stores, Redis has no query language for
matching on the content of a value, so :meth:filter is
implemented client-side by scanning every value in the store.
Subclasses only need to implement :meth:_encode and
:meth:_decode, which control how a value is serialized to and
from what is stored in Redis (see :class:AsyncRedisStore for a
JSON encoding and :class:AsyncPickleRedisStore for a pickle
encoding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The Redis connection URL passed to
|
'redis://localhost:6379/0'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.AsyncBaseSQLiteStore ¶
Bases: AsyncBaseStore, MultilineDisplayMixin
Define a base class for SQLite-backed asynchronous key-value stores.
Mirrors :class:~persista.store.sqlite.BaseSQLiteStore, but runs
every query through :mod:aiosqlite instead of the blocking
:mod:sqlite3 driver, so it can be awaited from an async
application without stalling the event loop on disk I/O.
A single store table backs every value; the primary key
column is named by :attr:_key_column. :meth:get,
:meth:get_many, :meth:filter, and :meth:iter_batches all
query the full row and hand it to :meth:_row_to_value to turn
it back into a value dict, which is what lets subclasses differ
in how a value is laid out across columns without duplicating any
of the surrounding query logic.
Subclasses only need to implement :meth:_create_table_sql,
:meth:_row_to_value, :meth:_build_filter_condition, and
:meth:_set_many (see :class:AsyncSQLiteStore for a JSON-only
layout).
The table schema is created lazily, on the first query, rather
than in the constructor -- __init__ cannot await, and
:func:aiosqlite.connect itself connects lazily on first use, so
there's no synchronous point at which the schema could be
created eagerly.
The constructor mirrors :func:aiosqlite.connect: the first
positional argument is the database argument (a path,
":memory:", or a file: URI when uri=True is passed),
and any additional keyword arguments are forwarded as-is. Use
:meth:from_path for a more convenient constructor that builds
the appropriate URI for you, including read-only access.
Requires the optional aiosqlite dependency
(pip install aiosqlite).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database
|
Path | str
|
The |
required |
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.AsyncBaseSQLiteStore.from_path
classmethod
¶
from_path(
path: Path | str,
*,
read_only: bool = False,
**kwargs: Any
) -> Self
Construct a store from a file path.
Builds the appropriate file: URI for aiosqlite.connect,
including read-only access, so callers don't need to
construct SQLite URIs themselves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path | str
|
Path to the SQLite file, or |
required |
read_only
|
bool
|
If |
False
|
**kwargs
|
Any
|
Additional keyword arguments to pass to the
constructor (and, from there, to |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
A new store connected to |
persista.store.AsyncBaseSQLiteStore.get_columns_info
async
¶
get_columns_info() -> dict[str, str]
Return the column names and types of the store's table.
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
A mapping of column name to SQLite declared type. |
persista.store.AsyncBaseSQLiteStore.show_columns_info
async
¶
show_columns_info() -> None
Print the store's table column names and types to stdout.
This is a convenience wrapper around :meth:get_columns_info
for interactive/debugging use. For programmatic access, use
:meth:get_columns_info instead.
persista.store.AsyncBaseStore ¶
Bases: ABC
Abstract base class for asynchronous key-value stores.
Mirrors :class:BaseStore, but every method that touches the
underlying store is a coroutine (or an async generator), for
implementations backed by an async driver (e.g. an async DB client).
Implementations are expected to support use as an async context
manager (async with SomeStore(...) as store: ...), which calls
:meth:close automatically on exit.
persista.store.AsyncBaseStore.closed
abstractmethod
property
¶
closed: bool
Indicate whether the store is closed.
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
open and ready to use. |
persista.store.AsyncBaseStore.clear
abstractmethod
async
¶
clear() -> None
Remove every key-value pair from the store.
This is equivalent to resetting the store to empty, without closing it.
persista.store.AsyncBaseStore.close
abstractmethod
async
¶
close() -> None
Close the store and release any underlying resources (e.g. database connections, file handles).
Implementations should make repeated calls to close() safe
(i.e. idempotent), since :meth:__aexit__ calls it
unconditionally and callers may also close a store manually
before using it as a context manager.
persista.store.AsyncBaseStore.contains_many
abstractmethod
async
¶
contains_many(
keys: list[str],
) -> tuple[list[str], list[str]]
Check which keys exist in the store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
keys
|
list[str]
|
The keys to check. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
A tuple of two lists: |
list[str]
|
contains the keys that exist in the store and |
tuple[list[str], list[str]]
|
contains the keys that do not. |
persista.store.AsyncBaseStore.count
abstractmethod
async
¶
count() -> int
Return the total number of key-value pairs in the store.
Returns:
| Type | Description |
|---|---|
int
|
The number of key-value pairs currently stored. |
persista.store.AsyncBaseStore.delete
abstractmethod
async
¶
delete(key: str) -> None
Delete a value by its key.
Keys that do not exist should be silently ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key of the value to delete. |
required |
persista.store.AsyncBaseStore.delete_many
abstractmethod
async
¶
delete_many(keys: list[str]) -> None
Delete multiple values by their keys.
Keys that do not exist should be silently ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
keys
|
list[str]
|
The keys of the values to delete. |
required |
persista.store.AsyncBaseStore.filter
abstractmethod
async
¶
filter(**field_filters: Any) -> list[dict[str, Any]]
Retrieve values whose content matches all provided field filters.
All filters should be combined with AND. Each keyword
argument matches the corresponding key in the stored value
exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**field_filters
|
Any
|
Key-value pairs where each key is a field name within a stored value and the value is the exact value to match. Calling with no arguments should return every value in the store. |
{}
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
A list of matching values. |
persista.store.AsyncBaseStore.get
abstractmethod
async
¶
get(key: str) -> dict[str, Any] | None
Retrieve a single value by its key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key to look up. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
The value associated with |
dict[str, Any] | None
|
key is not found. |
persista.store.AsyncBaseStore.get_many
abstractmethod
async
¶
get_many(keys: list[str]) -> list[dict[str, Any] | None]
Retrieve multiple values by their keys.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
keys
|
list[str]
|
The keys to look up. |
required |
Returns:
| Type | Description |
|---|---|
list[dict[str, Any] | None]
|
A list the same length as |
list[dict[str, Any] | None]
|
corresponding value for each key that exists, or |
list[dict[str, Any] | None]
|
|
persista.store.AsyncBaseStore.iter_batches
abstractmethod
¶
iter_batches(
batch_size: int = 32,
) -> AsyncGenerator[dict[str, dict[str, Any]], None]
Yield key-value pairs in batches, avoiding loading the whole store into memory at once.
This is the scalable equivalent of :meth:values: instead of
materializing every value as a single mapping, it streams
them from the underlying store in chunks of batch_size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
The maximum number of pairs to yield per batch. Must be a positive integer. |
32
|
Yields:
| Type | Description |
|---|---|
AsyncGenerator[dict[str, dict[str, Any]], None]
|
Dicts mapping key to value, each with at most |
AsyncGenerator[dict[str, dict[str, Any]], None]
|
|
AsyncGenerator[dict[str, dict[str, Any]], None]
|
meth: |
AsyncGenerator[dict[str, dict[str, Any]], None]
|
|
persista.store.AsyncBaseStore.keys
abstractmethod
¶
keys() -> AsyncIterator[str]
Iterate over all keys in the store.
Yields:
| Type | Description |
|---|---|
AsyncIterator[str]
|
Every key currently in the store. |
persista.store.AsyncBaseStore.set
abstractmethod
async
¶
set(
key: str,
value: dict[str, Any],
on_conflict: OnConflict = "overwrite",
) -> None
Add a single key-value pair to the store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key to set. |
required |
value
|
dict[str, Any]
|
The value to associate with |
required |
on_conflict
|
OnConflict
|
The strategy to use if
|
'overwrite'
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
persista.store.AsyncBaseStore.set_batches
async
¶
set_batches(
items: Iterable[tuple[str, dict[str, Any]]],
batch_size: int = 32,
on_conflict: OnConflict = "overwrite",
) -> None
Add key-value pairs from an iterable, writing them to the store in mini-batches.
This is the streaming equivalent of :meth:set_many: instead
of requiring every key-value pair to be materialized into a
single mapping upfront, it consumes items lazily and
writes at most batch_size pairs at a time. This keeps
memory usage bounded when items comes from a generator
over a large or unbounded source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
Iterable[tuple[str, dict[str, Any]]]
|
An iterable of |
required |
batch_size
|
int
|
The maximum number of pairs to write to the
store per underlying :meth: |
32
|
on_conflict
|
OnConflict
|
The strategy to use for keys that already
exist in the store. See :meth: |
'overwrite'
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
persista.store.AsyncBaseStore.set_many
abstractmethod
async
¶
set_many(
items: Mapping[str, dict[str, Any]],
on_conflict: OnConflict = "overwrite",
) -> None
Add multiple key-value pairs to the store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
Mapping[str, dict[str, Any]]
|
The values to add, keyed by their unique key. |
required |
on_conflict
|
OnConflict
|
The strategy to use for keys in |
'overwrite'
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
persista.store.AsyncBaseStore.values
async
¶
values(
batch_size: int = 32,
) -> AsyncIterator[dict[str, Any]]
Iterate over all values without loading them all into memory at once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
The batch size used internally when pulling values from the underlying store. Does not affect the granularity of what is yielded — values are always yielded one at a time. |
32
|
Yields:
| Type | Description |
|---|---|
AsyncIterator[dict[str, Any]]
|
One value at a time, in the same order as |
AsyncIterator[dict[str, Any]]
|
meth: |
persista.store.AsyncInMemoryStore ¶
Bases: AsyncBaseStore, InlineDisplayMixin
An :class:~persista.store.AsyncBaseStore implementation backed
by a plain dict.
Values are held entirely in process memory -- nothing is persisted to disk. This is primarily useful for testing, small-scale exploration, or async pipelines that don't need durability.
Values are deep-copied on both write and read so that mutating a
value returned by this store (or a value passed into :meth:set
/ :meth:set_many) never affects the store's internal state.
This trades some performance for isolation; for very large values
or hot loops, consider a store that doesn't copy on every access.
Example
>>> import asyncio
>>> from persista.store import AsyncInMemoryStore
>>> async def main():
... store = AsyncInMemoryStore()
... await store.set("1", {"text": "hello"})
... print(await store.count())
... print(await store.get("1"))
...
>>> asyncio.run(main())
1
{'text': 'hello'}
persista.store.AsyncPickleRedisStore ¶
Bases: AsyncBaseRedisStore
An asynchronous Redis-backed key-value store that serializes
values with pickle instead of JSON.
Unlike :class:AsyncRedisStore, this store can persist arbitrary
Python objects within a value's fields (tuples, sets, custom
classes, etc.), not just JSON-compatible types. The tradeoff is
that values are opaque binary blobs from outside Python (not
human-readable, not inspectable from non-Python Redis clients),
and, since :func:pickle.loads can execute arbitrary code, this
store must never be pointed at a Redis instance that isn't fully
trusted. Mirrors :class:~persista.store.redis.PickleRedisStore,
but every method is a coroutine, backed by :mod:redis.asyncio
instead of the blocking :mod:redis client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The Redis connection URL passed to
|
'redis://localhost:6379/0'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> import asyncio
>>> from persista.store import AsyncPickleRedisStore
>>> async def main():
... store = AsyncPickleRedisStore("redis://localhost:6379/0")
... await store.set("1", {"title": "Intro to Python", "tags": {"python", "intro"}})
... print(await store.get("1"))
... await store.close()
...
>>> asyncio.run(main()) # doctest: +SKIP
{'title': 'Intro to Python', 'tags': {'python', 'intro'}}
persista.store.AsyncPostgresStore ¶
Bases: AsyncBasePostgresStore
An asynchronous Postgres-backed key-value store.
Persists values to a Postgres database and supports adding,
retrieving, filtering, and deleting key-value pairs. Each value is
stored as a JSONB column, which provides flexibility for arbitrary
value fields without requiring a fixed schema. Mirrors
:class:~persista.store.postgres.PostgresStore, but every method
is a coroutine, backed by :class:psycopg.AsyncConnection instead
of the blocking :class:psycopg.Connection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conninfo
|
str
|
The connection string/DSN passed to
|
required |
table
|
str
|
The name of the table backing this store. |
'store'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> import asyncio
>>> from persista.store import AsyncPostgresStore
>>> async def main():
... store = AsyncPostgresStore("postgresql://user:pass@localhost/dbname")
... await store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice"},
... "2": {"title": "Advanced Python", "author": "Alice"},
... }
... )
... result = await store.filter(author="Alice")
... print(len(result))
... await store.close()
...
>>> asyncio.run(main()) # doctest: +SKIP
2
persista.store.AsyncRedisStore ¶
Bases: AsyncBaseRedisStore
An asynchronous Redis-backed key-value store.
Persists values to Redis and supports adding, retrieving,
filtering, and deleting key-value pairs. Each value is stored as
a JSON string, which provides flexibility for arbitrary value
fields without requiring a fixed schema, is human-readable
directly from Redis, and can be read by any Redis client
regardless of language. This means only JSON-compatible value
fields (str, int, float, bool, None, list, dict) are supported;
use :class:AsyncPickleRedisStore if you need to persist
arbitrary Python objects. Mirrors
:class:~persista.store.redis.RedisStore, but every method is a
coroutine, backed by :mod:redis.asyncio instead of the blocking
:mod:redis client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The Redis connection URL passed to
|
'redis://localhost:6379/0'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> import asyncio
>>> from persista.store import AsyncRedisStore
>>> async def main():
... store = AsyncRedisStore("redis://localhost:6379/0")
... await store.set_many(
... {
... "1": {
... "title": "Intro to Python",
... "author": "Alice",
... "category": "Programming",
... },
... "2": {
... "title": "Advanced Python",
... "author": "Alice",
... "category": "Programming",
... },
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... )
... result = await store.filter(author="Alice")
... print(len(result))
... await store.close()
...
>>> asyncio.run(main()) # doctest: +SKIP
2
persista.store.AsyncSQLiteStore ¶
Bases: AsyncBaseSQLiteStore
An asynchronous SQLite-backed key-value store.
Persists values to a SQLite database and supports adding,
retrieving, filtering, and deleting key-value pairs. Each value
is stored as a JSON column (using SQLite's built-in json1
functions), which provides flexibility for arbitrary value
fields without requiring a fixed schema. Mirrors
:class:~persista.store.sqlite.SQLiteStore, but every method is
a coroutine, backed by :mod:aiosqlite instead of the blocking
:mod:sqlite3 driver.
The constructor mirrors :func:aiosqlite.connect directly. For
the common case of opening a file by path (optionally read-only),
use :meth:~AsyncBaseSQLiteStore.from_path instead.
Requires the optional aiosqlite dependency
(pip install aiosqlite).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database
|
Path | str
|
The |
':memory:'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> import asyncio
>>> from persista.store import AsyncSQLiteStore
>>> async def main():
... store = AsyncSQLiteStore(":memory:")
... await store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice"},
... "2": {"title": "Advanced Python", "author": "Alice"},
... "3": {"title": "History of Rome", "author": "Bob"},
... }
... )
... result = await store.filter(author="Alice")
... print(len(result))
... await store.close()
...
>>> asyncio.run(main())
2
persista.store.AsyncTypedPostgresStore ¶
Bases: AsyncBasePostgresStore
An asynchronous Postgres-backed key-value store with an optional typed value schema.
Persists values to a Postgres database and supports adding,
retrieving, and filtering by value fields. An optional
value_schema maps known value field names to Postgres types.
Known fields are stored as typed columns for fast, index-friendly
queries. Any value fields not in the schema are stored in an
extra JSONB overflow column, so nothing is lost. Mirrors
:class:~persista.store.postgres.TypedPostgresStore, but every
method is a coroutine, backed by :class:psycopg.AsyncConnection
instead of the blocking :class:psycopg.Connection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conninfo
|
str
|
The connection string/DSN passed to
|
required |
table
|
str
|
The name of the table backing this store. |
'store'
|
value_schema
|
dict[str, str] | None
|
Optional mapping of value field names to
Postgres type strings (e.g. |
None
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> import asyncio
>>> from persista.store import AsyncTypedPostgresStore
>>> async def main():
... schema = {"author": "TEXT", "year": "INTEGER"}
... store = AsyncTypedPostgresStore(
... "postgresql://user:pass@localhost/dbname", value_schema=schema
... )
... await store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "year": 2022},
... "2": {"title": "History of Rome", "author": "Bob", "year": 2021},
... }
... )
... result = await store.filter(author="Alice")
... print(len(result))
... await store.close()
...
>>> asyncio.run(main()) # doctest: +SKIP
1
persista.store.AsyncTypedSQLiteStore ¶
Bases: AsyncBaseSQLiteStore
An asynchronous SQLite-backed key-value store with an optional typed value schema.
Persists values to a SQLite database and supports adding,
retrieving, and filtering by value fields. An optional
value_schema maps known value field names to SQLite types.
Known fields are stored as typed columns for fast, index-friendly
queries. Any value fields not in the schema are stored in an
extra JSON overflow column, so nothing is lost. Mirrors
:class:~persista.store.sqlite.TypedSQLiteStore, but every
method is a coroutine, backed by :mod:aiosqlite instead of the
blocking :mod:sqlite3 driver.
The constructor mirrors :func:aiosqlite.connect directly (plus
the value_schema argument). For the common case of opening a
file by path (optionally read-only), use
:meth:~AsyncBaseSQLiteStore.from_path instead.
Requires the optional aiosqlite dependency
(pip install aiosqlite).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database
|
Path | str
|
The |
':memory:'
|
value_schema
|
dict[str, str] | None
|
Optional mapping of value field names to SQLite
type strings (e.g. |
None
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> import asyncio
>>> from persista.store import AsyncTypedSQLiteStore
>>> async def main():
... schema = {"author": "TEXT", "year": "INTEGER", "category": "TEXT"}
... store = AsyncTypedSQLiteStore(":memory:", value_schema=schema)
... await store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice"},
... "2": {"title": "Advanced Python", "author": "Alice"},
... "3": {"title": "History of Rome", "author": "Bob"},
... }
... )
... result = await store.filter(author="Alice")
... print(len(result))
... await store.close()
...
>>> asyncio.run(main())
2
persista.store.BaseDuckDBStore ¶
Bases: BaseStore, MultilineDisplayMixin
Define a base class for DuckDB-backed key-value stores.
Subclasses only need to implement :meth:_select_columns,
:meth:_row_to_kv, :meth:_set_many, and :meth:filter, which
control how a value is laid out across the table's columns (see
:class:DuckDBStore for a single JSON column layout and
:class:~persista.store.duckdb_typed.TypedDuckDBStore for a typed
column layout with a JSON overflow column).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path | str
|
Path to the DuckDB file, or |
required |
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.BaseDuckDBStore.get_columns_info ¶
get_columns_info() -> dict[str, str]
Return the column names and types of the store's table.
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
A mapping of column name to DuckDB type name. |
persista.store.BaseDuckDBStore.show_columns_info ¶
show_columns_info() -> None
Print the store's table column names and types to stdout.
This is a convenience wrapper around :meth:get_columns_info
for interactive/debugging use. For programmatic access, use
:meth:get_columns_info instead.
persista.store.BaseLmdbStore ¶
Bases: BaseStore, MultilineDisplayMixin
Define a base class for LMDB-backed key-value stores.
LMDB is an embedded, memory-mapped key-value store backed by a
single-directory environment on disk, so unlike Redis this store
needs no separate server process and no explicit tracking of keys:
the environment's own B+tree already provides ordered iteration,
membership checks, and a cheap entry count. :meth:filter is
still implemented client-side by scanning every value in the
store, since LMDB has no query language for matching on the
content of a value.
Subclasses only need to implement :meth:_encode and
:meth:_decode, which control how a value is serialized to and
from what is stored in LMDB (see :class:LmdbStore for a JSON
encoding and :class:PickleLmdbStore for a pickle encoding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
The directory where the LMDB environment is stored. Created automatically if it does not already exist. |
required |
map_size
|
int
|
The maximum size in bytes of the memory map, i.e.
the upper bound on the total size of the environment
(keys and values combined). Passed to |
_DEFAULT_MAP_SIZE
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.BasePostgresStore ¶
Bases: BaseStore, MultilineDisplayMixin
Define a base class for Postgres-backed key-value stores.
A single table (named by the table argument, "store" by
default) backs every value; the primary key column is named by
:attr:_key_column. :meth:get, :meth:get_many, :meth:filter,
and :meth:iter_batches all query the full row and hand it to
:meth:_row_to_value to turn it back into a value dict, which is
what lets subclasses differ in how a value is laid out across
columns (a single JSONB column vs. typed columns plus a JSONB
overflow column) without duplicating any of the surrounding query
logic. This mirrors :class:~persista.store.sqlite.BaseSQLiteStore.
Subclasses only need to implement :meth:_create_table_sql,
:meth:_row_to_value, :meth:_build_filter_condition, and
:meth:_set_many (see :class:PostgresStore for a JSONB-only
layout and :class:~persista.store.postgres.TypedPostgresStore
for an optionally typed one).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conninfo
|
str
|
The connection string/DSN passed to
|
required |
table
|
str
|
The name of the table backing this store. Must be a valid SQL identifier (letters, digits, underscores, not starting with a digit). |
'store'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.BaseRedisStore ¶
Bases: BaseStore, MultilineDisplayMixin
Define a base class for Redis-backed key-value stores.
A Redis set at __keys__ tracks the keys currently in the
store, which allows :meth:count, :meth:keys, and
:meth:contains_many to avoid scanning the whole keyspace.
Unlike the SQL-backed stores, Redis has no query language for
matching on the content of a value, so :meth:filter is
implemented client-side by scanning every value in the store.
Subclasses only need to implement :meth:_encode and
:meth:_decode, which control how a value is serialized to and
from what is stored in Redis (see :class:RedisStore for a JSON
encoding and :class:~persista.store.redis_pickle.PickleRedisStore
for a pickle encoding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The Redis connection URL passed to
|
'redis://localhost:6379/0'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.BaseSQLiteStore ¶
Bases: BaseStore, MultilineDisplayMixin
Define a base class for SQLite-backed key-value stores.
A single store table backs every value; the primary key
column is named by :attr:_key_column. :meth:get,
:meth:get_many, :meth:filter, and :meth:iter_batches all
query the full row and hand it to :meth:_row_to_value to turn
it back into a value dict, which is what lets subclasses differ
in how a value is laid out across columns (a single JSON column
vs. typed columns plus a JSON overflow column) without
duplicating any of the surrounding query logic.
Subclasses only need to implement :meth:_create_table_sql,
:meth:_row_to_value, :meth:_build_filter_condition, and
:meth:_set_many (see :class:SQLiteStore for a JSON-only
layout and :class:~persista.store.sqlite_typed.TypedSQLiteStore
for an optionally typed one).
The constructor mirrors :func:sqlite3.connect: the first
positional argument is the database argument accepted by
sqlite3.connect (a path, ":memory:", or a file: URI
when uri=True is passed), and any additional keyword
arguments are forwarded as-is. Use :meth:from_path for a more
convenient constructor that builds the appropriate URI for you,
including read-only access.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database
|
Path | str
|
The |
required |
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
persista.store.BaseSQLiteStore.from_path
classmethod
¶
from_path(
path: Path | str,
*,
read_only: bool = False,
**kwargs: Any
) -> Self
Construct a store from a file path.
Builds the appropriate file: URI for sqlite3.connect,
including read-only access, so callers don't need to
construct SQLite URIs themselves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path | str
|
Path to the SQLite file, or |
required |
read_only
|
bool
|
If |
False
|
**kwargs
|
Any
|
Additional keyword arguments to pass to the
constructor (and, from there, to |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
A new store connected to |
persista.store.BaseSQLiteStore.get_columns_info ¶
get_columns_info() -> dict[str, str]
Return the column names and types of the store's table.
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
A mapping of column name to SQLite declared type. |
persista.store.BaseSQLiteStore.show_columns_info ¶
show_columns_info() -> None
Print the store's table column names and types to stdout.
This is a convenience wrapper around :meth:get_columns_info
for interactive/debugging use. For programmatic access, use
:meth:get_columns_info instead.
persista.store.BaseStore ¶
Bases: ABC
Abstract base class for key-value stores.
Defines the common interface that all key-value store
implementations must provide. Values are stored as dicts, which
allows :meth:filter to match on the content of a value.
To implement a custom store, subclass :class:BaseStore and
implement all abstract methods.
Implementations are expected to support use as a context manager
(with SomeStore(...) as store: ...), which calls :meth:close
automatically on exit.
persista.store.BaseStore.closed
abstractmethod
property
¶
closed: bool
Indicate whether the store is closed.
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
open and ready to use. |
persista.store.BaseStore.clear
abstractmethod
¶
clear() -> None
Remove every key-value pair from the store.
This is equivalent to resetting the store to empty, without closing it.
persista.store.BaseStore.close
abstractmethod
¶
close() -> None
Close the store and release any underlying resources (e.g. database connections, file handles).
Implementations should make repeated calls to close() safe
(i.e. idempotent), since :meth:__exit__ calls it
unconditionally and callers may also close a store manually
before using it as a context manager.
persista.store.BaseStore.contains_many
abstractmethod
¶
contains_many(
keys: list[str],
) -> tuple[list[str], list[str]]
Check which keys exist in the store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
keys
|
list[str]
|
The keys to check. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
A tuple of two lists: |
list[str]
|
contains the keys that exist in the store and |
tuple[list[str], list[str]]
|
contains the keys that do not. |
persista.store.BaseStore.count
abstractmethod
¶
count() -> int
Return the total number of key-value pairs in the store.
Returns:
| Type | Description |
|---|---|
int
|
The number of key-value pairs currently stored. |
persista.store.BaseStore.delete
abstractmethod
¶
delete(key: str) -> None
Delete a value by its key.
Keys that do not exist should be silently ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key of the value to delete. |
required |
persista.store.BaseStore.delete_many
abstractmethod
¶
delete_many(keys: list[str]) -> None
Delete multiple values by their keys.
Keys that do not exist should be silently ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
keys
|
list[str]
|
The keys of the values to delete. |
required |
persista.store.BaseStore.filter
abstractmethod
¶
filter(**field_filters: Any) -> list[dict[str, Any]]
Retrieve values whose content matches all provided field filters.
All filters should be combined with AND. Each keyword
argument matches the corresponding key in the stored value
exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**field_filters
|
Any
|
Key-value pairs where each key is a field name within a stored value and the value is the exact value to match. Calling with no arguments should return every value in the store. |
{}
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
A list of matching values. |
persista.store.BaseStore.get
abstractmethod
¶
get(key: str) -> dict[str, Any] | None
Retrieve a single value by its key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key to look up. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any] | None
|
The value associated with |
dict[str, Any] | None
|
key is not found. |
persista.store.BaseStore.get_many
abstractmethod
¶
get_many(keys: list[str]) -> list[dict[str, Any] | None]
Retrieve multiple values by their keys.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
keys
|
list[str]
|
The keys to look up. |
required |
Returns:
| Type | Description |
|---|---|
list[dict[str, Any] | None]
|
A list the same length as |
list[dict[str, Any] | None]
|
corresponding value for each key that exists, or |
list[dict[str, Any] | None]
|
|
persista.store.BaseStore.iter_batches
abstractmethod
¶
iter_batches(
batch_size: int = 32,
) -> Generator[dict[str, dict[str, Any]], None, None]
Yield key-value pairs in batches, avoiding loading the whole store into memory at once.
This is the scalable equivalent of :meth:values: instead of
materializing every value as a single mapping, it streams
them from the underlying store in chunks of batch_size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
The maximum number of pairs to yield per batch. Must be a positive integer. |
32
|
Yields:
| Type | Description |
|---|---|
dict[str, dict[str, Any]]
|
Dicts mapping key to value, each with at most |
dict[str, dict[str, Any]]
|
|
dict[str, dict[str, Any]]
|
meth: |
dict[str, dict[str, Any]]
|
|
persista.store.BaseStore.keys
abstractmethod
¶
keys() -> Iterator[str]
Iterate over all keys in the store.
Yields:
| Type | Description |
|---|---|
str
|
Every key currently in the store. |
persista.store.BaseStore.set
abstractmethod
¶
set(
key: str,
value: dict[str, Any],
on_conflict: OnConflict = "overwrite",
) -> None
Add a single key-value pair to the store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The key to set. |
required |
value
|
dict[str, Any]
|
The value to associate with |
required |
on_conflict
|
OnConflict
|
The strategy to use if
|
'overwrite'
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
persista.store.BaseStore.set_batches ¶
set_batches(
items: Iterable[tuple[str, dict[str, Any]]],
batch_size: int = 32,
on_conflict: OnConflict = "overwrite",
) -> None
Add key-value pairs from an iterable, writing them to the store in mini-batches.
This is the streaming equivalent of :meth:set_many: instead
of requiring every key-value pair to be materialized into a
single mapping upfront, it consumes items lazily and
writes at most batch_size pairs at a time. This keeps
memory usage bounded when items comes from a generator
over a large or unbounded source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
Iterable[tuple[str, dict[str, Any]]]
|
An iterable of |
required |
batch_size
|
int
|
The maximum number of pairs to write to the
store per underlying :meth: |
32
|
on_conflict
|
OnConflict
|
The strategy to use for keys that already
exist in the store. See :meth: |
'overwrite'
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
persista.store.BaseStore.set_many
abstractmethod
¶
set_many(
items: Mapping[str, dict[str, Any]],
on_conflict: OnConflict = "overwrite",
) -> None
Add multiple key-value pairs to the store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
Mapping[str, dict[str, Any]]
|
The values to add, keyed by their unique key. |
required |
on_conflict
|
OnConflict
|
The strategy to use for keys in |
'overwrite'
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
persista.store.BaseStore.values ¶
values(batch_size: int = 32) -> Iterator[dict[str, Any]]
Iterate over all values without loading them all into memory at once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
The batch size used internally when pulling values from the underlying store. Does not affect the granularity of what is yielded — values are always yielded one at a time. |
32
|
Yields:
| Type | Description |
|---|---|
dict[str, Any]
|
One value at a time, in the same order as |
dict[str, Any]
|
meth: |
persista.store.DuckDBStore ¶
Bases: BaseDuckDBStore
A DuckDB-backed key-value store.
Persists values to a DuckDB database and supports adding, retrieving, filtering, and deleting key-value pairs. Each value is stored as a JSON column, which provides flexibility for arbitrary value fields without requiring a fixed schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path | str
|
Path to the DuckDB file, or |
':memory:'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import DuckDBStore
>>> store = DuckDBStore(":memory:")
>>> store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "category": "Programming"},
... "2": {"title": "Advanced Python", "author": "Alice", "category": "Programming"},
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... )
>>> len(store.filter(author="Alice"))
2
>>> len(store.filter(author="Alice", category="Programming"))
2
>>> len(store.filter(category="History"))
1
persista.store.InMemoryStore ¶
Bases: BaseStore, InlineDisplayMixin
A :class:~persista.store.BaseStore implementation backed
by a plain dict.
Values are held entirely in process memory -- nothing is persisted to disk. This is primarily useful for testing, small-scale exploration, or pipelines that don't need durability.
Values are deep-copied on both write and read so that mutating a
value returned by this store (or a value passed into :meth:set
/ :meth:set_many) never affects the store's internal state.
This trades some performance for isolation; for very large values
or hot loops, consider a store that doesn't copy on every access.
Example
>>> from persista.store import InMemoryStore
>>> store = InMemoryStore()
>>> store.set("1", {"text": "hello"})
>>> store.count()
1
>>> store.get("1")
{'text': 'hello'}
persista.store.LmdbStore ¶
Bases: BaseLmdbStore
An LMDB-backed key-value store.
Persists values to an LMDB environment and supports adding,
retrieving, filtering, and deleting key-value pairs. Each value
is stored as a JSON-encoded string, which provides flexibility
for arbitrary value fields without requiring a fixed schema and
can be read back by any LMDB client regardless of language. This
means only JSON-compatible value fields (str, int, float, bool,
None, list, dict) are supported; use :class:PickleLmdbStore if
you need to persist arbitrary Python objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
The directory where the LMDB environment is stored. Created automatically if it does not already exist. |
required |
map_size
|
int
|
The maximum size in bytes of the memory map, i.e.
the upper bound on the total size of the environment
(keys and values combined). Passed to |
_DEFAULT_MAP_SIZE
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import LmdbStore
>>> store = LmdbStore("/tmp/lmdb_store") # doctest: +SKIP
>>> store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "category": "Programming"},
... "2": {"title": "Advanced Python", "author": "Alice", "category": "Programming"},
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... ) # doctest: +SKIP
>>> len(store.filter(author="Alice")) # doctest: +SKIP
2
persista.store.PickleLmdbStore ¶
Bases: BaseLmdbStore
An LMDB-backed key-value store that serializes values with
pickle instead of JSON.
Unlike :class:LmdbStore, this store can persist arbitrary
Python objects within a value's fields (tuples, sets, custom
classes, etc.), not just JSON-compatible types. The tradeoff is
that values are opaque binary blobs from outside Python (not
human-readable, not inspectable from non-Python LMDB clients),
and, since :func:pickle.loads can execute arbitrary code, this
store must never be pointed at an LMDB environment that isn't
fully trusted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
The directory where the LMDB environment is stored. Created automatically if it does not already exist. |
required |
map_size
|
int
|
The maximum size in bytes of the memory map, i.e.
the upper bound on the total size of the environment
(keys and values combined). Passed to |
_DEFAULT_MAP_SIZE
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import PickleLmdbStore
>>> store = PickleLmdbStore("/tmp/lmdb_store") # doctest: +SKIP
>>> store.set(
... "1", {"title": "Intro to Python", "tags": {"python", "intro"}}
... ) # doctest: +SKIP
>>> store.get("1") # doctest: +SKIP
{'title': 'Intro to Python', 'tags': {'python', 'intro'}}
persista.store.PickleRedisStore ¶
Bases: BaseRedisStore
A Redis-backed key-value store that serializes values with
pickle instead of JSON.
Unlike :class:~persista.store.RedisStore, this store can persist
arbitrary Python objects within a value's fields (tuples, sets,
custom classes, etc.), not just JSON-compatible types. The
tradeoff is that values are opaque binary blobs from outside
Python (not human-readable, not inspectable from non-Python Redis
clients), and, since :func:pickle.loads can execute arbitrary
code, this store must never be pointed at a Redis instance that
isn't fully trusted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The Redis connection URL passed to
|
'redis://localhost:6379/0'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import PickleRedisStore
>>> store = PickleRedisStore("redis://localhost:6379/0") # doctest: +SKIP
>>> store.set(
... "1", {"title": "Intro to Python", "tags": {"python", "intro"}}
... ) # doctest: +SKIP
>>> store.get("1") # doctest: +SKIP
{'title': 'Intro to Python', 'tags': {'python', 'intro'}}
persista.store.PostgresStore ¶
Bases: BasePostgresStore
A Postgres-backed key-value store.
Persists values to a Postgres database and supports adding, retrieving, filtering, and deleting key-value pairs. Each value is stored as a JSONB column, which provides flexibility for arbitrary value fields without requiring a fixed schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conninfo
|
str
|
The connection string/DSN passed to
|
required |
table
|
str
|
The name of the table backing this store. |
'store'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import PostgresStore
>>> store = PostgresStore("postgresql://user:pass@localhost/dbname") # doctest: +SKIP
>>> store.set_many( # doctest: +SKIP
... {
... "1": {"title": "Intro to Python", "author": "Alice"},
... "2": {"title": "Advanced Python", "author": "Alice"},
... }
... )
>>> len(store.filter(author="Alice")) # doctest: +SKIP
2
persista.store.RedisStore ¶
Bases: BaseRedisStore
A Redis-backed key-value store.
Persists values to Redis and supports adding, retrieving,
filtering, and deleting key-value pairs. Each value is stored as
a JSON string, which provides flexibility for arbitrary value
fields without requiring a fixed schema, is human-readable
directly from Redis, and can be read by any Redis client
regardless of language. This means only JSON-compatible value
fields (str, int, float, bool, None, list, dict) are supported;
use :class:~persista.store.redis_pickle.PickleRedisStore if you
need to persist arbitrary Python objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The Redis connection URL passed to
|
'redis://localhost:6379/0'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import RedisStore
>>> store = RedisStore("redis://localhost:6379/0") # doctest: +SKIP
>>> store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "category": "Programming"},
... "2": {"title": "Advanced Python", "author": "Alice", "category": "Programming"},
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... ) # doctest: +SKIP
>>> len(store.filter(author="Alice")) # doctest: +SKIP
2
persista.store.SQLiteStore ¶
Bases: BaseSQLiteStore
A SQLite-backed key-value store.
Persists values to a SQLite database and supports adding,
retrieving, filtering, and deleting key-value pairs. Each value
is stored as a JSON column (using SQLite's built-in json1
functions), which provides flexibility for arbitrary value
fields without requiring a fixed schema.
The constructor mirrors :func:sqlite3.connect directly. For the
common case of opening a file by path (optionally read-only), use
:meth:from_path instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database
|
Path | str
|
The |
':memory:'
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import SQLiteStore
>>> store = SQLiteStore(":memory:")
>>> store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "category": "Programming"},
... "2": {"title": "Advanced Python", "author": "Alice", "category": "Programming"},
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... )
>>> len(store.filter(author="Alice"))
2
>>> len(store.filter(author="Alice", category="Programming"))
2
>>> len(store.filter(category="History"))
1
persista.store.TypedDuckDBStore ¶
Bases: BaseDuckDBStore
A DuckDB-backed key-value store with an optional typed value schema.
Persists values to a DuckDB database and supports adding,
retrieving, and filtering by value fields. An optional
value_schema maps known value field names to DuckDB types.
Known fields are stored as typed columns for fast, index-friendly
queries. Any value fields not in the schema are stored in an
extra JSON overflow column, so nothing is lost.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path | str
|
Path to the DuckDB file, or |
':memory:'
|
value_schema
|
dict[str, str] | None
|
Optional mapping of value field names to DuckDB
type strings (e.g. |
None
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import TypedDuckDBStore
>>> schema = {"author": "VARCHAR", "year": "INTEGER", "category": "VARCHAR"}
>>> store = TypedDuckDBStore(":memory:", value_schema=schema)
>>> store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "category": "Programming"},
... "2": {"title": "Advanced Python", "author": "Alice", "category": "Programming"},
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... )
>>> len(store.filter(author="Alice"))
2
>>> len(store.filter(author="Alice", category="Programming"))
2
>>> len(store.filter(category="History"))
1
persista.store.TypedPostgresStore ¶
Bases: BasePostgresStore
A Postgres-backed key-value store with an optional typed value schema.
Persists values to a Postgres database and supports adding,
retrieving, and filtering by value fields. An optional
value_schema maps known value field names to Postgres types.
Known fields are stored as typed columns for fast, index-friendly
queries. Any value fields not in the schema are stored in an
extra JSONB overflow column, so nothing is lost. Mirrors
:class:~persista.store.sqlite.TypedSQLiteStore.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conninfo
|
str
|
The connection string/DSN passed to
|
required |
table
|
str
|
The name of the table backing this store. |
'store'
|
value_schema
|
dict[str, str] | None
|
Optional mapping of value field names to
Postgres type strings (e.g. |
None
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import TypedPostgresStore
>>> schema = {"author": "TEXT", "year": "INTEGER"}
>>> store = TypedPostgresStore( # doctest: +SKIP
... "postgresql://user:pass@localhost/dbname", value_schema=schema
... )
>>> store.set_many( # doctest: +SKIP
... {
... "1": {"title": "Intro to Python", "author": "Alice", "year": 2022},
... "2": {"title": "History of Rome", "author": "Bob", "year": 2021},
... }
... )
>>> len(store.filter(author="Alice")) # doctest: +SKIP
1
persista.store.TypedSQLiteStore ¶
Bases: BaseSQLiteStore
A SQLite-backed key-value store with an optional typed value schema.
Persists values to a SQLite database and supports adding,
retrieving, and filtering by value fields. An optional
value_schema maps known value field names to SQLite types.
Known fields are stored as typed columns for fast, index-friendly
queries. Any value fields not in the schema are stored in an
extra JSON overflow column, so nothing is lost.
The constructor mirrors :func:sqlite3.connect directly (plus the
value_schema argument). For the common case of opening a file
by path (optionally read-only), use :meth:from_path instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database
|
Path | str
|
The |
':memory:'
|
value_schema
|
dict[str, str] | None
|
Optional mapping of value field names to SQLite
type strings (e.g. |
None
|
**kwargs
|
Any
|
Additional keyword arguments to pass to
|
{}
|
Example
>>> from persista.store import TypedSQLiteStore
>>> schema = {"author": "TEXT", "year": "INTEGER", "category": "TEXT"}
>>> store = TypedSQLiteStore(":memory:", value_schema=schema)
>>> store.set_many(
... {
... "1": {"title": "Intro to Python", "author": "Alice", "category": "Programming"},
... "2": {"title": "Advanced Python", "author": "Alice", "category": "Programming"},
... "3": {"title": "History of Rome", "author": "Bob", "category": "History"},
... }
... )
>>> len(store.filter(author="Alice"))
2
>>> len(store.filter(author="Alice", category="Programming"))
2
>>> len(store.filter(category="History"))
1
persista.store.normalize_on_conflict ¶
normalize_on_conflict(on_conflict: str) -> OnConflict
Normalize and validate an on_conflict value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
on_conflict
|
str
|
The value to normalize. Matched case-insensitively, with leading/trailing whitespace stripped. |
required |
Returns:
| Type | Description |
|---|---|
OnConflict
|
The normalized value, one of :data: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the normalized value is not one of
:data: |
persista.store.validate_batch_size ¶
validate_batch_size(batch_size: int) -> None
Validate that a value is a valid batch_size strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
The value to validate. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
persista.store.validate_field_name ¶
validate_field_name(name: str) -> None
Validate that a value filter field name is safe to interpolate into a SQL fragment.
BaseStore.filter implementations build SQL by interpolating
the field name directly (only the filter value is passed as a
bound parameter), so an unrestricted field name is a SQL
injection vector. Restricting it to a simple identifier shape
closes that off.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The field name to validate. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
persista.store.validate_on_conflict ¶
validate_on_conflict(on_conflict: str) -> None
Validate that a value is a valid on_conflict strategy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
on_conflict
|
str
|
The value to validate. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |