Metadata-Version: 2.5
Name: psycache
Version: 26.4.0
Summary: A Psycopg-backed PostgreSQL cache
Project-URL: Documentation, https://psycache.hynek.me/
Project-URL: Changelog, https://github.com/hynek/psycache/blob/main/CHANGELOG.md
Project-URL: GitHub, https://github.com/hynek/psycache
Project-URL: Funding, https://github.com/sponsors/hynek
Project-URL: Tidelift, https://tidelift.com?utm_source=lifter&utm_medium=referral&utm_campaign=hynek
Project-URL: Mastodon, https://mastodon.social/@hynek
Project-URL: Bluesky, https://bsky.app/profile/hynek.me
Project-URL: Twitter, https://twitter.com/hynek
Author-email: Hynek Schlawack <hs@ox.cx>
License-Expression: MIT
License-File: LICENSE
Keywords: cache,postgres,postgresql,psycopg
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: psycopg>3
Provides-Extra: pool
Requires-Dist: psycopg-pool; extra == 'pool'
Provides-Extra: prometheus
Requires-Dist: prometheus-client; extra == 'prometheus'
Provides-Extra: sentry
Requires-Dist: sentry-sdk; extra == 'sentry'
Provides-Extra: sqlalchemy
Requires-Dist: sqlalchemy; extra == 'sqlalchemy'
Provides-Extra: sqlalchemy-asyncio
Requires-Dist: sqlalchemy[asyncio]; extra == 'sqlalchemy-asyncio'
Description-Content-Type: text/markdown

# *psycache*

*A Psycopg-backed PostgreSQL cache*

A key-value cache that stores JSON in PostgreSQL through [Psycopg](https://www.psycopg.org/) 3, with TTL-based expiration and pluggable instrumentation.

- Sync and async ✔︎
- Type-safe ✔︎
- Adapters for [SQLAlchemy](https://www.sqlalchemy.org) and [*psycopg-pool*](https://www.psycopg.org/psycopg3/docs/api/pool.html) ✔︎

---

*psycache* uses an [unlogged table](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-UNLOGGED) for performance and stores values as [JSONB](https://www.postgresql.org/docs/current/datatype-json.html) for versatility.

It's a great fit when you already have PostgreSQL and need a fast cache without introducing another piece of infrastructure like Redis.
For example, you can safely share a SQLAlchemy [`Engine`](https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Engine) (or [`AsyncEngine`](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.AsyncEngine)) with *psycache*.

<!-- --8<-- [end:spiel] -->


## Quick Start

Let's hitch-hike on a SQLAlchemy engine as a quick example!

First, install *psycache* from PyPI with the `sqlalchemy` extra:

```console
$ uv pip install "psycache[sqlalchemy]"
```

Initialize the cache table once[^cli] then store and retrieve JSON with a TTL:

[^cli]: `python -Im psycache init-db <dsn>` does the same from the shell.
  Add `--table <table>` to use a different table name, optionally schema-qualified with a dot.
  Omit `<dsn>` to print the SQL.

```python
import psycopg

from sqlalchemy import create_engine

import psycache

from psycache import PostgresCache
from psycache.sqlalchemy import SQLAlchemyCachePool


with psycopg.connect(
    "postgresql://psycache@127.0.0.1/psycache", autocommit=True
) as conn:
    psycache.init_db(conn)

engine = create_engine("postgresql+psycopg://psycache@127.0.0.1/psycache")
cache = PostgresCache(SQLAlchemyCachePool(engine))

cache.put_raw("user:alice", {"score": 42}, ttl=300)
value = cache.get_raw("user:alice")
# {"score": 42}

engine.dispose()
```


## Documentation

Full documentation lives at **<https://psycache.hynek.me/>**.


## Release Information

### Changed

- *psycache* is now considered stable and the usual [backwards-compatibility](https://github.com/hynek/psycache/blob/main/.github/SECURITY.md) promises apply.

- The *schema* parameters of `init_db()`, `PostgresCache`, and `AsyncPostgresCache` and the `--schema` option of `init-db` have been replaced by *table* and `--table`.
  They take the name of the cache table, optionally schema-qualified with a dot (for example, `app_cache.psycache`).


### Fixed

- sqlalchemy: Imports from `sqlalchemy.ext.asyncio` are now protected using `if TYPE_CHECKING:`.
  This fixes the runtime dependency on *greenlet* on SQLALchemy 2.1+, even if `psycache.sqlalchemy.AsyncSQLAlchemyCachePool` is not used.
  [#11](https://github.com/hynek/psycache/pull/6)


---

[Full Changelog →](https://github.com/hynek/psycache/blob/main/CHANGELOG.md)


## Credits

*psycache* is written by [Hynek Schlawack](https://hynek.me/) and distributed under the terms of the [MIT license](https://choosealicense.com/licenses/mit/).

The development is kindly supported by my employer [Variomedia AG](https://www.variomedia.de/) and all my fabulous [GitHub Sponsors](https://github.com/sponsors/hynek).