Ergonomic, type-safe, and SQL-injection-safe PostgreSQL interface using PEP 750 template strings.
Project description
Fassung
Fassung combines asyncpg, pydantic and the template strings to provide an ergonomic, type- and SQL-injection-safe interface for working with PostgreSQL databases. It also allows safe nested query composition.
Fassung is very similar to asyncpg. The main difference is that fassung does neither accept positional parameters nor normal python string ("" or f"") as arguments for it's query methods.
Instead it relies on python's new template string literals (t"") for query construction. This has several benefits:
- It is SQL-injection-safe: you can't pass a string with SQL code as a parameter
- It is ergonomic: you can pass variables directly into the query, your IDE will provide autocompletion and type checking
- It is composable: you can compose queries by concatenating template strings
Additionally, fassung also uses pydantic to map the query results to python objects, like dataclasses or pydantic models.
It is currently just a proof of concept and not ready for production use. Some features like COPY and LISTEN/NOTIFY are not implemented yet.
Installation
Fassung requires python 3.14 because it relies on template string literals (PEP 750). You can install it with
pip install fassung
or
uv add fassung
Usage
The central class is the Pool class, which is a context manager for a connection pool.
You can use the Pool class to create connections. Each connection can execute queries and create transactions.
Basic example:
import asyncio
from datetime import date
from string.templatelib import Template
from pydantic import BaseModel
from fassung import Pool, Transaction
class Student(BaseModel):
id: int
name: str
birth_date: date
async def fetch_all(
transaction: Transaction, limit: int | None = None, offset: int | None = None, where_clause: Template = t""
) -> tuple[int, list[Student]]:
limit_query = t""
if limit is not None:
limit_query = t"LIMIT {limit}"
offset_query = t""
if offset is not None:
offset_query = t"OFFSET {offset}"
count_query = t"SELECT COUNT(*) FROM students {where_clause}"
count = await transaction.fetchval(int, count_query)
query = t"SELECT * FROM students {limit_query} {offset_query} {where_clause}"
students = await transaction.fetch(Student, query)
return count, students
async def main():
pool = Pool.from_connection_string("postgresql://user:password@localhost:5432/testdb")
async with pool.acquire() as connection:
async with connection as transaction:
age = 18
students: list[Student] = await transaction.fetch(
Student, t"SELECT * FROM students WHERE age = {age}"
)
for student in students:
print(student.name)
since = date(2002, 5, 14)
count, students = await fetch_all(transaction, where_clause=t"WHERE birth_date = {since}")
if __name__ == "__main__":
asyncio.run(main())
More examples can be found in the tests/examples directory.
Contributing
We use uv for dependency management, ruff for formatting & linting, pyright for type checking, and pytest for testing. For information on how to set up the development environment and contribute to fassung, please see the Contributing Guide.
License
Fassung is licensed under the MIT license.
Why is it called fassung?
Fassung is the german word for "frame" or "socket". Since it assembles several great python libraries and features (asyncpg, pydantic and template strings) into a single package, it seemed appropriate.
Influences
The idea for the integration of pydantic with asyncpg came from the onlymaps project.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fassung-0.1.2.tar.gz.
File metadata
- Download URL: fassung-0.1.2.tar.gz
- Upload date:
- Size: 6.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b494217f5136d31b9751255cb8f39b09c425b7005793ae4d4344c109dbc194f4
|
|
| MD5 |
effb6e1164bf3c780ddf914d1507c781
|
|
| BLAKE2b-256 |
805b240650a58b4a65e0c612729e9d17dd31ff08cb912c75dc5fbe5af7a2d76c
|
Provenance
The following attestation bundles were made for fassung-0.1.2.tar.gz:
Publisher:
ci.yml on smorokin/fassung
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fassung-0.1.2.tar.gz -
Subject digest:
b494217f5136d31b9751255cb8f39b09c425b7005793ae4d4344c109dbc194f4 - Sigstore transparency entry: 945772605
- Sigstore integration time:
-
Permalink:
smorokin/fassung@e4a5f47cfa60805672805bc37851289ff79e930c -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/smorokin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@e4a5f47cfa60805672805bc37851289ff79e930c -
Trigger Event:
push
-
Statement type:
File details
Details for the file fassung-0.1.2-py3-none-any.whl.
File metadata
- Download URL: fassung-0.1.2-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae5e6a54141110919f996d9eaccd1a67a77d6534f7c9eadcbdd71a6257ebd59d
|
|
| MD5 |
3f84808129e33fbbe5a57881ade399f7
|
|
| BLAKE2b-256 |
682a1ea19857d3a6dd66384328113a9919f0713c13f35057037909d3481fa1a1
|
Provenance
The following attestation bundles were made for fassung-0.1.2-py3-none-any.whl:
Publisher:
ci.yml on smorokin/fassung
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fassung-0.1.2-py3-none-any.whl -
Subject digest:
ae5e6a54141110919f996d9eaccd1a67a77d6534f7c9eadcbdd71a6257ebd59d - Sigstore transparency entry: 945772614
- Sigstore integration time:
-
Permalink:
smorokin/fassung@e4a5f47cfa60805672805bc37851289ff79e930c -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/smorokin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@e4a5f47cfa60805672805bc37851289ff79e930c -
Trigger Event:
push
-
Statement type: