From b2f4fd76d53e1628e05c1796fd30719ed82e803e Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 05:29:51 +0100 Subject: [PATCH 01/13] 1st pass, tests are shit atm --- .../usage/usage_framework_integrations_1.py | 25 + .../usage/usage_framework_integrations_10.py | 32 + .../usage/usage_framework_integrations_11.py | 22 + .../usage/usage_framework_integrations_12.py | 27 + .../usage/usage_framework_integrations_13.py | 32 + .../usage/usage_framework_integrations_14.py | 23 + .../usage/usage_framework_integrations_15.py | 13 + .../usage/usage_framework_integrations_16.py | 21 + .../usage/usage_framework_integrations_17.py | 14 + .../usage/usage_framework_integrations_18.py | 22 + .../usage/usage_framework_integrations_19.py | 23 + .../usage/usage_framework_integrations_2.py | 61 ++ .../usage/usage_framework_integrations_20.py | 22 + .../usage/usage_framework_integrations_21.py | 27 + .../usage/usage_framework_integrations_22.py | 9 + .../usage/usage_framework_integrations_23.py | 15 + .../usage/usage_framework_integrations_24.py | 9 + .../usage/usage_framework_integrations_25.py | 21 + .../usage/usage_framework_integrations_26.py | 25 + .../usage/usage_framework_integrations_27.py | 26 + .../usage/usage_framework_integrations_3.py | 32 + .../usage/usage_framework_integrations_4.py | 28 + .../usage/usage_framework_integrations_5.py | 14 + .../usage/usage_framework_integrations_6.py | 27 + .../usage/usage_framework_integrations_7.py | 55 ++ .../usage/usage_framework_integrations_8.py | 37 + .../usage/usage_framework_integrations_9.py | 24 + docs/usage/framework_integrations.rst | 710 ++++-------------- 28 files changed, 832 insertions(+), 564 deletions(-) create mode 100644 docs/examples/usage/usage_framework_integrations_1.py create mode 100644 docs/examples/usage/usage_framework_integrations_10.py create mode 100644 docs/examples/usage/usage_framework_integrations_11.py create mode 100644 docs/examples/usage/usage_framework_integrations_12.py create mode 100644 docs/examples/usage/usage_framework_integrations_13.py create mode 100644 docs/examples/usage/usage_framework_integrations_14.py create mode 100644 docs/examples/usage/usage_framework_integrations_15.py create mode 100644 docs/examples/usage/usage_framework_integrations_16.py create mode 100644 docs/examples/usage/usage_framework_integrations_17.py create mode 100644 docs/examples/usage/usage_framework_integrations_18.py create mode 100644 docs/examples/usage/usage_framework_integrations_19.py create mode 100644 docs/examples/usage/usage_framework_integrations_2.py create mode 100644 docs/examples/usage/usage_framework_integrations_20.py create mode 100644 docs/examples/usage/usage_framework_integrations_21.py create mode 100644 docs/examples/usage/usage_framework_integrations_22.py create mode 100644 docs/examples/usage/usage_framework_integrations_23.py create mode 100644 docs/examples/usage/usage_framework_integrations_24.py create mode 100644 docs/examples/usage/usage_framework_integrations_25.py create mode 100644 docs/examples/usage/usage_framework_integrations_26.py create mode 100644 docs/examples/usage/usage_framework_integrations_27.py create mode 100644 docs/examples/usage/usage_framework_integrations_3.py create mode 100644 docs/examples/usage/usage_framework_integrations_4.py create mode 100644 docs/examples/usage/usage_framework_integrations_5.py create mode 100644 docs/examples/usage/usage_framework_integrations_6.py create mode 100644 docs/examples/usage/usage_framework_integrations_7.py create mode 100644 docs/examples/usage/usage_framework_integrations_8.py create mode 100644 docs/examples/usage/usage_framework_integrations_9.py diff --git a/docs/examples/usage/usage_framework_integrations_1.py b/docs/examples/usage/usage_framework_integrations_1.py new file mode 100644 index 00000000..93c11135 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_1.py @@ -0,0 +1,25 @@ +# start-example +from litestar import Litestar, get + +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.extensions.litestar import SQLSpecPlugin + +# Configure database and create plugin +spec = SQLSpec() +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) +sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + + +@get("/") +async def index() -> str: + return "integrated" + + +# Create Litestar app +app = Litestar(route_handlers=[index], plugins=[sqlspec_plugin]) +# end-example + + +def test_app_exists() -> None: + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_10.py b/docs/examples/usage/usage_framework_integrations_10.py new file mode 100644 index 00000000..d3077f0f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_10.py @@ -0,0 +1,32 @@ +# start-example +from fastapi import FastAPI, Depends +from contextlib import asynccontextmanager +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase + +# Configure database +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={ + "dsn": "postgresql://localhost/mydb", + "min_size": 10, + "max_size": 20, + } + ) +) + +# Lifespan context manager +@asynccontextmanager +async def lifespan(app: FastAPI): + # Startup + yield + # Shutdown + await spec.close_all_pools() + +app = FastAPI(lifespan=lifespan) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_11.py b/docs/examples/usage/usage_framework_integrations_11.py new file mode 100644 index 00000000..67ffbe11 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_11.py @@ -0,0 +1,22 @@ +# start-example +from typing import AsyncGenerator + +async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: + async with spec.provide_session(config) as session: + yield session + +# Use in route handlers +@app.get("/users/{user_id}") +async def get_user( + user_id: int, + db: AsyncDriverAdapterBase = Depends(get_db_session) +) -> dict: + result = await db.execute( + "SELECT id, name, email FROM users WHERE id = $1", + user_id + ) + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_12.py b/docs/examples/usage/usage_framework_integrations_12.py new file mode 100644 index 00000000..3f106f85 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_12.py @@ -0,0 +1,27 @@ +# start-example +@app.post("/users") +async def create_user( + user_data: dict, + db: AsyncDriverAdapterBase = Depends(get_db_session) +) -> dict: + async with db.begin_transaction(): + result = await db.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", + user_data["name"], + user_data["email"] + ) + + user_id = result.scalar() + + # Additional operations in same transaction + await db.execute( + "INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", + "user_created", + user_id + ) + + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_13.py b/docs/examples/usage/usage_framework_integrations_13.py new file mode 100644 index 00000000..69a80f24 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_13.py @@ -0,0 +1,32 @@ +# start-example +# Main database +main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) + +# Analytics database +analytics_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/analytics"})) + +# Dependency functions +async def get_main_db(): + async with spec.provide_session(main_db) as session: + yield session + +async def get_analytics_db(): + async with spec.provide_session(analytics_db) as session: + yield session + +# Use in handlers +@app.get("/report") +async def generate_report( + main_db: AsyncDriverAdapterBase = Depends(get_main_db), + analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db) +) -> dict: + users = await main_db.execute("SELECT COUNT(*) FROM users") + events = await analytics_db.execute("SELECT COUNT(*) FROM events") + return { + "users": users.scalar(), + "events": events.scalar() + } +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_14.py b/docs/examples/usage/usage_framework_integrations_14.py new file mode 100644 index 00000000..dfd1667f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_14.py @@ -0,0 +1,23 @@ +# start-example +from sanic import Sanic, Request, json +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + +app = Sanic("MyApp") + +# Initialize SQLSpec +spec = SQLSpec() +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) + +# Store in app context +app.ctx.sqlspec = spec +app.ctx.db_config = db + +# Cleanup on shutdown +@app.before_server_stop +async def close_db(app, loop): + await app.ctx.sqlspec.close_all_pools() +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_15.py b/docs/examples/usage/usage_framework_integrations_15.py new file mode 100644 index 00000000..8572782f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_15.py @@ -0,0 +1,13 @@ +# start-example +@app.get("/users/") +async def get_user(request: Request, user_id: int): + async with request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config) as db: + result = await db.execute( + "SELECT id, name, email FROM users WHERE id = $1", + user_id + ) + return json(result.one()) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_16.py b/docs/examples/usage/usage_framework_integrations_16.py new file mode 100644 index 00000000..f2bcdb57 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_16.py @@ -0,0 +1,21 @@ +# start-example +@app.middleware("request") +async def add_db_session(request): + request.ctx.db = await request.app.ctx.sqlspec.provide_session( + request.app.ctx.db_config + ).__aenter__() + +@app.middleware("response") +async def cleanup_db_session(request, response): + if hasattr(request.ctx, "db"): + await request.ctx.db.__aexit__(None, None, None) + +# Use in handlers +@app.get("/users") +async def list_users(request: Request): + result = await request.ctx.db.execute("SELECT * FROM users") + return json(result.rows) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_17.py b/docs/examples/usage/usage_framework_integrations_17.py new file mode 100644 index 00000000..0856646f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_17.py @@ -0,0 +1,14 @@ +# start-example +from flask import Flask, g +from sqlspec import SQLSpec +from sqlspec.adapters.sqlite import SqliteConfig + +app = Flask(__name__) + +# Initialize SQLSpec +spec = SQLSpec() +db = spec.add_config(SqliteConfig(pool_config={"database": "app.db"})) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_18.py b/docs/examples/usage/usage_framework_integrations_18.py new file mode 100644 index 00000000..b982203d --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_18.py @@ -0,0 +1,22 @@ +# start-example +def get_db(): + if 'db' not in g: + g.db = spec.provide_session(db).__enter__() + return g.db + +@app.teardown_appcontext +def close_db(error): + db = g.pop('db', None) + if db is not None: + db.__exit__(None, None, None) + +# Use in routes +@app.route('/users/') +def get_user(user_id): + db = get_db() + result = db.execute("SELECT * FROM users WHERE id = ?", user_id) + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_19.py b/docs/examples/usage/usage_framework_integrations_19.py new file mode 100644 index 00000000..315137ce --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_19.py @@ -0,0 +1,23 @@ +# start-example +class DatabaseSession: + def __init__(self, spec: SQLSpec, config): + self.spec = spec + self.config = config + self.session = None + + async def __aenter__(self): + self.session = await self.spec.provide_session(self.config).__aenter__() + return self.session + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self.session: + await self.session.__aexit__(exc_type, exc_val, exc_tb) + +# Usage +async def example_usage(): + async with DatabaseSession(spec, config) as db: + result = await db.execute("SELECT * FROM users") +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_2.py b/docs/examples/usage/usage_framework_integrations_2.py new file mode 100644 index 00000000..d04d0182 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_2.py @@ -0,0 +1,61 @@ +# start-example +from typing import Any + +import pytest +from asyncpg import Connection, Pool +from litestar import Litestar, get +from litestar.testing import AsyncTestClient +from pytest_databases.docker.postgres import PostgresService + +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase +from sqlspec.extensions.litestar import SQLSpecPlugin + + +# Inject database session +@get("/users/{user_id:int}") +async def get_user(user_id: int, db_session: AsyncDriverAdapterBase) -> dict: + result = await db_session.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) + return result.one() + + +# Inject connection pool +@get("/health") +async def health_check(db_pool: Pool) -> dict[str, Any]: + async with db_pool.acquire() as conn: + result = await conn.fetchval("SELECT 1") + return {"status": "healthy" if result == 1 else "unhealthy"} + + +# Inject raw connection +@get("/stats") +async def stats(db_connection: Connection) -> dict[str, Any]: + result = await db_connection.fetchval("SELECT COUNT(*) FROM users") + return {"user_count": result} + + +# end-example +@pytest.fixture +async def client(postgres_service: PostgresService): + spec = SQLSpec() + config = AsyncpgConfig( + pool_config={ + "host": postgres_service.host, + "port": postgres_service.port, + "user": postgres_service.user, + "password": postgres_service.password, + "database": postgres_service.database, + } + ) + db = spec.add_config(config) + + sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + app = Litestar(route_handlers=[health_check, stats, get_user], plugins=[sqlspec_plugin], debug=True) + async with AsyncTestClient(app) as client: + yield client + + +async def test_stub(client: AsyncTestClient): + response = await client.get("/stats") + assert response == {"status": "healthy"} diff --git a/docs/examples/usage/usage_framework_integrations_20.py b/docs/examples/usage/usage_framework_integrations_20.py new file mode 100644 index 00000000..a11fef97 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_20.py @@ -0,0 +1,22 @@ +# start-example +import asyncio +from contextvars import ContextVar + +db_session: ContextVar = ContextVar('db_session', default=None) + +async def get_session(): + session = db_session.get() + if session is None: + session = await spec.provide_session(config).__aenter__() + db_session.set(session) + return session + +async def cleanup_session(): + session = db_session.get() + if session: + await session.__aexit__(None, None, None) + db_session.set(None) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_21.py b/docs/examples/usage/usage_framework_integrations_21.py new file mode 100644 index 00000000..0d6c833f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_21.py @@ -0,0 +1,27 @@ +# start-example +class Database: + _instance = None + _spec = None + _config = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._spec = SQLSpec() + cls._config = cls._spec.add_config( + AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}) + ) + return cls._instance + + async def session(self): + return self._spec.provide_session(self._config) + +# Usage +db = Database() +async def example_usage(): + async with await db.session() as session: + result = await session.execute("SELECT * FROM users") +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_22.py b/docs/examples/usage/usage_framework_integrations_22.py new file mode 100644 index 00000000..4710213b --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_22.py @@ -0,0 +1,9 @@ +# start-example +# Prefer Litestar plugin over manual setup +spec = SQLSpec() +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) +app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py new file mode 100644 index 00000000..7143b57f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -0,0 +1,15 @@ +# start-example +# FastAPI +@asynccontextmanager +async def lifespan(app: FastAPI): + yield + await spec.close_all_pools() + +# Sanic +@app.before_server_stop +async def close_pools(app, loop): + await spec.close_all_pools() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_24.py b/docs/examples/usage/usage_framework_integrations_24.py new file mode 100644 index 00000000..2f30d8ea --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_24.py @@ -0,0 +1,9 @@ +# start-example +# Inject sessions, not global instances +async def get_db(): + async with spec.provide_session(config) as session: + yield session +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_25.py b/docs/examples/usage/usage_framework_integrations_25.py new file mode 100644 index 00000000..e350e011 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_25.py @@ -0,0 +1,21 @@ +# start-example +# Use autocommit for simple CRUD +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "autocommit"} + } + ) +) + +# Manual transactions for complex operations +async def manual_transaction(db_session): + async with db_session.begin_transaction(): + # Multiple operations + pass +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py new file mode 100644 index 00000000..3d034aaa --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -0,0 +1,25 @@ +# start-example +# Good: Separate repository layer +class UserRepository: + def __init__(self, db: AsyncDriverAdapterBase): + self.db = db + + async def get_user(self, user_id: int): + result = await self.db.execute( + "SELECT * FROM users WHERE id = $1", + user_id + ) + return result.one() + +# Use in handlers +@app.get("/users/{user_id}") +async def get_user( + user_id: int, + db: AsyncDriverAdapterBase = Depends(get_db) +): + repo = UserRepository(db) + return await repo.get_user(user_id) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_27.py b/docs/examples/usage/usage_framework_integrations_27.py new file mode 100644 index 00000000..f0404136 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_27.py @@ -0,0 +1,26 @@ +# start-example +import pytest +from sqlspec.adapters.sqlite import SqliteConfig + +@pytest.fixture +async def test_db(): + spec = SQLSpec() + db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) + + async with spec.provide_session(db) as session: + # Set up test schema + await session.execute(""" + CREATE TABLE users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL + ) + """) + yield session + +async def test_create_user(test_db): + result = await test_db.execute( + "INSERT INTO users (name) VALUES ($1) RETURNING id", + "Test User" + ) + assert result.scalar() == 1 +# end-example diff --git a/docs/examples/usage/usage_framework_integrations_3.py b/docs/examples/usage/usage_framework_integrations_3.py new file mode 100644 index 00000000..a8793315 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_3.py @@ -0,0 +1,32 @@ +# start-example +from litestar import post +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase + +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "manual"} # Default + } + ) +) + +@post("/users") +async def create_user( + data: dict, + db_session: AsyncDriverAdapterBase +) -> dict: + async with db_session.begin_transaction(): + result = await db_session.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", + data["name"], + data["email"] + ) + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_4.py b/docs/examples/usage/usage_framework_integrations_4.py new file mode 100644 index 00000000..2f2e8bfb --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_4.py @@ -0,0 +1,28 @@ +# start-example +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "autocommit"} # Auto-commit on 2xx + } + ) +) + +@post("/users") +async def create_user( + data: dict, + db_session: AsyncDriverAdapterBase +) -> dict: + # Transaction begins automatically + result = await db_session.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", + data["name"], + data["email"] + ) + # Commits automatically on success + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_5.py b/docs/examples/usage/usage_framework_integrations_5.py new file mode 100644 index 00000000..d79d5bed --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_5.py @@ -0,0 +1,14 @@ +# start-example +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "autocommit_include_redirect"} + } + ) +) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_6.py b/docs/examples/usage/usage_framework_integrations_6.py new file mode 100644 index 00000000..39fd00d6 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_6.py @@ -0,0 +1,27 @@ +# start-example +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase + +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": { + "connection_key": "database", # Default: "db_connection" + "pool_key": "db_pool", # Default: "db_pool" + "session_key": "session", # Default: "db_session" + } + } + ) +) + +@get("/users") +async def list_users(session: AsyncDriverAdapterBase) -> list: + result = await session.execute("SELECT * FROM users") + return result.all() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_7.py b/docs/examples/usage/usage_framework_integrations_7.py new file mode 100644 index 00000000..f79bebb4 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_7.py @@ -0,0 +1,55 @@ +# start-example +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase +from sqlspec.extensions.litestar import SQLSpecPlugin + +spec = SQLSpec() + +# Main database +main_db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/main"}, + extension_config={ + "litestar": { + "session_key": "main_db", + "connection_key": "main_db_connection", + } + } + ) +) + +# Analytics database +analytics_db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/analytics"}, + extension_config={ + "litestar": { + "session_key": "analytics_db", + "connection_key": "analytics_connection", + } + } + ) +) + +# Create single plugin with all configs +app = Litestar( + plugins=[SQLSpecPlugin(sqlspec=spec)] +) + +# Use in handlers +@get("/report") +async def generate_report( + main_db: AsyncDriverAdapterBase, + analytics_db: AsyncDriverAdapterBase +) -> dict: + users = await main_db.execute("SELECT COUNT(*) FROM users") + events = await analytics_db.execute("SELECT COUNT(*) FROM events") + return { + "total_users": users.scalar(), + "total_events": events.scalar() + } +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_8.py b/docs/examples/usage/usage_framework_integrations_8.py new file mode 100644 index 00000000..d5a4fe07 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_8.py @@ -0,0 +1,37 @@ +# start-example +from litestar import Litestar +from litestar.middleware.session.server_side import ServerSideSessionConfig +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.adapters.asyncpg.litestar import AsyncpgStore +from sqlspec.extensions.litestar import SQLSpecPlugin + +# Configure database with session support +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + extension_config={ + "litestar": {"session_table": "litestar_sessions"} + }, + migration_config={ + "script_location": "migrations", + "include_extensions": ["litestar"] + } + ) +) + +# Create session store using adapter-specific class +store = AsyncpgStore(db) + +# Configure Litestar with plugin and session middleware +app = Litestar( + plugins=[SQLSpecPlugin(sqlspec=spec)], + middleware=[ + ServerSideSessionConfig(store=store).middleware + ] +) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py new file mode 100644 index 00000000..77d0a8ea --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -0,0 +1,24 @@ +# start-example +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": { + "enable_correlation_middleware": True, # Default: True + "correlation_header": "x-request-id", + "correlation_headers": ["x-client-trace"], + "auto_trace_headers": True, + } + } + ) +) +# Queries will include correlation IDs in logs (header or generated UUID) +# Format: [correlation_id=abc123] SELECT * FROM users +# end-example + +def test_stub(): + assert True diff --git a/docs/usage/framework_integrations.rst b/docs/usage/framework_integrations.rst index 5860fd6f..443e3c80 100644 --- a/docs/usage/framework_integrations.rst +++ b/docs/usage/framework_integrations.rst @@ -23,66 +23,24 @@ The Litestar plugin provides first-class integration with comprehensive features Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from litestar import Litestar, get - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.extensions.litestar import SQLSpecPlugin - - # Configure database and create plugin - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/mydb", - "min_size": 10, - "max_size": 20, - } - ) - ) - sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) - - # Create Litestar app - app = Litestar( - route_handlers=[...], - plugins=[sqlspec_plugin] - ) +.. literalinclude:: ../examples/usage/usage_framework_integrations_1.py + :language: python + :dedent: 0 + :start-adter: # start-example + :end-before: # end-example + Using Dependency Injection ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The plugin provides dependency injection for connections, pools, and sessions: -.. code-block:: python - - from litestar import get, post - from sqlspec.driver import AsyncDriverAdapterBase - - # Inject database session - @get("/users/{user_id:int}") - async def get_user( - user_id: int, - db_session: AsyncDriverAdapterBase - ) -> dict: - result = await db_session.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) - return result.one() - - # Inject connection pool - @get("/health") - async def health_check(db_pool) -> dict: - async with db_pool.acquire() as conn: - result = await conn.fetchval("SELECT 1") - return {"status": "healthy" if result == 1 else "unhealthy"} - - # Inject raw connection - @get("/stats") - async def stats(db_connection) -> dict: - result = await db_connection.fetchval("SELECT COUNT(*) FROM users") - return {"user_count": result} +.. literalinclude:: ../examples/usage/usage_framework_integrations_2.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Commit Modes ^^^^^^^^^^^^ @@ -93,209 +51,70 @@ The plugin supports different transaction commit strategies configured via ``ext You control transaction boundaries explicitly: -.. code-block:: python - - from litestar import post - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "manual"} # Default - } - ) - ) - - @post("/users") - async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase - ) -> dict: - async with db_session.begin_transaction(): - result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] - ) - return result.one() +.. literalinclude:: ../examples/usage/usage_framework_integrations_3.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + **Autocommit Mode** Automatically commits on successful requests (2xx responses): -.. code-block:: python - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit"} # Auto-commit on 2xx - } - ) - ) - - @post("/users") - async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase - ) -> dict: - # Transaction begins automatically - result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] - ) - # Commits automatically on success - return result.one() +.. literalinclude:: ../examples/usage/usage_framework_integrations_4.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + **Autocommit with Redirects** Commits on both 2xx and 3xx responses: -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_5.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit_include_redirect"} - } - ) - ) Custom Dependency Keys ^^^^^^^^^^^^^^^^^^^^^^ Customize the dependency injection keys via ``extension_config``: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": { - "connection_key": "database", # Default: "db_connection" - "pool_key": "db_pool", # Default: "db_pool" - "session_key": "session", # Default: "db_session" - } - } - ) - ) - - @get("/users") - async def list_users(session: AsyncDriverAdapterBase) -> list: - result = await session.execute("SELECT * FROM users") - return result.all() +.. literalinclude:: ../examples/usage/usage_framework_integrations_6.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Multiple Database Configurations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The plugin supports multiple database configurations through a single SQLSpec instance: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - from sqlspec.extensions.litestar import SQLSpecPlugin - - spec = SQLSpec() - - # Main database - main_db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/main"}, - extension_config={ - "litestar": { - "session_key": "main_db", - "connection_key": "main_db_connection", - } - } - ) - ) - - # Analytics database - analytics_db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/analytics"}, - extension_config={ - "litestar": { - "session_key": "analytics_db", - "connection_key": "analytics_connection", - } - } - ) - ) - - # Create single plugin with all configs - app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)] - ) - - # Use in handlers - @get("/report") - async def generate_report( - main_db: AsyncDriverAdapterBase, - analytics_db: AsyncDriverAdapterBase - ) -> dict: - users = await main_db.execute("SELECT COUNT(*) FROM users") - events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "total_users": users.scalar(), - "total_events": events.scalar() - } +.. literalinclude:: ../examples/usage/usage_framework_integrations_7.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Session Storage Backend ^^^^^^^^^^^^^^^^^^^^^^^ Use SQLSpec as a session backend for Litestar: -.. code-block:: python - - from litestar import Litestar - from litestar.middleware.session.server_side import ServerSideSessionConfig - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.adapters.asyncpg.litestar import AsyncpgStore - from sqlspec.extensions.litestar import SQLSpecPlugin - - # Configure database with session support - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": {"session_table": "litestar_sessions"} - }, - migration_config={ - "script_location": "migrations", - "include_extensions": ["litestar"] - } - ) - ) - - # Create session store using adapter-specific class - store = AsyncpgStore(db) - - # Configure Litestar with plugin and session middleware - app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)], - middleware=[ - ServerSideSessionConfig(store=store).middleware - ] - ) +.. literalinclude:: ../examples/usage/usage_framework_integrations_8.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + CLI Integration ^^^^^^^^^^^^^^^ @@ -328,28 +147,12 @@ Correlation Middleware Enable request correlation tracking via ``extension_config``: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": { - "enable_correlation_middleware": True, # Default: True - "correlation_header": "x-request-id", - "correlation_headers": ["x-client-trace"], - "auto_trace_headers": True, - } - } - ) - ) - - # Queries will include correlation IDs in logs (header or generated UUID) - # Format: [correlation_id=abc123] SELECT * FROM users +.. literalinclude:: ../examples/usage/usage_framework_integrations_9.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + FastAPI Integration ------------------- @@ -359,125 +162,48 @@ While SQLSpec doesn't have a dedicated FastAPI plugin, integration is straightfo Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from fastapi import FastAPI, Depends - from contextlib import asynccontextmanager - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - - # Configure database - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/mydb", - "min_size": 10, - "max_size": 20, - } - ) - ) - - # Lifespan context manager - @asynccontextmanager - async def lifespan(app: FastAPI): - # Startup - yield - # Shutdown - await spec.close_all_pools() - - app = FastAPI(lifespan=lifespan) +.. literalinclude:: ../examples/usage/usage_framework_integrations_10.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Dependency Injection ^^^^^^^^^^^^^^^^^^^^ Create a dependency function for database sessions: -.. code-block:: python - - from typing import AsyncGenerator - - async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: - async with spec.provide_session(config) as session: - yield session +.. literalinclude:: ../examples/usage/usage_framework_integrations_11.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Use in route handlers - @app.get("/users/{user_id}") - async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db_session) - ) -> dict: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) - return result.one() Transaction Management ^^^^^^^^^^^^^^^^^^^^^^ Implement transaction handling with FastAPI: -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_12.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - @app.post("/users") - async def create_user( - user_data: dict, - db: AsyncDriverAdapterBase = Depends(get_db_session) - ) -> dict: - async with db.begin_transaction(): - result = await db.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - user_data["name"], - user_data["email"] - ) - - user_id = result.scalar() - - # Additional operations in same transaction - await db.execute( - "INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", - "user_created", - user_id - ) - - return result.one() Multiple Databases ^^^^^^^^^^^^^^^^^^ Support multiple databases with different dependencies: -.. code-block:: python - - # Main database - main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) - - # Analytics database - analytics_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/analytics"})) +.. literalinclude:: ../examples/usage/usage_framework_integrations_13.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Dependency functions - async def get_main_db(): - async with spec.provide_session(main_db) as session: - yield session - - async def get_analytics_db(): - async with spec.provide_session(analytics_db) as session: - yield session - - # Use in handlers - @app.get("/report") - async def generate_report( - main_db: AsyncDriverAdapterBase = Depends(get_main_db), - analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db) - ) -> dict: - users = await main_db.execute("SELECT COUNT(*) FROM users") - events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "users": users.scalar(), - "events": events.scalar() - } Sanic Integration ----------------- @@ -487,62 +213,32 @@ Integrate SQLSpec with Sanic using listeners and app context. Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from sanic import Sanic, Request, json - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - - app = Sanic("MyApp") - - # Initialize SQLSpec - spec = SQLSpec() - db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) +.. literalinclude:: ../examples/usage/usage_framework_integrations_14.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Store in app context - app.ctx.sqlspec = spec - app.ctx.db_config = db - - # Cleanup on shutdown - @app.before_server_stop - async def close_db(app, loop): - await app.ctx.sqlspec.close_all_pools() Using in Route Handlers ^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_15.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - @app.get("/users/") - async def get_user(request: Request, user_id: int): - async with request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config) as db: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) - return json(result.one()) Middleware for Automatic Sessions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - @app.middleware("request") - async def add_db_session(request): - request.ctx.db = await request.app.ctx.sqlspec.provide_session( - request.app.ctx.db_config - ).__aenter__() - - @app.middleware("response") - async def cleanup_db_session(request, response): - if hasattr(request.ctx, "db"): - await request.ctx.db.__aexit__(None, None, None) +.. literalinclude:: ../examples/usage/usage_framework_integrations_16.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Use in handlers - @app.get("/users") - async def list_users(request: Request): - result = await request.ctx.db.execute("SELECT * FROM users") - return json(result.rows) Flask Integration ----------------- @@ -552,40 +248,22 @@ Integrate SQLSpec with Flask using synchronous drivers. Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from flask import Flask, g - from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig - - app = Flask(__name__) +.. literalinclude:: ../examples/usage/usage_framework_integrations_17.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Initialize SQLSpec - spec = SQLSpec() - db = spec.add_config(SqliteConfig(pool_config={"database": "app.db"})) Using Request Context ^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_18.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - def get_db(): - if 'db' not in g: - g.db = spec.provide_session(db).__enter__() - return g.db - - @app.teardown_appcontext - def close_db(error): - db = g.pop('db', None) - if db is not None: - db.__exit__(None, None, None) - - # Use in routes - @app.route('/users/') - def get_user(user_id): - db = get_db() - result = db.execute("SELECT * FROM users WHERE id = ?", user_id) - return result.one() Custom Integration Patterns ---------------------------- @@ -595,160 +273,84 @@ Context Manager Pattern For frameworks without built-in dependency injection: -.. code-block:: python - - class DatabaseSession: - def __init__(self, spec: SQLSpec, config): - self.spec = spec - self.config = config - self.session = None - - async def __aenter__(self): - self.session = await self.spec.provide_session(self.config).__aenter__() - return self.session - - async def __aexit__(self, exc_type, exc_val, exc_tb): - if self.session: - await self.session.__aexit__(exc_type, exc_val, exc_tb) +.. literalinclude:: ../examples/usage/usage_framework_integrations_19.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Usage - async with DatabaseSession(spec, config) as db: - result = await db.execute("SELECT * FROM users") Request-Scoped Sessions ^^^^^^^^^^^^^^^^^^^^^^^ Implement request-scoped database sessions: -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_20.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - import asyncio - from contextvars import ContextVar - - db_session: ContextVar = ContextVar('db_session', default=None) - - async def get_session(): - session = db_session.get() - if session is None: - session = await spec.provide_session(config).__aenter__() - db_session.set(session) - return session - - async def cleanup_session(): - session = db_session.get() - if session: - await session.__aexit__(None, None, None) - db_session.set(None) Singleton Pattern ^^^^^^^^^^^^^^^^^ For simple applications with a single database: -.. code-block:: python - - class Database: - _instance = None - _spec = None - _config = None - - def __new__(cls): - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._spec = SQLSpec() - cls._config = cls._spec.add_config( - AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}) - ) - return cls._instance +.. literalinclude:: ../examples/usage/usage_framework_integrations_21.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - async def session(self): - return self._spec.provide_session(self._config) - - # Usage - db = Database() - async with await db.session() as session: - result = await session.execute("SELECT * FROM users") Best Practices -------------- **1. Use Framework-Specific Plugins When Available** -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_22.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Prefer Litestar plugin over manual setup - spec = SQLSpec() - db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) - app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) **2. Always Clean Up Pools** -.. code-block:: python - - # FastAPI - @asynccontextmanager - async def lifespan(app: FastAPI): - yield - await spec.close_all_pools() +.. literalinclude:: ../examples/usage/usage_framework_integrations_23.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Sanic - @app.before_server_stop - async def close_pools(app, loop): - await spec.close_all_pools() **3. Use Dependency Injection** -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_24.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Inject sessions, not global instances - async def get_db(): - async with spec.provide_session(config) as session: - yield session **4. Handle Transactions Appropriately** -.. code-block:: python - - # Use autocommit for simple CRUD - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit"} - } - ) - ) +.. literalinclude:: ../examples/usage/usage_framework_integrations_25.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Manual transactions for complex operations - async with db_session.begin_transaction(): - # Multiple operations - pass **5. Separate Database Logic** -.. code-block:: python - - # Good: Separate repository layer - class UserRepository: - def __init__(self, db: AsyncDriverAdapterBase): - self.db = db - - async def get_user(self, user_id: int): - result = await self.db.execute( - "SELECT * FROM users WHERE id = $1", - user_id - ) - return result.one() - - # Use in handlers - @app.get("/users/{user_id}") - async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db) - ): - repo = UserRepository(db) - return await repo.get_user(user_id) +.. literalinclude:: ../examples/usage/usage_framework_integrations_26.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Testing ------- @@ -756,32 +358,12 @@ Testing Testing with Framework Integration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - import pytest - from sqlspec.adapters.sqlite import SqliteConfig - - @pytest.fixture - async def test_db(): - spec = SQLSpec() - db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) - - async with spec.provide_session(db) as session: - # Set up test schema - await session.execute(""" - CREATE TABLE users ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL - ) - """) - yield session - - async def test_create_user(test_db): - result = await test_db.execute( - "INSERT INTO users (name) VALUES ($1) RETURNING id", - "Test User" - ) - assert result.scalar() == 1 +.. literalinclude:: ../examples/usage/usage_framework_integrations_27.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Next Steps ---------- From fd8d757c7c25d2b9da7772ef90f8f5ba4770f661 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 05:35:37 +0100 Subject: [PATCH 02/13] lint --- .../usage/usage_framework_integrations_1.py | 3 ++ .../usage/usage_framework_integrations_10.py | 23 ++++++------ .../usage/usage_framework_integrations_11.py | 21 ++++++----- .../usage/usage_framework_integrations_12.py | 23 +++++------- .../usage/usage_framework_integrations_13.py | 18 ++++++--- .../usage/usage_framework_integrations_14.py | 14 +++++-- .../usage/usage_framework_integrations_15.py | 13 ++++--- .../usage/usage_framework_integrations_16.py | 18 ++++++--- .../usage/usage_framework_integrations_17.py | 9 ++++- .../usage/usage_framework_integrations_18.py | 18 ++++++--- .../usage/usage_framework_integrations_19.py | 15 ++++++-- .../usage/usage_framework_integrations_2.py | 6 ++- .../usage/usage_framework_integrations_20.py | 15 ++++++-- .../usage/usage_framework_integrations_21.py | 19 +++++++--- .../usage/usage_framework_integrations_22.py | 6 ++- .../usage/usage_framework_integrations_23.py | 11 +++++- .../usage/usage_framework_integrations_24.py | 8 +++- .../usage/usage_framework_integrations_25.py | 18 +++++---- .../usage/usage_framework_integrations_26.py | 21 ++++++----- .../usage/usage_framework_integrations_27.py | 14 ++++--- .../usage/usage_framework_integrations_3.py | 21 ++++++----- .../usage/usage_framework_integrations_4.py | 20 +++++----- .../usage/usage_framework_integrations_5.py | 10 +++-- .../usage/usage_framework_integrations_6.py | 17 ++++++--- .../usage/usage_framework_integrations_7.py | 37 +++++++------------ .../usage/usage_framework_integrations_8.py | 23 +++++------- .../usage/usage_framework_integrations_9.py | 10 +++-- 27 files changed, 258 insertions(+), 173 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_1.py b/docs/examples/usage/usage_framework_integrations_1.py index 93c11135..344a5a63 100644 --- a/docs/examples/usage/usage_framework_integrations_1.py +++ b/docs/examples/usage/usage_framework_integrations_1.py @@ -5,6 +5,9 @@ from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("index", "test_app_exists" ) + + # Configure database and create plugin spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) diff --git a/docs/examples/usage/usage_framework_integrations_10.py b/docs/examples/usage/usage_framework_integrations_10.py index d3077f0f..9455efec 100644 --- a/docs/examples/usage/usage_framework_integrations_10.py +++ b/docs/examples/usage/usage_framework_integrations_10.py @@ -1,21 +1,18 @@ # start-example -from fastapi import FastAPI, Depends from contextlib import asynccontextmanager + +from fastapi import FastAPI + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig -from sqlspec.driver import AsyncDriverAdapterBase + +__all__ = ("lifespan", "test_stub" ) + # Configure database spec = SQLSpec() -db = spec.add_config( - AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/mydb", - "min_size": 10, - "max_size": 20, - } - ) -) +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) + # Lifespan context manager @asynccontextmanager @@ -25,8 +22,10 @@ async def lifespan(app: FastAPI): # Shutdown await spec.close_all_pools() + app = FastAPI(lifespan=lifespan) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_11.py b/docs/examples/usage/usage_framework_integrations_11.py index 67ffbe11..37d8f4ed 100644 --- a/docs/examples/usage/usage_framework_integrations_11.py +++ b/docs/examples/usage/usage_framework_integrations_11.py @@ -1,22 +1,23 @@ # start-example -from typing import AsyncGenerator +from collections.abc import AsyncGenerator + +__all__ = ("get_db_session", "get_user", "test_stub" ) + async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: async with spec.provide_session(config) as session: yield session + # Use in route handlers @app.get("/users/{user_id}") -async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db_session) -) -> dict: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) +async def get_user(user_id: int, db: AsyncDriverAdapterBase = Depends(get_db_session)) -> dict: + result = await db.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_12.py b/docs/examples/usage/usage_framework_integrations_12.py index 3f106f85..a819aef3 100644 --- a/docs/examples/usage/usage_framework_integrations_12.py +++ b/docs/examples/usage/usage_framework_integrations_12.py @@ -1,27 +1,24 @@ # start-example +__all__ = ("create_user", "test_stub" ) + + @app.post("/users") -async def create_user( - user_data: dict, - db: AsyncDriverAdapterBase = Depends(get_db_session) -) -> dict: +async def create_user(user_data: dict, db: AsyncDriverAdapterBase = Depends(get_db_session)) -> dict: async with db.begin_transaction(): result = await db.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - user_data["name"], - user_data["email"] + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", user_data["name"], user_data["email"] ) user_id = result.scalar() # Additional operations in same transaction - await db.execute( - "INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", - "user_created", - user_id - ) + await db.execute("INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", "user_created", user_id) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_13.py b/docs/examples/usage/usage_framework_integrations_13.py index 69a80f24..78cc0656 100644 --- a/docs/examples/usage/usage_framework_integrations_13.py +++ b/docs/examples/usage/usage_framework_integrations_13.py @@ -1,32 +1,38 @@ # start-example +__all__ = ("generate_report", "get_analytics_db", "get_main_db", "test_stub" ) + + # Main database main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) # Analytics database analytics_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/analytics"})) + # Dependency functions async def get_main_db(): async with spec.provide_session(main_db) as session: yield session + async def get_analytics_db(): async with spec.provide_session(analytics_db) as session: yield session + # Use in handlers @app.get("/report") async def generate_report( main_db: AsyncDriverAdapterBase = Depends(get_main_db), - analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db) + analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db), ) -> dict: users = await main_db.execute("SELECT COUNT(*) FROM users") events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "users": users.scalar(), - "events": events.scalar() - } + return {"users": users.scalar(), "events": events.scalar()} + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_14.py b/docs/examples/usage/usage_framework_integrations_14.py index dfd1667f..023b68e6 100644 --- a/docs/examples/usage/usage_framework_integrations_14.py +++ b/docs/examples/usage/usage_framework_integrations_14.py @@ -1,8 +1,12 @@ # start-example -from sanic import Sanic, Request, json +from sanic import Sanic + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig +__all__ = ("close_db", "test_stub" ) + + app = Sanic("MyApp") # Initialize SQLSpec @@ -13,11 +17,15 @@ app.ctx.sqlspec = spec app.ctx.db_config = db + # Cleanup on shutdown @app.before_server_stop -async def close_db(app, loop): +async def close_db(app, loop) -> None: await app.ctx.sqlspec.close_all_pools() + + # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_15.py b/docs/examples/usage/usage_framework_integrations_15.py index 8572782f..b6a96590 100644 --- a/docs/examples/usage/usage_framework_integrations_15.py +++ b/docs/examples/usage/usage_framework_integrations_15.py @@ -1,13 +1,16 @@ # start-example +__all__ = ("get_user", "test_stub" ) + + @app.get("/users/") async def get_user(request: Request, user_id: int): async with request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config) as db: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) + result = await db.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) return json(result.one()) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_16.py b/docs/examples/usage/usage_framework_integrations_16.py index f2bcdb57..0b8f4a8e 100644 --- a/docs/examples/usage/usage_framework_integrations_16.py +++ b/docs/examples/usage/usage_framework_integrations_16.py @@ -1,21 +1,27 @@ # start-example +__all__ = ("add_db_session", "cleanup_db_session", "list_users", "test_stub" ) + + @app.middleware("request") -async def add_db_session(request): - request.ctx.db = await request.app.ctx.sqlspec.provide_session( - request.app.ctx.db_config - ).__aenter__() +async def add_db_session(request) -> None: + request.ctx.db = await request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config).__aenter__() + @app.middleware("response") -async def cleanup_db_session(request, response): +async def cleanup_db_session(request, response) -> None: if hasattr(request.ctx, "db"): await request.ctx.db.__aexit__(None, None, None) + # Use in handlers @app.get("/users") async def list_users(request: Request): result = await request.ctx.db.execute("SELECT * FROM users") return json(result.rows) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_17.py b/docs/examples/usage/usage_framework_integrations_17.py index 0856646f..f71a1dca 100644 --- a/docs/examples/usage/usage_framework_integrations_17.py +++ b/docs/examples/usage/usage_framework_integrations_17.py @@ -1,8 +1,12 @@ # start-example -from flask import Flask, g +from flask import Flask + from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig +__all__ = ("test_stub", ) + + app = Flask(__name__) # Initialize SQLSpec @@ -10,5 +14,6 @@ db = spec.add_config(SqliteConfig(pool_config={"database": "app.db"})) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_18.py b/docs/examples/usage/usage_framework_integrations_18.py index b982203d..0725eb5c 100644 --- a/docs/examples/usage/usage_framework_integrations_18.py +++ b/docs/examples/usage/usage_framework_integrations_18.py @@ -1,22 +1,30 @@ # start-example +__all__ = ("close_db", "get_db", "get_user", "test_stub" ) + + def get_db(): - if 'db' not in g: + if "db" not in g: g.db = spec.provide_session(db).__enter__() return g.db + @app.teardown_appcontext -def close_db(error): - db = g.pop('db', None) +def close_db(error) -> None: + db = g.pop("db", None) if db is not None: db.__exit__(None, None, None) + # Use in routes -@app.route('/users/') +@app.route("/users/") def get_user(user_id): db = get_db() result = db.execute("SELECT * FROM users WHERE id = ?", user_id) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_19.py b/docs/examples/usage/usage_framework_integrations_19.py index 315137ce..a48ab193 100644 --- a/docs/examples/usage/usage_framework_integrations_19.py +++ b/docs/examples/usage/usage_framework_integrations_19.py @@ -1,6 +1,9 @@ # start-example +__all__ = ("DatabaseSession", "example_usage", "test_stub" ) + + class DatabaseSession: - def __init__(self, spec: SQLSpec, config): + def __init__(self, spec: SQLSpec, config) -> None: self.spec = spec self.config = config self.session = None @@ -13,11 +16,15 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): if self.session: await self.session.__aexit__(exc_type, exc_val, exc_tb) + # Usage -async def example_usage(): +async def example_usage() -> None: async with DatabaseSession(spec, config) as db: - result = await db.execute("SELECT * FROM users") + await db.execute("SELECT * FROM users") + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_2.py b/docs/examples/usage/usage_framework_integrations_2.py index d04d0182..b5fa6275 100644 --- a/docs/examples/usage/usage_framework_integrations_2.py +++ b/docs/examples/usage/usage_framework_integrations_2.py @@ -12,6 +12,8 @@ from sqlspec.driver import AsyncDriverAdapterBase from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("client", "get_user", "health_check", "stats", "test_stub" ) + # Inject database session @get("/users/{user_id:int}") @@ -48,7 +50,7 @@ async def client(postgres_service: PostgresService): "database": postgres_service.database, } ) - db = spec.add_config(config) + spec.add_config(config) sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) app = Litestar(route_handlers=[health_check, stats, get_user], plugins=[sqlspec_plugin], debug=True) @@ -56,6 +58,6 @@ async def client(postgres_service: PostgresService): yield client -async def test_stub(client: AsyncTestClient): +async def test_stub(client: AsyncTestClient) -> None: response = await client.get("/stats") assert response == {"status": "healthy"} diff --git a/docs/examples/usage/usage_framework_integrations_20.py b/docs/examples/usage/usage_framework_integrations_20.py index a11fef97..8a3ae5d3 100644 --- a/docs/examples/usage/usage_framework_integrations_20.py +++ b/docs/examples/usage/usage_framework_integrations_20.py @@ -1,8 +1,11 @@ # start-example -import asyncio from contextvars import ContextVar -db_session: ContextVar = ContextVar('db_session', default=None) +__all__ = ("cleanup_session", "get_session", "test_stub" ) + + +db_session: ContextVar = ContextVar("db_session", default=None) + async def get_session(): session = db_session.get() @@ -11,12 +14,16 @@ async def get_session(): db_session.set(session) return session -async def cleanup_session(): + +async def cleanup_session() -> None: session = db_session.get() if session: await session.__aexit__(None, None, None) db_session.set(None) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_21.py b/docs/examples/usage/usage_framework_integrations_21.py index 0d6c833f..628ba935 100644 --- a/docs/examples/usage/usage_framework_integrations_21.py +++ b/docs/examples/usage/usage_framework_integrations_21.py @@ -1,4 +1,7 @@ # start-example +__all__ = ("Database", "example_usage", "test_stub" ) + + class Database: _instance = None _spec = None @@ -8,20 +11,24 @@ def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._spec = SQLSpec() - cls._config = cls._spec.add_config( - AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}) - ) + cls._config = cls._spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) return cls._instance async def session(self): return self._spec.provide_session(self._config) + # Usage db = Database() -async def example_usage(): + + +async def example_usage() -> None: async with await db.session() as session: - result = await session.execute("SELECT * FROM users") + await session.execute("SELECT * FROM users") + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_22.py b/docs/examples/usage/usage_framework_integrations_22.py index 4710213b..425e00ae 100644 --- a/docs/examples/usage/usage_framework_integrations_22.py +++ b/docs/examples/usage/usage_framework_integrations_22.py @@ -1,9 +1,13 @@ # start-example +__all__ = ("test_stub", ) + + # Prefer Litestar plugin over manual setup spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py index 7143b57f..aa5a6b48 100644 --- a/docs/examples/usage/usage_framework_integrations_23.py +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -1,15 +1,22 @@ # start-example +__all__ = ("close_pools", "lifespan", "test_stub" ) + + # FastAPI @asynccontextmanager async def lifespan(app: FastAPI): yield await spec.close_all_pools() + # Sanic @app.before_server_stop -async def close_pools(app, loop): +async def close_pools(app, loop) -> None: await spec.close_all_pools() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_24.py b/docs/examples/usage/usage_framework_integrations_24.py index 2f30d8ea..fa31dce9 100644 --- a/docs/examples/usage/usage_framework_integrations_24.py +++ b/docs/examples/usage/usage_framework_integrations_24.py @@ -1,9 +1,15 @@ # start-example +__all__ = ("get_db", "test_stub" ) + + # Inject sessions, not global instances async def get_db(): async with spec.provide_session(config) as session: yield session + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_25.py b/docs/examples/usage/usage_framework_integrations_25.py index e350e011..97aef68e 100644 --- a/docs/examples/usage/usage_framework_integrations_25.py +++ b/docs/examples/usage/usage_framework_integrations_25.py @@ -1,21 +1,23 @@ # start-example +__all__ = ("manual_transaction", "test_stub" ) + + # Use autocommit for simple CRUD spec = SQLSpec() db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit"} - } - ) + AsyncpgConfig(pool_config={"dsn": "postgresql://..."}, extension_config={"litestar": {"commit_mode": "autocommit"}}) ) + # Manual transactions for complex operations -async def manual_transaction(db_session): +async def manual_transaction(db_session) -> None: async with db_session.begin_transaction(): # Multiple operations pass + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py index 3d034aaa..57f0f9bd 100644 --- a/docs/examples/usage/usage_framework_integrations_26.py +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -1,25 +1,26 @@ # start-example +__all__ = ("UserRepository", "get_user", "test_stub" ) + + # Good: Separate repository layer class UserRepository: - def __init__(self, db: AsyncDriverAdapterBase): + def __init__(self, db: AsyncDriverAdapterBase) -> None: self.db = db async def get_user(self, user_id: int): - result = await self.db.execute( - "SELECT * FROM users WHERE id = $1", - user_id - ) + result = await self.db.execute("SELECT * FROM users WHERE id = $1", user_id) return result.one() + # Use in handlers @app.get("/users/{user_id}") -async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db) -): +async def get_user(user_id: int, db: AsyncDriverAdapterBase = Depends(get_db)): repo = UserRepository(db) return await repo.get_user(user_id) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_27.py b/docs/examples/usage/usage_framework_integrations_27.py index f0404136..ee6742bc 100644 --- a/docs/examples/usage/usage_framework_integrations_27.py +++ b/docs/examples/usage/usage_framework_integrations_27.py @@ -1,7 +1,11 @@ # start-example import pytest + from sqlspec.adapters.sqlite import SqliteConfig +__all__ = ("test_create_user", "test_db" ) + + @pytest.fixture async def test_db(): spec = SQLSpec() @@ -17,10 +21,10 @@ async def test_db(): """) yield session -async def test_create_user(test_db): - result = await test_db.execute( - "INSERT INTO users (name) VALUES ($1) RETURNING id", - "Test User" - ) + +async def test_create_user(test_db) -> None: + result = await test_db.execute("INSERT INTO users (name) VALUES ($1) RETURNING id", "Test User") assert result.scalar() == 1 + + # end-example diff --git a/docs/examples/usage/usage_framework_integrations_3.py b/docs/examples/usage/usage_framework_integrations_3.py index a8793315..e61d16e7 100644 --- a/docs/examples/usage/usage_framework_integrations_3.py +++ b/docs/examples/usage/usage_framework_integrations_3.py @@ -1,32 +1,35 @@ # start-example from litestar import post + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase +__all__ = ("create_user", "test_stub" ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, extension_config={ "litestar": {"commit_mode": "manual"} # Default - } + }, ) ) + @post("/users") -async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase -) -> dict: +async def create_user(data: dict, db_session: AsyncDriverAdapterBase) -> dict: async with db_session.begin_transaction(): result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", data["name"], data["email"] ) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_4.py b/docs/examples/usage/usage_framework_integrations_4.py index 2f2e8bfb..8216206a 100644 --- a/docs/examples/usage/usage_framework_integrations_4.py +++ b/docs/examples/usage/usage_framework_integrations_4.py @@ -1,28 +1,30 @@ # start-example +__all__ = ("create_user", "test_stub" ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, extension_config={ "litestar": {"commit_mode": "autocommit"} # Auto-commit on 2xx - } + }, ) ) + @post("/users") -async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase -) -> dict: +async def create_user(data: dict, db_session: AsyncDriverAdapterBase) -> dict: # Transaction begins automatically result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", data["name"], data["email"] ) # Commits automatically on success return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_5.py b/docs/examples/usage/usage_framework_integrations_5.py index d79d5bed..55352e5a 100644 --- a/docs/examples/usage/usage_framework_integrations_5.py +++ b/docs/examples/usage/usage_framework_integrations_5.py @@ -1,14 +1,16 @@ # start-example +__all__ = ("test_stub", ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit_include_redirect"} - } + extension_config={"litestar": {"commit_mode": "autocommit_include_redirect"}}, ) ) # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_6.py b/docs/examples/usage/usage_framework_integrations_6.py index 39fd00d6..0e849476 100644 --- a/docs/examples/usage/usage_framework_integrations_6.py +++ b/docs/examples/usage/usage_framework_integrations_6.py @@ -3,25 +3,32 @@ from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase +__all__ = ("list_users", "test_stub" ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, extension_config={ "litestar": { - "connection_key": "database", # Default: "db_connection" - "pool_key": "db_pool", # Default: "db_pool" - "session_key": "session", # Default: "db_session" + "connection_key": "database", # Default: "db_connection" + "pool_key": "db_pool", # Default: "db_pool" + "session_key": "session", # Default: "db_session" } - } + }, ) ) + @get("/users") async def list_users(session: AsyncDriverAdapterBase) -> list: result = await session.execute("SELECT * FROM users") return result.all() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_7.py b/docs/examples/usage/usage_framework_integrations_7.py index f79bebb4..c8bf08f7 100644 --- a/docs/examples/usage/usage_framework_integrations_7.py +++ b/docs/examples/usage/usage_framework_integrations_7.py @@ -4,18 +4,16 @@ from sqlspec.driver import AsyncDriverAdapterBase from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("generate_report", "test_stub" ) + + spec = SQLSpec() # Main database main_db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/main"}, - extension_config={ - "litestar": { - "session_key": "main_db", - "connection_key": "main_db_connection", - } - } + extension_config={"litestar": {"session_key": "main_db", "connection_key": "main_db_connection"}}, ) ) @@ -23,33 +21,24 @@ analytics_db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/analytics"}, - extension_config={ - "litestar": { - "session_key": "analytics_db", - "connection_key": "analytics_connection", - } - } + extension_config={"litestar": {"session_key": "analytics_db", "connection_key": "analytics_connection"}}, ) ) # Create single plugin with all configs -app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)] -) +app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) + # Use in handlers @get("/report") -async def generate_report( - main_db: AsyncDriverAdapterBase, - analytics_db: AsyncDriverAdapterBase -) -> dict: +async def generate_report(main_db: AsyncDriverAdapterBase, analytics_db: AsyncDriverAdapterBase) -> dict: users = await main_db.execute("SELECT COUNT(*) FROM users") events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "total_users": users.scalar(), - "total_events": events.scalar() - } + return {"total_users": users.scalar(), "total_events": events.scalar()} + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_8.py b/docs/examples/usage/usage_framework_integrations_8.py index d5a4fe07..4cf72d93 100644 --- a/docs/examples/usage/usage_framework_integrations_8.py +++ b/docs/examples/usage/usage_framework_integrations_8.py @@ -1,23 +1,22 @@ # start-example from litestar import Litestar from litestar.middleware.session.server_side import ServerSideSessionConfig + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.adapters.asyncpg.litestar import AsyncpgStore from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("test_stub", ) + + # Configure database with session support spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": {"session_table": "litestar_sessions"} - }, - migration_config={ - "script_location": "migrations", - "include_extensions": ["litestar"] - } + extension_config={"litestar": {"session_table": "litestar_sessions"}}, + migration_config={"script_location": "migrations", "include_extensions": ["litestar"]}, ) ) @@ -25,13 +24,9 @@ store = AsyncpgStore(db) # Configure Litestar with plugin and session middleware -app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)], - middleware=[ - ServerSideSessionConfig(store=store).middleware - ] -) +app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)], middleware=[ServerSideSessionConfig(store=store).middleware]) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py index 77d0a8ea..14ce7dee 100644 --- a/docs/examples/usage/usage_framework_integrations_9.py +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -2,6 +2,9 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig +__all__ = ("test_stub", ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( @@ -12,13 +15,14 @@ "correlation_header": "x-request-id", "correlation_headers": ["x-client-trace"], "auto_trace_headers": True, - } - } + } + }, ) ) # Queries will include correlation IDs in logs (header or generated UUID) # Format: [correlation_id=abc123] SELECT * FROM users # end-example -def test_stub(): + +def test_stub() -> None: assert True From d0dad97b0834c450598de0be85b7cb2d6a2479f1 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 05:29:51 +0100 Subject: [PATCH 03/13] 1st pass, tests are shit atm --- .../usage/usage_framework_integrations_1.py | 25 + .../usage/usage_framework_integrations_10.py | 32 + .../usage/usage_framework_integrations_11.py | 22 + .../usage/usage_framework_integrations_12.py | 27 + .../usage/usage_framework_integrations_13.py | 32 + .../usage/usage_framework_integrations_14.py | 23 + .../usage/usage_framework_integrations_15.py | 13 + .../usage/usage_framework_integrations_16.py | 21 + .../usage/usage_framework_integrations_17.py | 14 + .../usage/usage_framework_integrations_18.py | 22 + .../usage/usage_framework_integrations_19.py | 23 + .../usage/usage_framework_integrations_2.py | 61 ++ .../usage/usage_framework_integrations_20.py | 22 + .../usage/usage_framework_integrations_21.py | 27 + .../usage/usage_framework_integrations_22.py | 9 + .../usage/usage_framework_integrations_23.py | 15 + .../usage/usage_framework_integrations_24.py | 9 + .../usage/usage_framework_integrations_25.py | 21 + .../usage/usage_framework_integrations_26.py | 25 + .../usage/usage_framework_integrations_27.py | 26 + .../usage/usage_framework_integrations_3.py | 32 + .../usage/usage_framework_integrations_4.py | 28 + .../usage/usage_framework_integrations_5.py | 14 + .../usage/usage_framework_integrations_6.py | 27 + .../usage/usage_framework_integrations_7.py | 55 ++ .../usage/usage_framework_integrations_8.py | 37 + .../usage/usage_framework_integrations_9.py | 24 + docs/usage/framework_integrations.rst | 710 ++++-------------- 28 files changed, 832 insertions(+), 564 deletions(-) create mode 100644 docs/examples/usage/usage_framework_integrations_1.py create mode 100644 docs/examples/usage/usage_framework_integrations_10.py create mode 100644 docs/examples/usage/usage_framework_integrations_11.py create mode 100644 docs/examples/usage/usage_framework_integrations_12.py create mode 100644 docs/examples/usage/usage_framework_integrations_13.py create mode 100644 docs/examples/usage/usage_framework_integrations_14.py create mode 100644 docs/examples/usage/usage_framework_integrations_15.py create mode 100644 docs/examples/usage/usage_framework_integrations_16.py create mode 100644 docs/examples/usage/usage_framework_integrations_17.py create mode 100644 docs/examples/usage/usage_framework_integrations_18.py create mode 100644 docs/examples/usage/usage_framework_integrations_19.py create mode 100644 docs/examples/usage/usage_framework_integrations_2.py create mode 100644 docs/examples/usage/usage_framework_integrations_20.py create mode 100644 docs/examples/usage/usage_framework_integrations_21.py create mode 100644 docs/examples/usage/usage_framework_integrations_22.py create mode 100644 docs/examples/usage/usage_framework_integrations_23.py create mode 100644 docs/examples/usage/usage_framework_integrations_24.py create mode 100644 docs/examples/usage/usage_framework_integrations_25.py create mode 100644 docs/examples/usage/usage_framework_integrations_26.py create mode 100644 docs/examples/usage/usage_framework_integrations_27.py create mode 100644 docs/examples/usage/usage_framework_integrations_3.py create mode 100644 docs/examples/usage/usage_framework_integrations_4.py create mode 100644 docs/examples/usage/usage_framework_integrations_5.py create mode 100644 docs/examples/usage/usage_framework_integrations_6.py create mode 100644 docs/examples/usage/usage_framework_integrations_7.py create mode 100644 docs/examples/usage/usage_framework_integrations_8.py create mode 100644 docs/examples/usage/usage_framework_integrations_9.py diff --git a/docs/examples/usage/usage_framework_integrations_1.py b/docs/examples/usage/usage_framework_integrations_1.py new file mode 100644 index 00000000..93c11135 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_1.py @@ -0,0 +1,25 @@ +# start-example +from litestar import Litestar, get + +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.extensions.litestar import SQLSpecPlugin + +# Configure database and create plugin +spec = SQLSpec() +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) +sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + + +@get("/") +async def index() -> str: + return "integrated" + + +# Create Litestar app +app = Litestar(route_handlers=[index], plugins=[sqlspec_plugin]) +# end-example + + +def test_app_exists() -> None: + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_10.py b/docs/examples/usage/usage_framework_integrations_10.py new file mode 100644 index 00000000..d3077f0f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_10.py @@ -0,0 +1,32 @@ +# start-example +from fastapi import FastAPI, Depends +from contextlib import asynccontextmanager +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase + +# Configure database +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={ + "dsn": "postgresql://localhost/mydb", + "min_size": 10, + "max_size": 20, + } + ) +) + +# Lifespan context manager +@asynccontextmanager +async def lifespan(app: FastAPI): + # Startup + yield + # Shutdown + await spec.close_all_pools() + +app = FastAPI(lifespan=lifespan) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_11.py b/docs/examples/usage/usage_framework_integrations_11.py new file mode 100644 index 00000000..67ffbe11 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_11.py @@ -0,0 +1,22 @@ +# start-example +from typing import AsyncGenerator + +async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: + async with spec.provide_session(config) as session: + yield session + +# Use in route handlers +@app.get("/users/{user_id}") +async def get_user( + user_id: int, + db: AsyncDriverAdapterBase = Depends(get_db_session) +) -> dict: + result = await db.execute( + "SELECT id, name, email FROM users WHERE id = $1", + user_id + ) + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_12.py b/docs/examples/usage/usage_framework_integrations_12.py new file mode 100644 index 00000000..3f106f85 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_12.py @@ -0,0 +1,27 @@ +# start-example +@app.post("/users") +async def create_user( + user_data: dict, + db: AsyncDriverAdapterBase = Depends(get_db_session) +) -> dict: + async with db.begin_transaction(): + result = await db.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", + user_data["name"], + user_data["email"] + ) + + user_id = result.scalar() + + # Additional operations in same transaction + await db.execute( + "INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", + "user_created", + user_id + ) + + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_13.py b/docs/examples/usage/usage_framework_integrations_13.py new file mode 100644 index 00000000..69a80f24 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_13.py @@ -0,0 +1,32 @@ +# start-example +# Main database +main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) + +# Analytics database +analytics_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/analytics"})) + +# Dependency functions +async def get_main_db(): + async with spec.provide_session(main_db) as session: + yield session + +async def get_analytics_db(): + async with spec.provide_session(analytics_db) as session: + yield session + +# Use in handlers +@app.get("/report") +async def generate_report( + main_db: AsyncDriverAdapterBase = Depends(get_main_db), + analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db) +) -> dict: + users = await main_db.execute("SELECT COUNT(*) FROM users") + events = await analytics_db.execute("SELECT COUNT(*) FROM events") + return { + "users": users.scalar(), + "events": events.scalar() + } +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_14.py b/docs/examples/usage/usage_framework_integrations_14.py new file mode 100644 index 00000000..dfd1667f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_14.py @@ -0,0 +1,23 @@ +# start-example +from sanic import Sanic, Request, json +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + +app = Sanic("MyApp") + +# Initialize SQLSpec +spec = SQLSpec() +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) + +# Store in app context +app.ctx.sqlspec = spec +app.ctx.db_config = db + +# Cleanup on shutdown +@app.before_server_stop +async def close_db(app, loop): + await app.ctx.sqlspec.close_all_pools() +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_15.py b/docs/examples/usage/usage_framework_integrations_15.py new file mode 100644 index 00000000..8572782f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_15.py @@ -0,0 +1,13 @@ +# start-example +@app.get("/users/") +async def get_user(request: Request, user_id: int): + async with request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config) as db: + result = await db.execute( + "SELECT id, name, email FROM users WHERE id = $1", + user_id + ) + return json(result.one()) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_16.py b/docs/examples/usage/usage_framework_integrations_16.py new file mode 100644 index 00000000..f2bcdb57 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_16.py @@ -0,0 +1,21 @@ +# start-example +@app.middleware("request") +async def add_db_session(request): + request.ctx.db = await request.app.ctx.sqlspec.provide_session( + request.app.ctx.db_config + ).__aenter__() + +@app.middleware("response") +async def cleanup_db_session(request, response): + if hasattr(request.ctx, "db"): + await request.ctx.db.__aexit__(None, None, None) + +# Use in handlers +@app.get("/users") +async def list_users(request: Request): + result = await request.ctx.db.execute("SELECT * FROM users") + return json(result.rows) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_17.py b/docs/examples/usage/usage_framework_integrations_17.py new file mode 100644 index 00000000..0856646f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_17.py @@ -0,0 +1,14 @@ +# start-example +from flask import Flask, g +from sqlspec import SQLSpec +from sqlspec.adapters.sqlite import SqliteConfig + +app = Flask(__name__) + +# Initialize SQLSpec +spec = SQLSpec() +db = spec.add_config(SqliteConfig(pool_config={"database": "app.db"})) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_18.py b/docs/examples/usage/usage_framework_integrations_18.py new file mode 100644 index 00000000..b982203d --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_18.py @@ -0,0 +1,22 @@ +# start-example +def get_db(): + if 'db' not in g: + g.db = spec.provide_session(db).__enter__() + return g.db + +@app.teardown_appcontext +def close_db(error): + db = g.pop('db', None) + if db is not None: + db.__exit__(None, None, None) + +# Use in routes +@app.route('/users/') +def get_user(user_id): + db = get_db() + result = db.execute("SELECT * FROM users WHERE id = ?", user_id) + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_19.py b/docs/examples/usage/usage_framework_integrations_19.py new file mode 100644 index 00000000..315137ce --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_19.py @@ -0,0 +1,23 @@ +# start-example +class DatabaseSession: + def __init__(self, spec: SQLSpec, config): + self.spec = spec + self.config = config + self.session = None + + async def __aenter__(self): + self.session = await self.spec.provide_session(self.config).__aenter__() + return self.session + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self.session: + await self.session.__aexit__(exc_type, exc_val, exc_tb) + +# Usage +async def example_usage(): + async with DatabaseSession(spec, config) as db: + result = await db.execute("SELECT * FROM users") +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_2.py b/docs/examples/usage/usage_framework_integrations_2.py new file mode 100644 index 00000000..d04d0182 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_2.py @@ -0,0 +1,61 @@ +# start-example +from typing import Any + +import pytest +from asyncpg import Connection, Pool +from litestar import Litestar, get +from litestar.testing import AsyncTestClient +from pytest_databases.docker.postgres import PostgresService + +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase +from sqlspec.extensions.litestar import SQLSpecPlugin + + +# Inject database session +@get("/users/{user_id:int}") +async def get_user(user_id: int, db_session: AsyncDriverAdapterBase) -> dict: + result = await db_session.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) + return result.one() + + +# Inject connection pool +@get("/health") +async def health_check(db_pool: Pool) -> dict[str, Any]: + async with db_pool.acquire() as conn: + result = await conn.fetchval("SELECT 1") + return {"status": "healthy" if result == 1 else "unhealthy"} + + +# Inject raw connection +@get("/stats") +async def stats(db_connection: Connection) -> dict[str, Any]: + result = await db_connection.fetchval("SELECT COUNT(*) FROM users") + return {"user_count": result} + + +# end-example +@pytest.fixture +async def client(postgres_service: PostgresService): + spec = SQLSpec() + config = AsyncpgConfig( + pool_config={ + "host": postgres_service.host, + "port": postgres_service.port, + "user": postgres_service.user, + "password": postgres_service.password, + "database": postgres_service.database, + } + ) + db = spec.add_config(config) + + sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + app = Litestar(route_handlers=[health_check, stats, get_user], plugins=[sqlspec_plugin], debug=True) + async with AsyncTestClient(app) as client: + yield client + + +async def test_stub(client: AsyncTestClient): + response = await client.get("/stats") + assert response == {"status": "healthy"} diff --git a/docs/examples/usage/usage_framework_integrations_20.py b/docs/examples/usage/usage_framework_integrations_20.py new file mode 100644 index 00000000..a11fef97 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_20.py @@ -0,0 +1,22 @@ +# start-example +import asyncio +from contextvars import ContextVar + +db_session: ContextVar = ContextVar('db_session', default=None) + +async def get_session(): + session = db_session.get() + if session is None: + session = await spec.provide_session(config).__aenter__() + db_session.set(session) + return session + +async def cleanup_session(): + session = db_session.get() + if session: + await session.__aexit__(None, None, None) + db_session.set(None) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_21.py b/docs/examples/usage/usage_framework_integrations_21.py new file mode 100644 index 00000000..0d6c833f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_21.py @@ -0,0 +1,27 @@ +# start-example +class Database: + _instance = None + _spec = None + _config = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._spec = SQLSpec() + cls._config = cls._spec.add_config( + AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}) + ) + return cls._instance + + async def session(self): + return self._spec.provide_session(self._config) + +# Usage +db = Database() +async def example_usage(): + async with await db.session() as session: + result = await session.execute("SELECT * FROM users") +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_22.py b/docs/examples/usage/usage_framework_integrations_22.py new file mode 100644 index 00000000..4710213b --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_22.py @@ -0,0 +1,9 @@ +# start-example +# Prefer Litestar plugin over manual setup +spec = SQLSpec() +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) +app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py new file mode 100644 index 00000000..7143b57f --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -0,0 +1,15 @@ +# start-example +# FastAPI +@asynccontextmanager +async def lifespan(app: FastAPI): + yield + await spec.close_all_pools() + +# Sanic +@app.before_server_stop +async def close_pools(app, loop): + await spec.close_all_pools() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_24.py b/docs/examples/usage/usage_framework_integrations_24.py new file mode 100644 index 00000000..2f30d8ea --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_24.py @@ -0,0 +1,9 @@ +# start-example +# Inject sessions, not global instances +async def get_db(): + async with spec.provide_session(config) as session: + yield session +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_25.py b/docs/examples/usage/usage_framework_integrations_25.py new file mode 100644 index 00000000..e350e011 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_25.py @@ -0,0 +1,21 @@ +# start-example +# Use autocommit for simple CRUD +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "autocommit"} + } + ) +) + +# Manual transactions for complex operations +async def manual_transaction(db_session): + async with db_session.begin_transaction(): + # Multiple operations + pass +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py new file mode 100644 index 00000000..3d034aaa --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -0,0 +1,25 @@ +# start-example +# Good: Separate repository layer +class UserRepository: + def __init__(self, db: AsyncDriverAdapterBase): + self.db = db + + async def get_user(self, user_id: int): + result = await self.db.execute( + "SELECT * FROM users WHERE id = $1", + user_id + ) + return result.one() + +# Use in handlers +@app.get("/users/{user_id}") +async def get_user( + user_id: int, + db: AsyncDriverAdapterBase = Depends(get_db) +): + repo = UserRepository(db) + return await repo.get_user(user_id) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_27.py b/docs/examples/usage/usage_framework_integrations_27.py new file mode 100644 index 00000000..f0404136 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_27.py @@ -0,0 +1,26 @@ +# start-example +import pytest +from sqlspec.adapters.sqlite import SqliteConfig + +@pytest.fixture +async def test_db(): + spec = SQLSpec() + db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) + + async with spec.provide_session(db) as session: + # Set up test schema + await session.execute(""" + CREATE TABLE users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL + ) + """) + yield session + +async def test_create_user(test_db): + result = await test_db.execute( + "INSERT INTO users (name) VALUES ($1) RETURNING id", + "Test User" + ) + assert result.scalar() == 1 +# end-example diff --git a/docs/examples/usage/usage_framework_integrations_3.py b/docs/examples/usage/usage_framework_integrations_3.py new file mode 100644 index 00000000..a8793315 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_3.py @@ -0,0 +1,32 @@ +# start-example +from litestar import post +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase + +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "manual"} # Default + } + ) +) + +@post("/users") +async def create_user( + data: dict, + db_session: AsyncDriverAdapterBase +) -> dict: + async with db_session.begin_transaction(): + result = await db_session.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", + data["name"], + data["email"] + ) + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_4.py b/docs/examples/usage/usage_framework_integrations_4.py new file mode 100644 index 00000000..2f2e8bfb --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_4.py @@ -0,0 +1,28 @@ +# start-example +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "autocommit"} # Auto-commit on 2xx + } + ) +) + +@post("/users") +async def create_user( + data: dict, + db_session: AsyncDriverAdapterBase +) -> dict: + # Transaction begins automatically + result = await db_session.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", + data["name"], + data["email"] + ) + # Commits automatically on success + return result.one() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_5.py b/docs/examples/usage/usage_framework_integrations_5.py new file mode 100644 index 00000000..d79d5bed --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_5.py @@ -0,0 +1,14 @@ +# start-example +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": {"commit_mode": "autocommit_include_redirect"} + } + ) +) +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_6.py b/docs/examples/usage/usage_framework_integrations_6.py new file mode 100644 index 00000000..39fd00d6 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_6.py @@ -0,0 +1,27 @@ +# start-example +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase + +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": { + "connection_key": "database", # Default: "db_connection" + "pool_key": "db_pool", # Default: "db_pool" + "session_key": "session", # Default: "db_session" + } + } + ) +) + +@get("/users") +async def list_users(session: AsyncDriverAdapterBase) -> list: + result = await session.execute("SELECT * FROM users") + return result.all() +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_7.py b/docs/examples/usage/usage_framework_integrations_7.py new file mode 100644 index 00000000..f79bebb4 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_7.py @@ -0,0 +1,55 @@ +# start-example +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.driver import AsyncDriverAdapterBase +from sqlspec.extensions.litestar import SQLSpecPlugin + +spec = SQLSpec() + +# Main database +main_db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/main"}, + extension_config={ + "litestar": { + "session_key": "main_db", + "connection_key": "main_db_connection", + } + } + ) +) + +# Analytics database +analytics_db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/analytics"}, + extension_config={ + "litestar": { + "session_key": "analytics_db", + "connection_key": "analytics_connection", + } + } + ) +) + +# Create single plugin with all configs +app = Litestar( + plugins=[SQLSpecPlugin(sqlspec=spec)] +) + +# Use in handlers +@get("/report") +async def generate_report( + main_db: AsyncDriverAdapterBase, + analytics_db: AsyncDriverAdapterBase +) -> dict: + users = await main_db.execute("SELECT COUNT(*) FROM users") + events = await analytics_db.execute("SELECT COUNT(*) FROM events") + return { + "total_users": users.scalar(), + "total_events": events.scalar() + } +# end-example + +def test_stub(): + assert True diff --git a/docs/examples/usage/usage_framework_integrations_8.py b/docs/examples/usage/usage_framework_integrations_8.py new file mode 100644 index 00000000..d5a4fe07 --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_8.py @@ -0,0 +1,37 @@ +# start-example +from litestar import Litestar +from litestar.middleware.session.server_side import ServerSideSessionConfig +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.adapters.asyncpg.litestar import AsyncpgStore +from sqlspec.extensions.litestar import SQLSpecPlugin + +# Configure database with session support +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + extension_config={ + "litestar": {"session_table": "litestar_sessions"} + }, + migration_config={ + "script_location": "migrations", + "include_extensions": ["litestar"] + } + ) +) + +# Create session store using adapter-specific class +store = AsyncpgStore(db) + +# Configure Litestar with plugin and session middleware +app = Litestar( + plugins=[SQLSpecPlugin(sqlspec=spec)], + middleware=[ + ServerSideSessionConfig(store=store).middleware + ] +) +# end-example + +def test_stub(): + assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py new file mode 100644 index 00000000..77d0a8ea --- /dev/null +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -0,0 +1,24 @@ +# start-example +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + +spec = SQLSpec() +db = spec.add_config( + AsyncpgConfig( + pool_config={"dsn": "postgresql://..."}, + extension_config={ + "litestar": { + "enable_correlation_middleware": True, # Default: True + "correlation_header": "x-request-id", + "correlation_headers": ["x-client-trace"], + "auto_trace_headers": True, + } + } + ) +) +# Queries will include correlation IDs in logs (header or generated UUID) +# Format: [correlation_id=abc123] SELECT * FROM users +# end-example + +def test_stub(): + assert True diff --git a/docs/usage/framework_integrations.rst b/docs/usage/framework_integrations.rst index 5860fd6f..443e3c80 100644 --- a/docs/usage/framework_integrations.rst +++ b/docs/usage/framework_integrations.rst @@ -23,66 +23,24 @@ The Litestar plugin provides first-class integration with comprehensive features Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from litestar import Litestar, get - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.extensions.litestar import SQLSpecPlugin - - # Configure database and create plugin - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/mydb", - "min_size": 10, - "max_size": 20, - } - ) - ) - sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) - - # Create Litestar app - app = Litestar( - route_handlers=[...], - plugins=[sqlspec_plugin] - ) +.. literalinclude:: ../examples/usage/usage_framework_integrations_1.py + :language: python + :dedent: 0 + :start-adter: # start-example + :end-before: # end-example + Using Dependency Injection ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The plugin provides dependency injection for connections, pools, and sessions: -.. code-block:: python - - from litestar import get, post - from sqlspec.driver import AsyncDriverAdapterBase - - # Inject database session - @get("/users/{user_id:int}") - async def get_user( - user_id: int, - db_session: AsyncDriverAdapterBase - ) -> dict: - result = await db_session.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) - return result.one() - - # Inject connection pool - @get("/health") - async def health_check(db_pool) -> dict: - async with db_pool.acquire() as conn: - result = await conn.fetchval("SELECT 1") - return {"status": "healthy" if result == 1 else "unhealthy"} - - # Inject raw connection - @get("/stats") - async def stats(db_connection) -> dict: - result = await db_connection.fetchval("SELECT COUNT(*) FROM users") - return {"user_count": result} +.. literalinclude:: ../examples/usage/usage_framework_integrations_2.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Commit Modes ^^^^^^^^^^^^ @@ -93,209 +51,70 @@ The plugin supports different transaction commit strategies configured via ``ext You control transaction boundaries explicitly: -.. code-block:: python - - from litestar import post - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "manual"} # Default - } - ) - ) - - @post("/users") - async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase - ) -> dict: - async with db_session.begin_transaction(): - result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] - ) - return result.one() +.. literalinclude:: ../examples/usage/usage_framework_integrations_3.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + **Autocommit Mode** Automatically commits on successful requests (2xx responses): -.. code-block:: python - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit"} # Auto-commit on 2xx - } - ) - ) - - @post("/users") - async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase - ) -> dict: - # Transaction begins automatically - result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] - ) - # Commits automatically on success - return result.one() +.. literalinclude:: ../examples/usage/usage_framework_integrations_4.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + **Autocommit with Redirects** Commits on both 2xx and 3xx responses: -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_5.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit_include_redirect"} - } - ) - ) Custom Dependency Keys ^^^^^^^^^^^^^^^^^^^^^^ Customize the dependency injection keys via ``extension_config``: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": { - "connection_key": "database", # Default: "db_connection" - "pool_key": "db_pool", # Default: "db_pool" - "session_key": "session", # Default: "db_session" - } - } - ) - ) - - @get("/users") - async def list_users(session: AsyncDriverAdapterBase) -> list: - result = await session.execute("SELECT * FROM users") - return result.all() +.. literalinclude:: ../examples/usage/usage_framework_integrations_6.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Multiple Database Configurations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The plugin supports multiple database configurations through a single SQLSpec instance: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - from sqlspec.extensions.litestar import SQLSpecPlugin - - spec = SQLSpec() - - # Main database - main_db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/main"}, - extension_config={ - "litestar": { - "session_key": "main_db", - "connection_key": "main_db_connection", - } - } - ) - ) - - # Analytics database - analytics_db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/analytics"}, - extension_config={ - "litestar": { - "session_key": "analytics_db", - "connection_key": "analytics_connection", - } - } - ) - ) - - # Create single plugin with all configs - app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)] - ) - - # Use in handlers - @get("/report") - async def generate_report( - main_db: AsyncDriverAdapterBase, - analytics_db: AsyncDriverAdapterBase - ) -> dict: - users = await main_db.execute("SELECT COUNT(*) FROM users") - events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "total_users": users.scalar(), - "total_events": events.scalar() - } +.. literalinclude:: ../examples/usage/usage_framework_integrations_7.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Session Storage Backend ^^^^^^^^^^^^^^^^^^^^^^^ Use SQLSpec as a session backend for Litestar: -.. code-block:: python - - from litestar import Litestar - from litestar.middleware.session.server_side import ServerSideSessionConfig - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.adapters.asyncpg.litestar import AsyncpgStore - from sqlspec.extensions.litestar import SQLSpecPlugin - - # Configure database with session support - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": {"session_table": "litestar_sessions"} - }, - migration_config={ - "script_location": "migrations", - "include_extensions": ["litestar"] - } - ) - ) - - # Create session store using adapter-specific class - store = AsyncpgStore(db) - - # Configure Litestar with plugin and session middleware - app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)], - middleware=[ - ServerSideSessionConfig(store=store).middleware - ] - ) +.. literalinclude:: ../examples/usage/usage_framework_integrations_8.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + CLI Integration ^^^^^^^^^^^^^^^ @@ -328,28 +147,12 @@ Correlation Middleware Enable request correlation tracking via ``extension_config``: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": { - "enable_correlation_middleware": True, # Default: True - "correlation_header": "x-request-id", - "correlation_headers": ["x-client-trace"], - "auto_trace_headers": True, - } - } - ) - ) - - # Queries will include correlation IDs in logs (header or generated UUID) - # Format: [correlation_id=abc123] SELECT * FROM users +.. literalinclude:: ../examples/usage/usage_framework_integrations_9.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + FastAPI Integration ------------------- @@ -359,125 +162,48 @@ While SQLSpec doesn't have a dedicated FastAPI plugin, integration is straightfo Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from fastapi import FastAPI, Depends - from contextlib import asynccontextmanager - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.driver import AsyncDriverAdapterBase - - # Configure database - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/mydb", - "min_size": 10, - "max_size": 20, - } - ) - ) - - # Lifespan context manager - @asynccontextmanager - async def lifespan(app: FastAPI): - # Startup - yield - # Shutdown - await spec.close_all_pools() - - app = FastAPI(lifespan=lifespan) +.. literalinclude:: ../examples/usage/usage_framework_integrations_10.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Dependency Injection ^^^^^^^^^^^^^^^^^^^^ Create a dependency function for database sessions: -.. code-block:: python - - from typing import AsyncGenerator - - async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: - async with spec.provide_session(config) as session: - yield session +.. literalinclude:: ../examples/usage/usage_framework_integrations_11.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Use in route handlers - @app.get("/users/{user_id}") - async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db_session) - ) -> dict: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) - return result.one() Transaction Management ^^^^^^^^^^^^^^^^^^^^^^ Implement transaction handling with FastAPI: -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_12.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - @app.post("/users") - async def create_user( - user_data: dict, - db: AsyncDriverAdapterBase = Depends(get_db_session) - ) -> dict: - async with db.begin_transaction(): - result = await db.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - user_data["name"], - user_data["email"] - ) - - user_id = result.scalar() - - # Additional operations in same transaction - await db.execute( - "INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", - "user_created", - user_id - ) - - return result.one() Multiple Databases ^^^^^^^^^^^^^^^^^^ Support multiple databases with different dependencies: -.. code-block:: python - - # Main database - main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) - - # Analytics database - analytics_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/analytics"})) +.. literalinclude:: ../examples/usage/usage_framework_integrations_13.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Dependency functions - async def get_main_db(): - async with spec.provide_session(main_db) as session: - yield session - - async def get_analytics_db(): - async with spec.provide_session(analytics_db) as session: - yield session - - # Use in handlers - @app.get("/report") - async def generate_report( - main_db: AsyncDriverAdapterBase = Depends(get_main_db), - analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db) - ) -> dict: - users = await main_db.execute("SELECT COUNT(*) FROM users") - events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "users": users.scalar(), - "events": events.scalar() - } Sanic Integration ----------------- @@ -487,62 +213,32 @@ Integrate SQLSpec with Sanic using listeners and app context. Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from sanic import Sanic, Request, json - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - - app = Sanic("MyApp") - - # Initialize SQLSpec - spec = SQLSpec() - db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) +.. literalinclude:: ../examples/usage/usage_framework_integrations_14.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Store in app context - app.ctx.sqlspec = spec - app.ctx.db_config = db - - # Cleanup on shutdown - @app.before_server_stop - async def close_db(app, loop): - await app.ctx.sqlspec.close_all_pools() Using in Route Handlers ^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_15.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - @app.get("/users/") - async def get_user(request: Request, user_id: int): - async with request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config) as db: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) - return json(result.one()) Middleware for Automatic Sessions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - @app.middleware("request") - async def add_db_session(request): - request.ctx.db = await request.app.ctx.sqlspec.provide_session( - request.app.ctx.db_config - ).__aenter__() - - @app.middleware("response") - async def cleanup_db_session(request, response): - if hasattr(request.ctx, "db"): - await request.ctx.db.__aexit__(None, None, None) +.. literalinclude:: ../examples/usage/usage_framework_integrations_16.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Use in handlers - @app.get("/users") - async def list_users(request: Request): - result = await request.ctx.db.execute("SELECT * FROM users") - return json(result.rows) Flask Integration ----------------- @@ -552,40 +248,22 @@ Integrate SQLSpec with Flask using synchronous drivers. Basic Setup ^^^^^^^^^^^ -.. code-block:: python - - from flask import Flask, g - from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig - - app = Flask(__name__) +.. literalinclude:: ../examples/usage/usage_framework_integrations_17.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Initialize SQLSpec - spec = SQLSpec() - db = spec.add_config(SqliteConfig(pool_config={"database": "app.db"})) Using Request Context ^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_18.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - def get_db(): - if 'db' not in g: - g.db = spec.provide_session(db).__enter__() - return g.db - - @app.teardown_appcontext - def close_db(error): - db = g.pop('db', None) - if db is not None: - db.__exit__(None, None, None) - - # Use in routes - @app.route('/users/') - def get_user(user_id): - db = get_db() - result = db.execute("SELECT * FROM users WHERE id = ?", user_id) - return result.one() Custom Integration Patterns ---------------------------- @@ -595,160 +273,84 @@ Context Manager Pattern For frameworks without built-in dependency injection: -.. code-block:: python - - class DatabaseSession: - def __init__(self, spec: SQLSpec, config): - self.spec = spec - self.config = config - self.session = None - - async def __aenter__(self): - self.session = await self.spec.provide_session(self.config).__aenter__() - return self.session - - async def __aexit__(self, exc_type, exc_val, exc_tb): - if self.session: - await self.session.__aexit__(exc_type, exc_val, exc_tb) +.. literalinclude:: ../examples/usage/usage_framework_integrations_19.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Usage - async with DatabaseSession(spec, config) as db: - result = await db.execute("SELECT * FROM users") Request-Scoped Sessions ^^^^^^^^^^^^^^^^^^^^^^^ Implement request-scoped database sessions: -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_20.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - import asyncio - from contextvars import ContextVar - - db_session: ContextVar = ContextVar('db_session', default=None) - - async def get_session(): - session = db_session.get() - if session is None: - session = await spec.provide_session(config).__aenter__() - db_session.set(session) - return session - - async def cleanup_session(): - session = db_session.get() - if session: - await session.__aexit__(None, None, None) - db_session.set(None) Singleton Pattern ^^^^^^^^^^^^^^^^^ For simple applications with a single database: -.. code-block:: python - - class Database: - _instance = None - _spec = None - _config = None - - def __new__(cls): - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._spec = SQLSpec() - cls._config = cls._spec.add_config( - AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}) - ) - return cls._instance +.. literalinclude:: ../examples/usage/usage_framework_integrations_21.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - async def session(self): - return self._spec.provide_session(self._config) - - # Usage - db = Database() - async with await db.session() as session: - result = await session.execute("SELECT * FROM users") Best Practices -------------- **1. Use Framework-Specific Plugins When Available** -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_22.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Prefer Litestar plugin over manual setup - spec = SQLSpec() - db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) - app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) **2. Always Clean Up Pools** -.. code-block:: python - - # FastAPI - @asynccontextmanager - async def lifespan(app: FastAPI): - yield - await spec.close_all_pools() +.. literalinclude:: ../examples/usage/usage_framework_integrations_23.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Sanic - @app.before_server_stop - async def close_pools(app, loop): - await spec.close_all_pools() **3. Use Dependency Injection** -.. code-block:: python +.. literalinclude:: ../examples/usage/usage_framework_integrations_24.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Inject sessions, not global instances - async def get_db(): - async with spec.provide_session(config) as session: - yield session **4. Handle Transactions Appropriately** -.. code-block:: python - - # Use autocommit for simple CRUD - spec = SQLSpec() - db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit"} - } - ) - ) +.. literalinclude:: ../examples/usage/usage_framework_integrations_25.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example - # Manual transactions for complex operations - async with db_session.begin_transaction(): - # Multiple operations - pass **5. Separate Database Logic** -.. code-block:: python - - # Good: Separate repository layer - class UserRepository: - def __init__(self, db: AsyncDriverAdapterBase): - self.db = db - - async def get_user(self, user_id: int): - result = await self.db.execute( - "SELECT * FROM users WHERE id = $1", - user_id - ) - return result.one() - - # Use in handlers - @app.get("/users/{user_id}") - async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db) - ): - repo = UserRepository(db) - return await repo.get_user(user_id) +.. literalinclude:: ../examples/usage/usage_framework_integrations_26.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Testing ------- @@ -756,32 +358,12 @@ Testing Testing with Framework Integration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - import pytest - from sqlspec.adapters.sqlite import SqliteConfig - - @pytest.fixture - async def test_db(): - spec = SQLSpec() - db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) - - async with spec.provide_session(db) as session: - # Set up test schema - await session.execute(""" - CREATE TABLE users ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL - ) - """) - yield session - - async def test_create_user(test_db): - result = await test_db.execute( - "INSERT INTO users (name) VALUES ($1) RETURNING id", - "Test User" - ) - assert result.scalar() == 1 +.. literalinclude:: ../examples/usage/usage_framework_integrations_27.py + :language: python + :dedent: 0 + :start-after: # start-example + :end-before: # end-example + Next Steps ---------- From 0872a38ce1c23c432bbe23ea1789163501c22966 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 05:35:37 +0100 Subject: [PATCH 04/13] lint --- .../usage/usage_framework_integrations_1.py | 3 ++ .../usage/usage_framework_integrations_10.py | 23 ++++++------ .../usage/usage_framework_integrations_11.py | 21 ++++++----- .../usage/usage_framework_integrations_12.py | 23 +++++------- .../usage/usage_framework_integrations_13.py | 18 ++++++--- .../usage/usage_framework_integrations_14.py | 14 +++++-- .../usage/usage_framework_integrations_15.py | 13 ++++--- .../usage/usage_framework_integrations_16.py | 18 ++++++--- .../usage/usage_framework_integrations_17.py | 9 ++++- .../usage/usage_framework_integrations_18.py | 18 ++++++--- .../usage/usage_framework_integrations_19.py | 15 ++++++-- .../usage/usage_framework_integrations_2.py | 6 ++- .../usage/usage_framework_integrations_20.py | 15 ++++++-- .../usage/usage_framework_integrations_21.py | 19 +++++++--- .../usage/usage_framework_integrations_22.py | 6 ++- .../usage/usage_framework_integrations_23.py | 11 +++++- .../usage/usage_framework_integrations_24.py | 8 +++- .../usage/usage_framework_integrations_25.py | 18 +++++---- .../usage/usage_framework_integrations_26.py | 21 ++++++----- .../usage/usage_framework_integrations_27.py | 14 ++++--- .../usage/usage_framework_integrations_3.py | 21 ++++++----- .../usage/usage_framework_integrations_4.py | 20 +++++----- .../usage/usage_framework_integrations_5.py | 10 +++-- .../usage/usage_framework_integrations_6.py | 17 ++++++--- .../usage/usage_framework_integrations_7.py | 37 +++++++------------ .../usage/usage_framework_integrations_8.py | 23 +++++------- .../usage/usage_framework_integrations_9.py | 10 +++-- 27 files changed, 258 insertions(+), 173 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_1.py b/docs/examples/usage/usage_framework_integrations_1.py index 93c11135..344a5a63 100644 --- a/docs/examples/usage/usage_framework_integrations_1.py +++ b/docs/examples/usage/usage_framework_integrations_1.py @@ -5,6 +5,9 @@ from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("index", "test_app_exists" ) + + # Configure database and create plugin spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) diff --git a/docs/examples/usage/usage_framework_integrations_10.py b/docs/examples/usage/usage_framework_integrations_10.py index d3077f0f..9455efec 100644 --- a/docs/examples/usage/usage_framework_integrations_10.py +++ b/docs/examples/usage/usage_framework_integrations_10.py @@ -1,21 +1,18 @@ # start-example -from fastapi import FastAPI, Depends from contextlib import asynccontextmanager + +from fastapi import FastAPI + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig -from sqlspec.driver import AsyncDriverAdapterBase + +__all__ = ("lifespan", "test_stub" ) + # Configure database spec = SQLSpec() -db = spec.add_config( - AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/mydb", - "min_size": 10, - "max_size": 20, - } - ) -) +db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) + # Lifespan context manager @asynccontextmanager @@ -25,8 +22,10 @@ async def lifespan(app: FastAPI): # Shutdown await spec.close_all_pools() + app = FastAPI(lifespan=lifespan) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_11.py b/docs/examples/usage/usage_framework_integrations_11.py index 67ffbe11..37d8f4ed 100644 --- a/docs/examples/usage/usage_framework_integrations_11.py +++ b/docs/examples/usage/usage_framework_integrations_11.py @@ -1,22 +1,23 @@ # start-example -from typing import AsyncGenerator +from collections.abc import AsyncGenerator + +__all__ = ("get_db_session", "get_user", "test_stub" ) + async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: async with spec.provide_session(config) as session: yield session + # Use in route handlers @app.get("/users/{user_id}") -async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db_session) -) -> dict: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) +async def get_user(user_id: int, db: AsyncDriverAdapterBase = Depends(get_db_session)) -> dict: + result = await db.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_12.py b/docs/examples/usage/usage_framework_integrations_12.py index 3f106f85..a819aef3 100644 --- a/docs/examples/usage/usage_framework_integrations_12.py +++ b/docs/examples/usage/usage_framework_integrations_12.py @@ -1,27 +1,24 @@ # start-example +__all__ = ("create_user", "test_stub" ) + + @app.post("/users") -async def create_user( - user_data: dict, - db: AsyncDriverAdapterBase = Depends(get_db_session) -) -> dict: +async def create_user(user_data: dict, db: AsyncDriverAdapterBase = Depends(get_db_session)) -> dict: async with db.begin_transaction(): result = await db.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - user_data["name"], - user_data["email"] + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", user_data["name"], user_data["email"] ) user_id = result.scalar() # Additional operations in same transaction - await db.execute( - "INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", - "user_created", - user_id - ) + await db.execute("INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", "user_created", user_id) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_13.py b/docs/examples/usage/usage_framework_integrations_13.py index 69a80f24..78cc0656 100644 --- a/docs/examples/usage/usage_framework_integrations_13.py +++ b/docs/examples/usage/usage_framework_integrations_13.py @@ -1,32 +1,38 @@ # start-example +__all__ = ("generate_report", "get_analytics_db", "get_main_db", "test_stub" ) + + # Main database main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) # Analytics database analytics_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/analytics"})) + # Dependency functions async def get_main_db(): async with spec.provide_session(main_db) as session: yield session + async def get_analytics_db(): async with spec.provide_session(analytics_db) as session: yield session + # Use in handlers @app.get("/report") async def generate_report( main_db: AsyncDriverAdapterBase = Depends(get_main_db), - analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db) + analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db), ) -> dict: users = await main_db.execute("SELECT COUNT(*) FROM users") events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "users": users.scalar(), - "events": events.scalar() - } + return {"users": users.scalar(), "events": events.scalar()} + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_14.py b/docs/examples/usage/usage_framework_integrations_14.py index dfd1667f..023b68e6 100644 --- a/docs/examples/usage/usage_framework_integrations_14.py +++ b/docs/examples/usage/usage_framework_integrations_14.py @@ -1,8 +1,12 @@ # start-example -from sanic import Sanic, Request, json +from sanic import Sanic + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig +__all__ = ("close_db", "test_stub" ) + + app = Sanic("MyApp") # Initialize SQLSpec @@ -13,11 +17,15 @@ app.ctx.sqlspec = spec app.ctx.db_config = db + # Cleanup on shutdown @app.before_server_stop -async def close_db(app, loop): +async def close_db(app, loop) -> None: await app.ctx.sqlspec.close_all_pools() + + # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_15.py b/docs/examples/usage/usage_framework_integrations_15.py index 8572782f..b6a96590 100644 --- a/docs/examples/usage/usage_framework_integrations_15.py +++ b/docs/examples/usage/usage_framework_integrations_15.py @@ -1,13 +1,16 @@ # start-example +__all__ = ("get_user", "test_stub" ) + + @app.get("/users/") async def get_user(request: Request, user_id: int): async with request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config) as db: - result = await db.execute( - "SELECT id, name, email FROM users WHERE id = $1", - user_id - ) + result = await db.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) return json(result.one()) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_16.py b/docs/examples/usage/usage_framework_integrations_16.py index f2bcdb57..0b8f4a8e 100644 --- a/docs/examples/usage/usage_framework_integrations_16.py +++ b/docs/examples/usage/usage_framework_integrations_16.py @@ -1,21 +1,27 @@ # start-example +__all__ = ("add_db_session", "cleanup_db_session", "list_users", "test_stub" ) + + @app.middleware("request") -async def add_db_session(request): - request.ctx.db = await request.app.ctx.sqlspec.provide_session( - request.app.ctx.db_config - ).__aenter__() +async def add_db_session(request) -> None: + request.ctx.db = await request.app.ctx.sqlspec.provide_session(request.app.ctx.db_config).__aenter__() + @app.middleware("response") -async def cleanup_db_session(request, response): +async def cleanup_db_session(request, response) -> None: if hasattr(request.ctx, "db"): await request.ctx.db.__aexit__(None, None, None) + # Use in handlers @app.get("/users") async def list_users(request: Request): result = await request.ctx.db.execute("SELECT * FROM users") return json(result.rows) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_17.py b/docs/examples/usage/usage_framework_integrations_17.py index 0856646f..f71a1dca 100644 --- a/docs/examples/usage/usage_framework_integrations_17.py +++ b/docs/examples/usage/usage_framework_integrations_17.py @@ -1,8 +1,12 @@ # start-example -from flask import Flask, g +from flask import Flask + from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig +__all__ = ("test_stub", ) + + app = Flask(__name__) # Initialize SQLSpec @@ -10,5 +14,6 @@ db = spec.add_config(SqliteConfig(pool_config={"database": "app.db"})) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_18.py b/docs/examples/usage/usage_framework_integrations_18.py index b982203d..0725eb5c 100644 --- a/docs/examples/usage/usage_framework_integrations_18.py +++ b/docs/examples/usage/usage_framework_integrations_18.py @@ -1,22 +1,30 @@ # start-example +__all__ = ("close_db", "get_db", "get_user", "test_stub" ) + + def get_db(): - if 'db' not in g: + if "db" not in g: g.db = spec.provide_session(db).__enter__() return g.db + @app.teardown_appcontext -def close_db(error): - db = g.pop('db', None) +def close_db(error) -> None: + db = g.pop("db", None) if db is not None: db.__exit__(None, None, None) + # Use in routes -@app.route('/users/') +@app.route("/users/") def get_user(user_id): db = get_db() result = db.execute("SELECT * FROM users WHERE id = ?", user_id) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_19.py b/docs/examples/usage/usage_framework_integrations_19.py index 315137ce..a48ab193 100644 --- a/docs/examples/usage/usage_framework_integrations_19.py +++ b/docs/examples/usage/usage_framework_integrations_19.py @@ -1,6 +1,9 @@ # start-example +__all__ = ("DatabaseSession", "example_usage", "test_stub" ) + + class DatabaseSession: - def __init__(self, spec: SQLSpec, config): + def __init__(self, spec: SQLSpec, config) -> None: self.spec = spec self.config = config self.session = None @@ -13,11 +16,15 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): if self.session: await self.session.__aexit__(exc_type, exc_val, exc_tb) + # Usage -async def example_usage(): +async def example_usage() -> None: async with DatabaseSession(spec, config) as db: - result = await db.execute("SELECT * FROM users") + await db.execute("SELECT * FROM users") + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_2.py b/docs/examples/usage/usage_framework_integrations_2.py index d04d0182..b5fa6275 100644 --- a/docs/examples/usage/usage_framework_integrations_2.py +++ b/docs/examples/usage/usage_framework_integrations_2.py @@ -12,6 +12,8 @@ from sqlspec.driver import AsyncDriverAdapterBase from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("client", "get_user", "health_check", "stats", "test_stub" ) + # Inject database session @get("/users/{user_id:int}") @@ -48,7 +50,7 @@ async def client(postgres_service: PostgresService): "database": postgres_service.database, } ) - db = spec.add_config(config) + spec.add_config(config) sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) app = Litestar(route_handlers=[health_check, stats, get_user], plugins=[sqlspec_plugin], debug=True) @@ -56,6 +58,6 @@ async def client(postgres_service: PostgresService): yield client -async def test_stub(client: AsyncTestClient): +async def test_stub(client: AsyncTestClient) -> None: response = await client.get("/stats") assert response == {"status": "healthy"} diff --git a/docs/examples/usage/usage_framework_integrations_20.py b/docs/examples/usage/usage_framework_integrations_20.py index a11fef97..8a3ae5d3 100644 --- a/docs/examples/usage/usage_framework_integrations_20.py +++ b/docs/examples/usage/usage_framework_integrations_20.py @@ -1,8 +1,11 @@ # start-example -import asyncio from contextvars import ContextVar -db_session: ContextVar = ContextVar('db_session', default=None) +__all__ = ("cleanup_session", "get_session", "test_stub" ) + + +db_session: ContextVar = ContextVar("db_session", default=None) + async def get_session(): session = db_session.get() @@ -11,12 +14,16 @@ async def get_session(): db_session.set(session) return session -async def cleanup_session(): + +async def cleanup_session() -> None: session = db_session.get() if session: await session.__aexit__(None, None, None) db_session.set(None) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_21.py b/docs/examples/usage/usage_framework_integrations_21.py index 0d6c833f..628ba935 100644 --- a/docs/examples/usage/usage_framework_integrations_21.py +++ b/docs/examples/usage/usage_framework_integrations_21.py @@ -1,4 +1,7 @@ # start-example +__all__ = ("Database", "example_usage", "test_stub" ) + + class Database: _instance = None _spec = None @@ -8,20 +11,24 @@ def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._spec = SQLSpec() - cls._config = cls._spec.add_config( - AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}) - ) + cls._config = cls._spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) return cls._instance async def session(self): return self._spec.provide_session(self._config) + # Usage db = Database() -async def example_usage(): + + +async def example_usage() -> None: async with await db.session() as session: - result = await session.execute("SELECT * FROM users") + await session.execute("SELECT * FROM users") + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_22.py b/docs/examples/usage/usage_framework_integrations_22.py index 4710213b..425e00ae 100644 --- a/docs/examples/usage/usage_framework_integrations_22.py +++ b/docs/examples/usage/usage_framework_integrations_22.py @@ -1,9 +1,13 @@ # start-example +__all__ = ("test_stub", ) + + # Prefer Litestar plugin over manual setup spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py index 7143b57f..aa5a6b48 100644 --- a/docs/examples/usage/usage_framework_integrations_23.py +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -1,15 +1,22 @@ # start-example +__all__ = ("close_pools", "lifespan", "test_stub" ) + + # FastAPI @asynccontextmanager async def lifespan(app: FastAPI): yield await spec.close_all_pools() + # Sanic @app.before_server_stop -async def close_pools(app, loop): +async def close_pools(app, loop) -> None: await spec.close_all_pools() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_24.py b/docs/examples/usage/usage_framework_integrations_24.py index 2f30d8ea..fa31dce9 100644 --- a/docs/examples/usage/usage_framework_integrations_24.py +++ b/docs/examples/usage/usage_framework_integrations_24.py @@ -1,9 +1,15 @@ # start-example +__all__ = ("get_db", "test_stub" ) + + # Inject sessions, not global instances async def get_db(): async with spec.provide_session(config) as session: yield session + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_25.py b/docs/examples/usage/usage_framework_integrations_25.py index e350e011..97aef68e 100644 --- a/docs/examples/usage/usage_framework_integrations_25.py +++ b/docs/examples/usage/usage_framework_integrations_25.py @@ -1,21 +1,23 @@ # start-example +__all__ = ("manual_transaction", "test_stub" ) + + # Use autocommit for simple CRUD spec = SQLSpec() db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit"} - } - ) + AsyncpgConfig(pool_config={"dsn": "postgresql://..."}, extension_config={"litestar": {"commit_mode": "autocommit"}}) ) + # Manual transactions for complex operations -async def manual_transaction(db_session): +async def manual_transaction(db_session) -> None: async with db_session.begin_transaction(): # Multiple operations pass + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py index 3d034aaa..57f0f9bd 100644 --- a/docs/examples/usage/usage_framework_integrations_26.py +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -1,25 +1,26 @@ # start-example +__all__ = ("UserRepository", "get_user", "test_stub" ) + + # Good: Separate repository layer class UserRepository: - def __init__(self, db: AsyncDriverAdapterBase): + def __init__(self, db: AsyncDriverAdapterBase) -> None: self.db = db async def get_user(self, user_id: int): - result = await self.db.execute( - "SELECT * FROM users WHERE id = $1", - user_id - ) + result = await self.db.execute("SELECT * FROM users WHERE id = $1", user_id) return result.one() + # Use in handlers @app.get("/users/{user_id}") -async def get_user( - user_id: int, - db: AsyncDriverAdapterBase = Depends(get_db) -): +async def get_user(user_id: int, db: AsyncDriverAdapterBase = Depends(get_db)): repo = UserRepository(db) return await repo.get_user(user_id) + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_27.py b/docs/examples/usage/usage_framework_integrations_27.py index f0404136..ee6742bc 100644 --- a/docs/examples/usage/usage_framework_integrations_27.py +++ b/docs/examples/usage/usage_framework_integrations_27.py @@ -1,7 +1,11 @@ # start-example import pytest + from sqlspec.adapters.sqlite import SqliteConfig +__all__ = ("test_create_user", "test_db" ) + + @pytest.fixture async def test_db(): spec = SQLSpec() @@ -17,10 +21,10 @@ async def test_db(): """) yield session -async def test_create_user(test_db): - result = await test_db.execute( - "INSERT INTO users (name) VALUES ($1) RETURNING id", - "Test User" - ) + +async def test_create_user(test_db) -> None: + result = await test_db.execute("INSERT INTO users (name) VALUES ($1) RETURNING id", "Test User") assert result.scalar() == 1 + + # end-example diff --git a/docs/examples/usage/usage_framework_integrations_3.py b/docs/examples/usage/usage_framework_integrations_3.py index a8793315..e61d16e7 100644 --- a/docs/examples/usage/usage_framework_integrations_3.py +++ b/docs/examples/usage/usage_framework_integrations_3.py @@ -1,32 +1,35 @@ # start-example from litestar import post + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase +__all__ = ("create_user", "test_stub" ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, extension_config={ "litestar": {"commit_mode": "manual"} # Default - } + }, ) ) + @post("/users") -async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase -) -> dict: +async def create_user(data: dict, db_session: AsyncDriverAdapterBase) -> dict: async with db_session.begin_transaction(): result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", data["name"], data["email"] ) return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_4.py b/docs/examples/usage/usage_framework_integrations_4.py index 2f2e8bfb..8216206a 100644 --- a/docs/examples/usage/usage_framework_integrations_4.py +++ b/docs/examples/usage/usage_framework_integrations_4.py @@ -1,28 +1,30 @@ # start-example +__all__ = ("create_user", "test_stub" ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, extension_config={ "litestar": {"commit_mode": "autocommit"} # Auto-commit on 2xx - } + }, ) ) + @post("/users") -async def create_user( - data: dict, - db_session: AsyncDriverAdapterBase -) -> dict: +async def create_user(data: dict, db_session: AsyncDriverAdapterBase) -> dict: # Transaction begins automatically result = await db_session.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", - data["name"], - data["email"] + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", data["name"], data["email"] ) # Commits automatically on success return result.one() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_5.py b/docs/examples/usage/usage_framework_integrations_5.py index d79d5bed..55352e5a 100644 --- a/docs/examples/usage/usage_framework_integrations_5.py +++ b/docs/examples/usage/usage_framework_integrations_5.py @@ -1,14 +1,16 @@ # start-example +__all__ = ("test_stub", ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, - extension_config={ - "litestar": {"commit_mode": "autocommit_include_redirect"} - } + extension_config={"litestar": {"commit_mode": "autocommit_include_redirect"}}, ) ) # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_6.py b/docs/examples/usage/usage_framework_integrations_6.py index 39fd00d6..0e849476 100644 --- a/docs/examples/usage/usage_framework_integrations_6.py +++ b/docs/examples/usage/usage_framework_integrations_6.py @@ -3,25 +3,32 @@ from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase +__all__ = ("list_users", "test_stub" ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://..."}, extension_config={ "litestar": { - "connection_key": "database", # Default: "db_connection" - "pool_key": "db_pool", # Default: "db_pool" - "session_key": "session", # Default: "db_session" + "connection_key": "database", # Default: "db_connection" + "pool_key": "db_pool", # Default: "db_pool" + "session_key": "session", # Default: "db_session" } - } + }, ) ) + @get("/users") async def list_users(session: AsyncDriverAdapterBase) -> list: result = await session.execute("SELECT * FROM users") return result.all() + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_7.py b/docs/examples/usage/usage_framework_integrations_7.py index f79bebb4..c8bf08f7 100644 --- a/docs/examples/usage/usage_framework_integrations_7.py +++ b/docs/examples/usage/usage_framework_integrations_7.py @@ -4,18 +4,16 @@ from sqlspec.driver import AsyncDriverAdapterBase from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("generate_report", "test_stub" ) + + spec = SQLSpec() # Main database main_db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/main"}, - extension_config={ - "litestar": { - "session_key": "main_db", - "connection_key": "main_db_connection", - } - } + extension_config={"litestar": {"session_key": "main_db", "connection_key": "main_db_connection"}}, ) ) @@ -23,33 +21,24 @@ analytics_db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/analytics"}, - extension_config={ - "litestar": { - "session_key": "analytics_db", - "connection_key": "analytics_connection", - } - } + extension_config={"litestar": {"session_key": "analytics_db", "connection_key": "analytics_connection"}}, ) ) # Create single plugin with all configs -app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)] -) +app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) + # Use in handlers @get("/report") -async def generate_report( - main_db: AsyncDriverAdapterBase, - analytics_db: AsyncDriverAdapterBase -) -> dict: +async def generate_report(main_db: AsyncDriverAdapterBase, analytics_db: AsyncDriverAdapterBase) -> dict: users = await main_db.execute("SELECT COUNT(*) FROM users") events = await analytics_db.execute("SELECT COUNT(*) FROM events") - return { - "total_users": users.scalar(), - "total_events": events.scalar() - } + return {"total_users": users.scalar(), "total_events": events.scalar()} + + # end-example -def test_stub(): + +def test_stub() -> None: assert True diff --git a/docs/examples/usage/usage_framework_integrations_8.py b/docs/examples/usage/usage_framework_integrations_8.py index d5a4fe07..4cf72d93 100644 --- a/docs/examples/usage/usage_framework_integrations_8.py +++ b/docs/examples/usage/usage_framework_integrations_8.py @@ -1,23 +1,22 @@ # start-example from litestar import Litestar from litestar.middleware.session.server_side import ServerSideSessionConfig + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.adapters.asyncpg.litestar import AsyncpgStore from sqlspec.extensions.litestar import SQLSpecPlugin +__all__ = ("test_stub", ) + + # Configure database with session support spec = SQLSpec() db = spec.add_config( AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": {"session_table": "litestar_sessions"} - }, - migration_config={ - "script_location": "migrations", - "include_extensions": ["litestar"] - } + extension_config={"litestar": {"session_table": "litestar_sessions"}}, + migration_config={"script_location": "migrations", "include_extensions": ["litestar"]}, ) ) @@ -25,13 +24,9 @@ store = AsyncpgStore(db) # Configure Litestar with plugin and session middleware -app = Litestar( - plugins=[SQLSpecPlugin(sqlspec=spec)], - middleware=[ - ServerSideSessionConfig(store=store).middleware - ] -) +app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)], middleware=[ServerSideSessionConfig(store=store).middleware]) # end-example -def test_stub(): + +def test_stub() -> None: assert app is not None diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py index 77d0a8ea..14ce7dee 100644 --- a/docs/examples/usage/usage_framework_integrations_9.py +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -2,6 +2,9 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig +__all__ = ("test_stub", ) + + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( @@ -12,13 +15,14 @@ "correlation_header": "x-request-id", "correlation_headers": ["x-client-trace"], "auto_trace_headers": True, - } - } + } + }, ) ) # Queries will include correlation IDs in logs (header or generated UUID) # Format: [correlation_id=abc123] SELECT * FROM users # end-example -def test_stub(): + +def test_stub() -> None: assert True From c4380595058bf5ef40d6eab55e260ec85d02d6b0 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 05:54:07 +0100 Subject: [PATCH 05/13] need main to pass test, and even with that we get error --- .../usage/usage_framework_integrations_2.py | 10 ++-- pyproject.toml | 8 ++- uv.lock | 49 ++----------------- 3 files changed, 17 insertions(+), 50 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_2.py b/docs/examples/usage/usage_framework_integrations_2.py index b5fa6275..6f521839 100644 --- a/docs/examples/usage/usage_framework_integrations_2.py +++ b/docs/examples/usage/usage_framework_integrations_2.py @@ -1,4 +1,3 @@ -# start-example from typing import Any import pytest @@ -12,9 +11,10 @@ from sqlspec.driver import AsyncDriverAdapterBase from sqlspec.extensions.litestar import SQLSpecPlugin -__all__ = ("client", "get_user", "health_check", "stats", "test_stub" ) +__all__ = ("client", "get_user", "health_check", "stats", "test_stub") +# start-example # Inject database session @get("/users/{user_id:int}") async def get_user(user_id: int, db_session: AsyncDriverAdapterBase) -> dict: @@ -53,11 +53,13 @@ async def client(postgres_service: PostgresService): spec.add_config(config) sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + async with spec.provide_session(config) as session: + await session.execute("""create table if not exists users(id int primary key, name text, email text)""") app = Litestar(route_handlers=[health_check, stats, get_user], plugins=[sqlspec_plugin], debug=True) async with AsyncTestClient(app) as client: yield client async def test_stub(client: AsyncTestClient) -> None: - response = await client.get("/stats") - assert response == {"status": "healthy"} + response = await client.get("/health") + assert response.json() == {"status": "healthy"} diff --git a/pyproject.toml b/pyproject.toml index 1f0c0f39..9d09c7d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,9 @@ duckdb = ["duckdb"] fastapi = ["fastapi"] flask = ["flask"] fsspec = ["fsspec"] -litestar = ["litestar"] +litestar = [ + "litestar", +] msgspec = ["msgspec"] mypyc = [] nanoid = ["fastnanoid>=0.4.1"] @@ -296,6 +298,7 @@ filterwarnings = [ "ignore:`use_rich_markup=` will be deprecated:PendingDeprecationWarning", "ignore:`show_metavars_column=` will be deprecated:PendingDeprecationWarning", "ignore:`append_metavars_help=` will be deprecated:PendingDeprecationWarning", + "ignore:Call to deprecated function 'resolve_before_request'. Deprecated in litestar 3.0. This function will be removed in 4.0. Use '.before_request attribute' instead:DeprecationWarning:litestar", ] markers = [ "integration: marks tests that require an external database", @@ -536,3 +539,6 @@ split-on-trailing-comma = false [tool.ruff.lint.flake8-tidy-imports] # Disallow all relative imports. ban-relative-imports = "all" + +[tool.uv.sources] +litestar = { git = "https://github.com/euri10/litestar.git" } diff --git a/uv.lock b/uv.lock index ab3478b3..1e9a33f0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10, <4.0" resolution-markers = [ "python_full_version >= '3.14'", @@ -1349,18 +1349,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, ] -[[package]] -name = "faker" -version = "38.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/05/206c151fe8ca9c8e46963d6c8b6e2e281f272009dad30fe3792005393a5e/faker-38.0.0.tar.gz", hash = "sha256:797aa03fa86982dfb6206918acc10ebf3655bdaa89ddfd3e668d7cc69537331a", size = 1935705, upload-time = "2025-11-12T01:47:39.586Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/1e/e6d1940d2c2617d7e6a0a3fdd90e506ff141715cdc4c3ecd7217d937e656/faker-38.0.0-py3-none-any.whl", hash = "sha256:ad4ea6fbfaac2a75d92943e6a79c81f38ecff92378f6541dea9a677ec789a5b2", size = 1975561, upload-time = "2025-11-12T01:47:36.672Z" }, -] - [[package]] name = "fastapi" version = "0.121.2" @@ -2516,36 +2504,20 @@ wheels = [ [[package]] name = "litestar" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } +version = "3.0.0b0" +source = { git = "https://github.com/euri10/litestar.git#9bcae1e42dabce7a7d4308cebec0615d4c1c93c4" } dependencies = [ { name = "anyio" }, { name = "click" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "httpx" }, - { name = "litestar-htmx" }, { name = "msgspec" }, { name = "multidict" }, { name = "multipart" }, - { name = "polyfactory" }, - { name = "pyyaml" }, { name = "rich" }, { name = "rich-click" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/75/e0c286719860bc63459dd3364616bb436aecfbd8ab26f212a3a7957a551f/litestar-2.18.0.tar.gz", hash = "sha256:be8f91813854722b7a2f37cbb57d76977050a96b2427d3c4455d406f0f4fcd50", size = 372890, upload-time = "2025-10-05T16:24:00.094Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/ec/76d3e07db8bac2f3248acb8879c7a8ba6b6c96e846c40d8474d1a0f79160/litestar-2.18.0-py3-none-any.whl", hash = "sha256:459ec993bafe47245c981d802a0a0c73f47c98313b3c4e47923eebe978f0e511", size = 564603, upload-time = "2025-10-05T16:23:58.439Z" }, -] - -[[package]] -name = "litestar-htmx" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/b9/7e296aa1adada25cce8e5f89a996b0e38d852d93b1b656a2058226c542a2/litestar_htmx-0.5.0.tar.gz", hash = "sha256:e02d1a3a92172c874835fa3e6749d65ae9fc626d0df46719490a16293e2146fb", size = 119755, upload-time = "2025-06-11T21:19:45.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/24/8d99982f0aa9c1cd82073c6232b54a0dbe6797c7d63c0583a6c68ee3ddf2/litestar_htmx-0.5.0-py3-none-any.whl", hash = "sha256:92833aa47e0d0e868d2a7dbfab75261f124f4b83d4f9ad12b57b9a68f86c50e6", size = 9970, upload-time = "2025-06-11T21:19:44.465Z" }, -] [[package]] name = "mako" @@ -3774,19 +3746,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/d1/8d1b28d007da43c750367c8bf5cb0f22758c16b1104b2b73b9acadb2d17a/polars_runtime_32-1.35.2-cp39-abi3-win_arm64.whl", hash = "sha256:6861145aa321a44eda7cc6694fb7751cb7aa0f21026df51b5faa52e64f9dc39b", size = 36955684, upload-time = "2025-11-09T13:19:15.666Z" }, ] -[[package]] -name = "polyfactory" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "faker" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/c6/2f795ede27e82bf6ed10a6b689f975017e36bf497915bfeafc3932b1a613/polyfactory-3.0.0.tar.gz", hash = "sha256:c1be54fc10fe738415eef2248bcfc687e3d74634f5e73b70e4ba58952df31363", size = 304234, upload-time = "2025-11-15T11:23:02.003Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/86/742c4eb237cdb5e7e6dfb1474f5f0dac967896185f832c012dff4d7bdce2/polyfactory-3.0.0-py3-none-any.whl", hash = "sha256:ad9ba3b3f04c292d30673587946e1c5d18e249074368b09abb7c9455288cfc37", size = 61237, upload-time = "2025-11-15T11:23:00.524Z" }, -] - [[package]] name = "pre-commit" version = "4.4.0" @@ -6181,7 +6140,7 @@ requires-dist = [ { name = "google-cloud-bigquery", marker = "extra == 'bigquery'" }, { name = "google-cloud-spanner", marker = "extra == 'spanner'" }, { name = "google-cloud-storage", marker = "extra == 'bigquery'" }, - { name = "litestar", marker = "extra == 'litestar'" }, + { name = "litestar", marker = "extra == 'litestar'", git = "https://github.com/euri10/litestar.git" }, { name = "msgspec", marker = "extra == 'msgspec'" }, { name = "msgspec", marker = "extra == 'performance'" }, { name = "mypy-extensions" }, From ff6c9a92c7ac6cf0a4bf75953e92ef42b9c9e85a Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 06:36:28 +0100 Subject: [PATCH 06/13] fix test 2 --- .../usage/usage_framework_integrations_2.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_2.py b/docs/examples/usage/usage_framework_integrations_2.py index 04d12846..2bd4ec2a 100644 --- a/docs/examples/usage/usage_framework_integrations_2.py +++ b/docs/examples/usage/usage_framework_integrations_2.py @@ -1,7 +1,6 @@ # start-example from typing import Any -import pytest from asyncpg import Connection, Pool from litestar import Litestar, get from litestar.testing import AsyncTestClient @@ -18,7 +17,7 @@ # Inject database session @get("/users/{user_id:int}") async def get_user(user_id: int, db_session: AsyncDriverAdapterBase) -> dict: - result = await db_session.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) + result = await db_session.execute("SELECT id, name, email FROM users_fi2 WHERE id = $1", user_id) return result.one() @@ -33,13 +32,14 @@ async def health_check(db_pool: Pool) -> dict[str, Any]: # Inject raw connection @get("/stats") async def stats(db_connection: Connection) -> dict[str, Any]: - result = await db_connection.fetchval("SELECT COUNT(*) FROM users") + result = await db_connection.fetchval("SELECT COUNT(*) FROM users_fi2") return {"user_count": result} # end-example -@pytest.fixture -async def client(postgres_service: PostgresService): + + +async def test_stub(postgres_service: PostgresService) -> None: spec = SQLSpec() config = AsyncpgConfig( pool_config={ @@ -53,11 +53,14 @@ async def client(postgres_service: PostgresService): spec.add_config(config) sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + async with spec.provide_session(config) as session: + await session.execute("""CREATE TABLE users_fi2(id integer primary key, name text, email text)""") + await session.execute("""insert into users_fi2(id, name, email) values (1, 'Alice', 'alice@example.com')""") app = Litestar(route_handlers=[health_check, stats, get_user], plugins=[sqlspec_plugin], debug=True) async with AsyncTestClient(app) as client: - yield client - - -async def test_stub(client: AsyncTestClient) -> None: - response = await client.get("/stats") - assert response == {"status": "healthy"} + response = await client.get("/health") + assert response.json() == {"status": "healthy"} + response = await client.get("/stats") + assert response.json() == {"user_count": 1} + response = await client.get("/users/1") + assert response.json() == {"email": "alice@example.com", "id": 1, "name": "Alice"} From dc1bd81d4405b04ba049b6a8ac081345b41901b5 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 08:34:52 +0100 Subject: [PATCH 07/13] fix some imports, test still bad --- .../usage/usage_framework_integrations_11.py | 14 +- .../usage/usage_framework_integrations_12.py | 12 +- .../usage/usage_framework_integrations_13.py | 11 +- .../usage/usage_framework_integrations_14.py | 2 +- .../usage/usage_framework_integrations_15.py | 9 +- .../usage/usage_framework_integrations_19.py | 5 +- .../usage/usage_framework_integrations_21.py | 6 +- .../usage/usage_framework_integrations_22.py | 8 +- .../usage/usage_framework_integrations_23.py | 7 +- .../usage/usage_framework_integrations_25.py | 5 +- .../usage/usage_framework_integrations_26.py | 7 +- .../usage/usage_framework_integrations_27.py | 20 +- .../usage/usage_framework_integrations_4.py | 7 +- .../usage/usage_framework_integrations_5.py | 5 +- .../usage/usage_framework_integrations_6.py | 4 +- .../usage/usage_framework_integrations_7.py | 4 +- .../usage/usage_framework_integrations_8.py | 15 +- pyproject.toml | 1 + uv.lock | 206 +++++++++++++++++- 19 files changed, 313 insertions(+), 35 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_11.py b/docs/examples/usage/usage_framework_integrations_11.py index 37d8f4ed..40e4d3f7 100644 --- a/docs/examples/usage/usage_framework_integrations_11.py +++ b/docs/examples/usage/usage_framework_integrations_11.py @@ -1,7 +1,15 @@ -# start-example from collections.abc import AsyncGenerator +from typing import Annotated + +from fastapi import Depends, FastAPI + +from sqlspec.driver import AsyncDriverAdapterBase -__all__ = ("get_db_session", "get_user", "test_stub" ) +__all__ = ("get_db_session", "get_user", "test_stub") + + +# start-example +app = FastAPI() async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: @@ -11,7 +19,7 @@ async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: # Use in route handlers @app.get("/users/{user_id}") -async def get_user(user_id: int, db: AsyncDriverAdapterBase = Depends(get_db_session)) -> dict: +async def get_user(user_id: int, db: Annotated[AsyncDriverAdapterBase, Depends(get_db_session)]) -> dict: result = await db.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) return result.one() diff --git a/docs/examples/usage/usage_framework_integrations_12.py b/docs/examples/usage/usage_framework_integrations_12.py index a819aef3..f1c66778 100644 --- a/docs/examples/usage/usage_framework_integrations_12.py +++ b/docs/examples/usage/usage_framework_integrations_12.py @@ -1,9 +1,17 @@ # start-example -__all__ = ("create_user", "test_stub" ) +__all__ = ("create_user", "test_stub") + +from typing import Annotated + +from fastapi import Depends, FastAPI + +from sqlspec import AsyncDriverAdapterBase + +app = FastAPI() @app.post("/users") -async def create_user(user_data: dict, db: AsyncDriverAdapterBase = Depends(get_db_session)) -> dict: +async def create_user(user_data: dict, db: Annotated[AsyncDriverAdapterBase, Depends(get_db_session)]) -> dict: async with db.begin_transaction(): result = await db.execute( "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", user_data["name"], user_data["email"] diff --git a/docs/examples/usage/usage_framework_integrations_13.py b/docs/examples/usage/usage_framework_integrations_13.py index 78cc0656..ca200cbb 100644 --- a/docs/examples/usage/usage_framework_integrations_13.py +++ b/docs/examples/usage/usage_framework_integrations_13.py @@ -1,8 +1,14 @@ # start-example -__all__ = ("generate_report", "get_analytics_db", "get_main_db", "test_stub" ) +__all__ = ("generate_report", "get_analytics_db", "get_main_db", "test_stub") # Main database +from fastapi import Depends, FastAPI + +from sqlspec import AsyncDriverAdapterBase, SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + +spec = SQLSpec() main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/main"})) # Analytics database @@ -20,6 +26,9 @@ async def get_analytics_db(): yield session +app = FastAPI() + + # Use in handlers @app.get("/report") async def generate_report( diff --git a/docs/examples/usage/usage_framework_integrations_14.py b/docs/examples/usage/usage_framework_integrations_14.py index 023b68e6..5d93a340 100644 --- a/docs/examples/usage/usage_framework_integrations_14.py +++ b/docs/examples/usage/usage_framework_integrations_14.py @@ -4,7 +4,7 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig -__all__ = ("close_db", "test_stub" ) +__all__ = ("close_db", "test_stub") app = Sanic("MyApp") diff --git a/docs/examples/usage/usage_framework_integrations_15.py b/docs/examples/usage/usage_framework_integrations_15.py index b6a96590..70109a11 100644 --- a/docs/examples/usage/usage_framework_integrations_15.py +++ b/docs/examples/usage/usage_framework_integrations_15.py @@ -1,5 +1,12 @@ # start-example -__all__ = ("get_user", "test_stub" ) +__all__ = ("get_user", "test_stub") + + +import json + +from fastapi import FastAPI, Request + +app = FastAPI() @app.get("/users/") diff --git a/docs/examples/usage/usage_framework_integrations_19.py b/docs/examples/usage/usage_framework_integrations_19.py index a48ab193..8a760187 100644 --- a/docs/examples/usage/usage_framework_integrations_19.py +++ b/docs/examples/usage/usage_framework_integrations_19.py @@ -1,5 +1,8 @@ # start-example -__all__ = ("DatabaseSession", "example_usage", "test_stub" ) +__all__ = ("DatabaseSession", "example_usage", "test_stub") + + +from sqlspec import SQLSpec class DatabaseSession: diff --git a/docs/examples/usage/usage_framework_integrations_21.py b/docs/examples/usage/usage_framework_integrations_21.py index 628ba935..4c14f594 100644 --- a/docs/examples/usage/usage_framework_integrations_21.py +++ b/docs/examples/usage/usage_framework_integrations_21.py @@ -1,5 +1,9 @@ # start-example -__all__ = ("Database", "example_usage", "test_stub" ) +__all__ = ("Database", "example_usage", "test_stub") + + +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig class Database: diff --git a/docs/examples/usage/usage_framework_integrations_22.py b/docs/examples/usage/usage_framework_integrations_22.py index 425e00ae..9eeb79a6 100644 --- a/docs/examples/usage/usage_framework_integrations_22.py +++ b/docs/examples/usage/usage_framework_integrations_22.py @@ -1,8 +1,14 @@ # start-example -__all__ = ("test_stub", ) +__all__ = ("test_stub",) # Prefer Litestar plugin over manual setup +from litestar import Litestar + +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig +from sqlspec.extensions.litestar import SQLSpecPlugin + spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)]) diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py index aa5a6b48..025f6919 100644 --- a/docs/examples/usage/usage_framework_integrations_23.py +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -1,8 +1,13 @@ # start-example -__all__ = ("close_pools", "lifespan", "test_stub" ) +__all__ = ("close_pools", "lifespan", "test_stub") # FastAPI +from contextlib import asynccontextmanager + +from fastapi import FastAPI + + @asynccontextmanager async def lifespan(app: FastAPI): yield diff --git a/docs/examples/usage/usage_framework_integrations_25.py b/docs/examples/usage/usage_framework_integrations_25.py index 97aef68e..619b342b 100644 --- a/docs/examples/usage/usage_framework_integrations_25.py +++ b/docs/examples/usage/usage_framework_integrations_25.py @@ -1,8 +1,11 @@ # start-example -__all__ = ("manual_transaction", "test_stub" ) +__all__ = ("manual_transaction", "test_stub") # Use autocommit for simple CRUD +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + spec = SQLSpec() db = spec.add_config( AsyncpgConfig(pool_config={"dsn": "postgresql://..."}, extension_config={"litestar": {"commit_mode": "autocommit"}}) diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py index 57f0f9bd..46d03077 100644 --- a/docs/examples/usage/usage_framework_integrations_26.py +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -1,8 +1,13 @@ # start-example -__all__ = ("UserRepository", "get_user", "test_stub" ) +__all__ = ("UserRepository", "get_user", "test_stub") # Good: Separate repository layer +from fastapi import Depends + +from sqlspec import AsyncDriverAdapterBase + + class UserRepository: def __init__(self, db: AsyncDriverAdapterBase) -> None: self.db = db diff --git a/docs/examples/usage/usage_framework_integrations_27.py b/docs/examples/usage/usage_framework_integrations_27.py index ee6742bc..4bd9e410 100644 --- a/docs/examples/usage/usage_framework_integrations_27.py +++ b/docs/examples/usage/usage_framework_integrations_27.py @@ -1,19 +1,23 @@ # start-example +from collections.abc import Generator + import pytest -from sqlspec.adapters.sqlite import SqliteConfig +from sqlspec import SQLSpec +from sqlspec.adapters.sqlite import SqliteConfig, SqliteDriver -__all__ = ("test_create_user", "test_db" ) +__all__ = ("test_create_user", "test_db") @pytest.fixture -async def test_db(): +def test_db() -> Generator[SqliteDriver]: spec = SQLSpec() - db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) + config = SqliteConfig(pool_config={"database": ":memory:"}) + spec.add_config(config) - async with spec.provide_session(db) as session: + with spec.provide_session(config) as session: # Set up test schema - await session.execute(""" + session.execute(""" CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL @@ -22,8 +26,8 @@ async def test_db(): yield session -async def test_create_user(test_db) -> None: - result = await test_db.execute("INSERT INTO users (name) VALUES ($1) RETURNING id", "Test User") +def test_create_user(test_db: SqliteDriver) -> None: + result = test_db.execute("INSERT INTO users (name) VALUES ($1) RETURNING id", "Test User") assert result.scalar() == 1 diff --git a/docs/examples/usage/usage_framework_integrations_4.py b/docs/examples/usage/usage_framework_integrations_4.py index 8216206a..f11382ff 100644 --- a/docs/examples/usage/usage_framework_integrations_4.py +++ b/docs/examples/usage/usage_framework_integrations_4.py @@ -1,7 +1,12 @@ # start-example -__all__ = ("create_user", "test_stub" ) +__all__ = ("create_user", "test_stub") +from litestar import post + +from sqlspec import AsyncDriverAdapterBase, SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( diff --git a/docs/examples/usage/usage_framework_integrations_5.py b/docs/examples/usage/usage_framework_integrations_5.py index 55352e5a..c33ed9eb 100644 --- a/docs/examples/usage/usage_framework_integrations_5.py +++ b/docs/examples/usage/usage_framework_integrations_5.py @@ -1,7 +1,10 @@ # start-example -__all__ = ("test_stub", ) +__all__ = ("test_stub",) +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + spec = SQLSpec() db = spec.add_config( AsyncpgConfig( diff --git a/docs/examples/usage/usage_framework_integrations_6.py b/docs/examples/usage/usage_framework_integrations_6.py index 0e849476..a707dbcf 100644 --- a/docs/examples/usage/usage_framework_integrations_6.py +++ b/docs/examples/usage/usage_framework_integrations_6.py @@ -1,9 +1,11 @@ # start-example +from litestar import get + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase -__all__ = ("list_users", "test_stub" ) +__all__ = ("list_users", "test_stub") spec = SQLSpec() diff --git a/docs/examples/usage/usage_framework_integrations_7.py b/docs/examples/usage/usage_framework_integrations_7.py index c8bf08f7..703574ea 100644 --- a/docs/examples/usage/usage_framework_integrations_7.py +++ b/docs/examples/usage/usage_framework_integrations_7.py @@ -1,10 +1,12 @@ # start-example +from litestar import Litestar, get + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase from sqlspec.extensions.litestar import SQLSpecPlugin -__all__ = ("generate_report", "test_stub" ) +__all__ = ("generate_report", "test_stub") spec = SQLSpec() diff --git a/docs/examples/usage/usage_framework_integrations_8.py b/docs/examples/usage/usage_framework_integrations_8.py index 4cf72d93..695bb471 100644 --- a/docs/examples/usage/usage_framework_integrations_8.py +++ b/docs/examples/usage/usage_framework_integrations_8.py @@ -7,21 +7,20 @@ from sqlspec.adapters.asyncpg.litestar import AsyncpgStore from sqlspec.extensions.litestar import SQLSpecPlugin -__all__ = ("test_stub", ) +__all__ = ("test_stub",) # Configure database with session support spec = SQLSpec() -db = spec.add_config( - AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={"litestar": {"session_table": "litestar_sessions"}}, - migration_config={"script_location": "migrations", "include_extensions": ["litestar"]}, - ) +config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + extension_config={"litestar": {"session_table": "litestar_sessions"}}, + migration_config={"script_location": "migrations", "include_extensions": ["litestar"]}, ) +db = spec.add_config(config) # Create session store using adapter-specific class -store = AsyncpgStore(db) +store = AsyncpgStore(config) # Configure Litestar with plugin and session middleware app = Litestar(plugins=[SQLSpecPlugin(sqlspec=spec)], middleware=[ServerSideSessionConfig(store=store).middleware]) diff --git a/pyproject.toml b/pyproject.toml index 8b2e8f7b..ff1af4ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ psycopg = ["psycopg[binary,pool]"] pydantic = ["pydantic", "pydantic-extra-types"] pymssql = ["pymssql"] pymysql = ["pymysql"] +sanic = ["sanic"] spanner = ["google-cloud-spanner"] uuid = ["uuid-utils"] diff --git a/uv.lock b/uv.lock index 1a1341b1..e213d174 100644 --- a/uv.lock +++ b/uv.lock @@ -2339,6 +2339,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173, upload-time = "2020-06-22T23:32:36.781Z" }, ] +[[package]] +name = "html5tagger" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/02/2ae5f46d517a2c1d4a17f2b1e4834c2c7cc0fb3a69c92389172fa16ab389/html5tagger-1.3.0.tar.gz", hash = "sha256:84fa3dfb49e5c83b79bbd856ab7b1de8e2311c3bb46a8be925f119e3880a8da9", size = 14196, upload-time = "2023-03-28T05:59:34.642Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/12/2f5d43ee912ea14a6baba4b3db6d309b02d932e3b7074c3339b4aded98ff/html5tagger-1.3.0-py3-none-any.whl", hash = "sha256:ce14313515edffec8ed8a36c5890d023922641171b4e6e5774ad1a74998f5351", size = 10956, upload-time = "2023-03-28T05:59:32.524Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -2364,6 +2373,49 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148, upload-time = "2025-09-11T12:16:01.803Z" }, ] +[[package]] +name = "httptools" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" }, + { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" }, + { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" }, + { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" }, + { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" }, + { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, + { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, + { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, + { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, +] + [[package]] name = "httpx" version = "0.28.1" @@ -5158,6 +5210,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/fc/56cba14af8ad8fd020c85b6e44328520ac55939bb1f9d01444ad470504cb/s3fs-2025.10.0-py3-none-any.whl", hash = "sha256:da7ef25efc1541f5fca8e1116361e49ea1081f83f4e8001fbd77347c625da28a", size = 30357, upload-time = "2025-10-30T15:06:03.48Z" }, ] +[[package]] +name = "sanic" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "html5tagger" }, + { name = "httptools" }, + { name = "multidict" }, + { name = "sanic-routing" }, + { name = "setuptools" }, + { name = "tracerite" }, + { name = "typing-extensions" }, + { name = "ujson", marker = "implementation_name == 'cpython' and sys_platform != 'win32'" }, + { name = "uvloop", marker = "implementation_name == 'cpython' and sys_platform != 'win32'" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/8b/08dc376390fe854ef32984973883b646ee68c6727da72ffcc65340d8f192/sanic-25.3.0.tar.gz", hash = "sha256:775d522001ec81f034ec8e4d7599e2175bfc097b8d57884f5e4c9322f5e369bb", size = 353027, upload-time = "2025-03-31T21:22:29.718Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/e1/b36ddc16862d63d22986ae21b04a79c8fb7ec48d5d664acdfd1c2acf78ac/sanic-25.3.0-py3-none-any.whl", hash = "sha256:fb519b38b4c220569b0e2e868583ffeaffaab96a78b2e42ae78bc56a644a4cd7", size = 246416, upload-time = "2025-03-31T21:22:27.946Z" }, +] + +[[package]] +name = "sanic-routing" +version = "23.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/5c/2a7edd14fbccca3719a8d680951d4b25f986752c781c61ccf156a6d1ebff/sanic-routing-23.12.0.tar.gz", hash = "sha256:1dcadc62c443e48c852392dba03603f9862b6197fc4cba5bbefeb1ace0848b04", size = 29473, upload-time = "2023-12-31T09:28:36.992Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/e3/3425c9a8773807ac2c01d6a56c8521733f09b627e5827e733c5cd36b9ac5/sanic_routing-23.12.0-py3-none-any.whl", hash = "sha256:1558a72afcb9046ed3134a5edae02fc1552cff08f0fff2e8d5de0877ea43ed73", size = 25522, upload-time = "2023-12-31T09:28:35.233Z" }, +] + [[package]] name = "setuptools" version = "80.9.0" @@ -5968,6 +6051,9 @@ pymssql = [ pymysql = [ { name = "pymysql" }, ] +sanic = [ + { name = "sanic" }, +] spanner = [ { name = "google-cloud-spanner" }, ] @@ -6162,12 +6248,13 @@ requires-dist = [ { name = "pymysql", marker = "extra == 'pymysql'" }, { name = "rich-click" }, { name = "rich-click", marker = "extra == 'cli'" }, + { name = "sanic", marker = "extra == 'sanic'" }, { name = "sqlglot", specifier = ">=19.9.0" }, { name = "sqlglot", extras = ["rs"], marker = "extra == 'performance'" }, { name = "typing-extensions" }, { name = "uuid-utils", marker = "extra == 'uuid'" }, ] -provides-extras = ["adbc", "adk", "aioodbc", "aiosql", "aiosqlite", "alloydb", "asyncmy", "asyncpg", "attrs", "bigquery", "cli", "cloud-sql", "duckdb", "fastapi", "flask", "fsspec", "litestar", "msgspec", "mypyc", "nanoid", "obstore", "opentelemetry", "oracledb", "orjson", "pandas", "performance", "polars", "prometheus", "psqlpy", "psycopg", "pydantic", "pymssql", "pymysql", "spanner", "uuid"] +provides-extras = ["adbc", "adk", "aioodbc", "aiosql", "aiosqlite", "alloydb", "asyncmy", "asyncpg", "attrs", "bigquery", "cli", "cloud-sql", "duckdb", "fastapi", "flask", "fsspec", "litestar", "msgspec", "mypyc", "nanoid", "obstore", "opentelemetry", "oracledb", "orjson", "pandas", "performance", "polars", "prometheus", "psqlpy", "psycopg", "pydantic", "pymssql", "pymysql", "sanic", "spanner", "uuid"] [package.metadata.requires-dev] benchmarks = [ @@ -6426,6 +6513,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, ] +[[package]] +name = "tracerite" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "html5tagger" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/b2/37b825b881f23bc56384c3142214ccbe5d9de7e7c5fe3d155fa032738b98/tracerite-1.1.3.tar.gz", hash = "sha256:119fc006f240aa03fffb41cf99cf82fda5c0449c7d4b6fe42c6340403578b31e", size = 269646, upload-time = "2025-06-19T17:47:42.289Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/bf/c65d36ec5a93048dd55b3247be26059970daad72263e35ecace2f3188b2c/tracerite-1.1.3-py3-none-any.whl", hash = "sha256:811d8e2e0fb563b77340eebe2e9f7b324acfe01e09ea58db8bcaecb24327c823", size = 12422, upload-time = "2025-06-19T17:47:40.173Z" }, +] + [[package]] name = "trove-classifiers" version = "2025.11.14.15" @@ -6555,6 +6654,67 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, ] +[[package]] +name = "ujson" +version = "5.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/d9/3f17e3c5773fb4941c68d9a37a47b1a79c9649d6c56aefbed87cc409d18a/ujson-5.11.0.tar.gz", hash = "sha256:e204ae6f909f099ba6b6b942131cee359ddda2b6e4ea39c12eb8b991fe2010e0", size = 7156583, upload-time = "2025-08-20T11:57:02.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/8bf7a4fabfd01c7eed92d9b290930ce6d14910dec708e73538baa38885d1/ujson-5.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:446e8c11c06048611c9d29ef1237065de0af07cabdd97e6b5b527b957692ec25", size = 55248, upload-time = "2025-08-20T11:55:02.368Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2e/eeab0b8b641817031ede4f790db4c4942df44a12f44d72b3954f39c6a115/ujson-5.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16ccb973b7ada0455201808ff11d48fe9c3f034a6ab5bd93b944443c88299f89", size = 53157, upload-time = "2025-08-20T11:55:04.012Z" }, + { url = "https://files.pythonhosted.org/packages/21/1b/a4e7a41870797633423ea79618526747353fd7be9191f3acfbdee0bf264b/ujson-5.11.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3134b783ab314d2298d58cda7e47e7a0f7f71fc6ade6ac86d5dbeaf4b9770fa6", size = 57657, upload-time = "2025-08-20T11:55:05.169Z" }, + { url = "https://files.pythonhosted.org/packages/94/ae/4e0d91b8f6db7c9b76423b3649612189506d5a06ddd3b6334b6d37f77a01/ujson-5.11.0-cp310-cp310-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:185f93ebccffebc8baf8302c869fac70dd5dd78694f3b875d03a31b03b062cdb", size = 59780, upload-time = "2025-08-20T11:55:06.325Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cc/46b124c2697ca2da7c65c4931ed3cb670646978157aa57a7a60f741c530f/ujson-5.11.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d06e87eded62ff0e5f5178c916337d2262fdbc03b31688142a3433eabb6511db", size = 57307, upload-time = "2025-08-20T11:55:07.493Z" }, + { url = "https://files.pythonhosted.org/packages/39/eb/20dd1282bc85dede2f1c62c45b4040bc4c389c80a05983515ab99771bca7/ujson-5.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:181fb5b15703a8b9370b25345d2a1fd1359f0f18776b3643d24e13ed9c036d4c", size = 1036369, upload-time = "2025-08-20T11:55:09.192Z" }, + { url = "https://files.pythonhosted.org/packages/64/a2/80072439065d493e3a4b1fbeec991724419a1b4c232e2d1147d257cac193/ujson-5.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4df61a6df0a4a8eb5b9b1ffd673429811f50b235539dac586bb7e9e91994138", size = 1195738, upload-time = "2025-08-20T11:55:11.402Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7e/d77f9e9c039d58299c350c978e086a804d1fceae4fd4a1cc6e8d0133f838/ujson-5.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6eff24e1abd79e0ec6d7eae651dd675ddbc41f9e43e29ef81e16b421da896915", size = 1088718, upload-time = "2025-08-20T11:55:13.297Z" }, + { url = "https://files.pythonhosted.org/packages/da/ea/80346b826349d60ca4d612a47cdf3533694e49b45e9d1c07071bb867a184/ujson-5.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d7c46cb0fe5e7056b9acb748a4c35aa1b428025853032540bb7e41f46767321f", size = 55248, upload-time = "2025-08-20T11:55:19.033Z" }, + { url = "https://files.pythonhosted.org/packages/57/df/b53e747562c89515e18156513cc7c8ced2e5e3fd6c654acaa8752ffd7cd9/ujson-5.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8951bb7a505ab2a700e26f691bdfacf395bc7e3111e3416d325b513eea03a58", size = 53156, upload-time = "2025-08-20T11:55:20.174Z" }, + { url = "https://files.pythonhosted.org/packages/41/b8/ab67ec8c01b8a3721fd13e5cb9d85ab2a6066a3a5e9148d661a6870d6293/ujson-5.11.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952c0be400229940248c0f5356514123d428cba1946af6fa2bbd7503395fef26", size = 57657, upload-time = "2025-08-20T11:55:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c7/fb84f27cd80a2c7e2d3c6012367aecade0da936790429801803fa8d4bffc/ujson-5.11.0-cp311-cp311-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:94fcae844f1e302f6f8095c5d1c45a2f0bfb928cccf9f1b99e3ace634b980a2a", size = 59779, upload-time = "2025-08-20T11:55:22.772Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/48706f7c1e917ecb97ddcfb7b1d756040b86ed38290e28579d63bd3fcc48/ujson-5.11.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e0ec1646db172beb8d3df4c32a9d78015e671d2000af548252769e33079d9a6", size = 57284, upload-time = "2025-08-20T11:55:24.01Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ce/48877c6eb4afddfd6bd1db6be34456538c07ca2d6ed233d3f6c6efc2efe8/ujson-5.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:da473b23e3a54448b008d33f742bcd6d5fb2a897e42d1fc6e7bf306ea5d18b1b", size = 1036395, upload-time = "2025-08-20T11:55:25.725Z" }, + { url = "https://files.pythonhosted.org/packages/8b/7a/2c20dc97ad70cd7c31ad0596ba8e2cf8794d77191ba4d1e0bded69865477/ujson-5.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:aa6b3d4f1c0d3f82930f4cbd7fe46d905a4a9205a7c13279789c1263faf06dba", size = 1195731, upload-time = "2025-08-20T11:55:27.915Z" }, + { url = "https://files.pythonhosted.org/packages/15/f5/ca454f2f6a2c840394b6f162fff2801450803f4ff56c7af8ce37640b8a2a/ujson-5.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4843f3ab4fe1cc596bb7e02228ef4c25d35b4bb0809d6a260852a4bfcab37ba3", size = 1088710, upload-time = "2025-08-20T11:55:29.426Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ef/a9cb1fce38f699123ff012161599fb9f2ff3f8d482b4b18c43a2dc35073f/ujson-5.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7895f0d2d53bd6aea11743bd56e3cb82d729980636cd0ed9b89418bf66591702", size = 55434, upload-time = "2025-08-20T11:55:34.987Z" }, + { url = "https://files.pythonhosted.org/packages/b1/05/dba51a00eb30bd947791b173766cbed3492269c150a7771d2750000c965f/ujson-5.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12b5e7e22a1fe01058000d1b317d3b65cc3daf61bd2ea7a2b76721fe160fa74d", size = 53190, upload-time = "2025-08-20T11:55:36.384Z" }, + { url = "https://files.pythonhosted.org/packages/03/3c/fd11a224f73fbffa299fb9644e425f38b38b30231f7923a088dd513aabb4/ujson-5.11.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0180a480a7d099082501cad1fe85252e4d4bf926b40960fb3d9e87a3a6fbbc80", size = 57600, upload-time = "2025-08-20T11:55:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/55/b9/405103cae24899df688a3431c776e00528bd4799e7d68820e7ebcf824f92/ujson-5.11.0-cp312-cp312-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:fa79fdb47701942c2132a9dd2297a1a85941d966d8c87bfd9e29b0cf423f26cc", size = 59791, upload-time = "2025-08-20T11:55:38.877Z" }, + { url = "https://files.pythonhosted.org/packages/17/7b/2dcbc2bbfdbf68f2368fb21ab0f6735e872290bb604c75f6e06b81edcb3f/ujson-5.11.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8254e858437c00f17cb72e7a644fc42dad0ebb21ea981b71df6e84b1072aaa7c", size = 57356, upload-time = "2025-08-20T11:55:40.036Z" }, + { url = "https://files.pythonhosted.org/packages/d1/71/fea2ca18986a366c750767b694430d5ded6b20b6985fddca72f74af38a4c/ujson-5.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1aa8a2ab482f09f6c10fba37112af5f957689a79ea598399c85009f2f29898b5", size = 1036313, upload-time = "2025-08-20T11:55:41.408Z" }, + { url = "https://files.pythonhosted.org/packages/a3/bb/d4220bd7532eac6288d8115db51710fa2d7d271250797b0bfba9f1e755af/ujson-5.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a638425d3c6eed0318df663df44480f4a40dc87cc7c6da44d221418312f6413b", size = 1195782, upload-time = "2025-08-20T11:55:43.357Z" }, + { url = "https://files.pythonhosted.org/packages/80/47/226e540aa38878ce1194454385701d82df538ccb5ff8db2cf1641dde849a/ujson-5.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e3cff632c1d78023b15f7e3a81c3745cd3f94c044d1e8fa8efbd6b161997bbc", size = 1088817, upload-time = "2025-08-20T11:55:45.262Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ec/2de9dd371d52c377abc05d2b725645326c4562fc87296a8907c7bcdf2db7/ujson-5.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:109f59885041b14ee9569bf0bb3f98579c3fa0652317b355669939e5fc5ede53", size = 55435, upload-time = "2025-08-20T11:55:50.243Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a4/f611f816eac3a581d8a4372f6967c3ed41eddbae4008d1d77f223f1a4e0a/ujson-5.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a31c6b8004438e8c20fc55ac1c0e07dad42941db24176fe9acf2815971f8e752", size = 53193, upload-time = "2025-08-20T11:55:51.373Z" }, + { url = "https://files.pythonhosted.org/packages/e9/c5/c161940967184de96f5cbbbcce45b562a4bf851d60f4c677704b1770136d/ujson-5.11.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c684fb21255b9b90320ba7e199780f653e03f6c2528663768965f4126a5b50", size = 57603, upload-time = "2025-08-20T11:55:52.583Z" }, + { url = "https://files.pythonhosted.org/packages/2b/d6/c7b2444238f5b2e2d0e3dab300b9ddc3606e4b1f0e4bed5a48157cebc792/ujson-5.11.0-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:4c9f5d6a27d035dd90a146f7761c2272cf7103de5127c9ab9c4cd39ea61e878a", size = 59794, upload-time = "2025-08-20T11:55:53.69Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a3/292551f936d3d02d9af148f53e1bc04306b00a7cf1fcbb86fa0d1c887242/ujson-5.11.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:837da4d27fed5fdc1b630bd18f519744b23a0b5ada1bbde1a36ba463f2900c03", size = 57363, upload-time = "2025-08-20T11:55:54.843Z" }, + { url = "https://files.pythonhosted.org/packages/90/a6/82cfa70448831b1a9e73f882225980b5c689bf539ec6400b31656a60ea46/ujson-5.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:787aff4a84da301b7f3bac09bc696e2e5670df829c6f8ecf39916b4e7e24e701", size = 1036311, upload-time = "2025-08-20T11:55:56.197Z" }, + { url = "https://files.pythonhosted.org/packages/84/5c/96e2266be50f21e9b27acaee8ca8f23ea0b85cb998c33d4f53147687839b/ujson-5.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6dd703c3e86dc6f7044c5ac0b3ae079ed96bf297974598116aa5fb7f655c3a60", size = 1195783, upload-time = "2025-08-20T11:55:58.081Z" }, + { url = "https://files.pythonhosted.org/packages/8d/20/78abe3d808cf3bb3e76f71fca46cd208317bf461c905d79f0d26b9df20f1/ujson-5.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3772e4fe6b0c1e025ba3c50841a0ca4786825a4894c8411bf8d3afe3a8061328", size = 1088822, upload-time = "2025-08-20T11:55:59.469Z" }, + { url = "https://files.pythonhosted.org/packages/28/08/4518146f4984d112764b1dfa6fb7bad691c44a401adadaa5e23ccd930053/ujson-5.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65724738c73645db88f70ba1f2e6fb678f913281804d5da2fd02c8c5839af302", size = 55462, upload-time = "2025-08-20T11:56:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/29/37/2107b9a62168867a692654d8766b81bd2fd1e1ba13e2ec90555861e02b0c/ujson-5.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29113c003ca33ab71b1b480bde952fbab2a0b6b03a4ee4c3d71687cdcbd1a29d", size = 53246, upload-time = "2025-08-20T11:56:06.054Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f8/25583c70f83788edbe3ca62ce6c1b79eff465d78dec5eb2b2b56b3e98b33/ujson-5.11.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c44c703842024d796b4c78542a6fcd5c3cb948b9fc2a73ee65b9c86a22ee3638", size = 57631, upload-time = "2025-08-20T11:56:07.374Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ca/19b3a632933a09d696f10dc1b0dfa1d692e65ad507d12340116ce4f67967/ujson-5.11.0-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:e750c436fb90edf85585f5c62a35b35082502383840962c6983403d1bd96a02c", size = 59877, upload-time = "2025-08-20T11:56:08.534Z" }, + { url = "https://files.pythonhosted.org/packages/55/7a/4572af5324ad4b2bfdd2321e898a527050290147b4ea337a79a0e4e87ec7/ujson-5.11.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f278b31a7c52eb0947b2db55a5133fbc46b6f0ef49972cd1a80843b72e135aba", size = 57363, upload-time = "2025-08-20T11:56:09.758Z" }, + { url = "https://files.pythonhosted.org/packages/7b/71/a2b8c19cf4e1efe53cf439cdf7198ac60ae15471d2f1040b490c1f0f831f/ujson-5.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ab2cb8351d976e788669c8281465d44d4e94413718af497b4e7342d7b2f78018", size = 1036394, upload-time = "2025-08-20T11:56:11.168Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3e/7b98668cba3bb3735929c31b999b374ebc02c19dfa98dfebaeeb5c8597ca/ujson-5.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:090b4d11b380ae25453100b722d0609d5051ffe98f80ec52853ccf8249dfd840", size = 1195837, upload-time = "2025-08-20T11:56:12.6Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/8870f208c20b43571a5c409ebb2fe9b9dba5f494e9e60f9314ac01ea8f78/ujson-5.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:80017e870d882d5517d28995b62e4e518a894f932f1e242cbc802a2fd64d365c", size = 1088837, upload-time = "2025-08-20T11:56:14.15Z" }, + { url = "https://files.pythonhosted.org/packages/6e/cd/e9809b064a89fe5c4184649adeb13c1b98652db3f8518980b04227358574/ujson-5.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:de6e88f62796372fba1de973c11138f197d3e0e1d80bcb2b8aae1e826096d433", size = 55759, upload-time = "2025-08-20T11:56:18.882Z" }, + { url = "https://files.pythonhosted.org/packages/1b/be/ae26a6321179ebbb3a2e2685b9007c71bcda41ad7a77bbbe164005e956fc/ujson-5.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e56ef8066f11b80d620985ae36869a3ff7e4b74c3b6129182ec5d1df0255f3", size = 53634, upload-time = "2025-08-20T11:56:20.012Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/fb4a220ee6939db099f4cfeeae796ecb91e7584ad4d445d4ca7f994a9135/ujson-5.11.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a325fd2c3a056cf6c8e023f74a0c478dd282a93141356ae7f16d5309f5ff823", size = 58547, upload-time = "2025-08-20T11:56:21.175Z" }, + { url = "https://files.pythonhosted.org/packages/bd/f8/fc4b952b8f5fea09ea3397a0bd0ad019e474b204cabcb947cead5d4d1ffc/ujson-5.11.0-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:a0af6574fc1d9d53f4ff371f58c96673e6d988ed2b5bf666a6143c782fa007e9", size = 60489, upload-time = "2025-08-20T11:56:22.342Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e5/af5491dfda4f8b77e24cf3da68ee0d1552f99a13e5c622f4cef1380925c3/ujson-5.11.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10f29e71ecf4ecd93a6610bd8efa8e7b6467454a363c3d6416db65de883eb076", size = 58035, upload-time = "2025-08-20T11:56:23.92Z" }, + { url = "https://files.pythonhosted.org/packages/c4/09/0945349dd41f25cc8c38d78ace49f14c5052c5bbb7257d2f466fa7bdb533/ujson-5.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a0a9b76a89827a592656fe12e000cf4f12da9692f51a841a4a07aa4c7ecc41c", size = 1037212, upload-time = "2025-08-20T11:56:25.274Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/8e04496acb3d5a1cbee3a54828d9652f67a37523efa3d3b18a347339680a/ujson-5.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b16930f6a0753cdc7d637b33b4e8f10d5e351e1fb83872ba6375f1e87be39746", size = 1196500, upload-time = "2025-08-20T11:56:27.517Z" }, + { url = "https://files.pythonhosted.org/packages/64/ae/4bc825860d679a0f208a19af2f39206dfd804ace2403330fdc3170334a2f/ujson-5.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04c41afc195fd477a59db3a84d5b83a871bd648ef371cf8c6f43072d89144eef", size = 1089487, upload-time = "2025-08-20T11:56:29.07Z" }, + { url = "https://files.pythonhosted.org/packages/50/17/30275aa2933430d8c0c4ead951cc4fdb922f575a349aa0b48a6f35449e97/ujson-5.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:abae0fb58cc820092a0e9e8ba0051ac4583958495bfa5262a12f628249e3b362", size = 51206, upload-time = "2025-08-20T11:56:48.797Z" }, + { url = "https://files.pythonhosted.org/packages/c3/15/42b3924258eac2551f8f33fa4e35da20a06a53857ccf3d4deb5e5d7c0b6c/ujson-5.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fac6c0649d6b7c3682a0a6e18d3de6857977378dce8d419f57a0b20e3d775b39", size = 48907, upload-time = "2025-08-20T11:56:50.136Z" }, + { url = "https://files.pythonhosted.org/packages/94/7e/0519ff7955aba581d1fe1fb1ca0e452471250455d182f686db5ac9e46119/ujson-5.11.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b42c115c7c6012506e8168315150d1e3f76e7ba0f4f95616f4ee599a1372bbc", size = 50319, upload-time = "2025-08-20T11:56:51.63Z" }, + { url = "https://files.pythonhosted.org/packages/74/cf/209d90506b7d6c5873f82c5a226d7aad1a1da153364e9ebf61eff0740c33/ujson-5.11.0-pp311-pypy311_pp73-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:86baf341d90b566d61a394869ce77188cc8668f76d7bb2c311d77a00f4bdf844", size = 56584, upload-time = "2025-08-20T11:56:52.89Z" }, + { url = "https://files.pythonhosted.org/packages/e9/97/bd939bb76943cb0e1d2b692d7e68629f51c711ef60425fa5bb6968037ecd/ujson-5.11.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4598bf3965fc1a936bd84034312bcbe00ba87880ef1ee33e33c1e88f2c398b49", size = 51588, upload-time = "2025-08-20T11:56:54.054Z" }, +] + [[package]] name = "uritemplate" version = "4.2.0" @@ -6614,6 +6774,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, ] +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" }, + { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" }, + { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, + { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, + { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + [[package]] name = "virtualenv" version = "20.35.4" From 202b15c74ef26fb5e26bea6c054a1d6da1b4da7e Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 13:51:59 +0100 Subject: [PATCH 08/13] fix 11 and some imports --- .../usage/usage_framework_integrations_10.py | 5 +- .../usage/usage_framework_integrations_11.py | 54 ++++++++++++------- .../usage/usage_framework_integrations_13.py | 6 ++- .../usage/usage_framework_integrations_16.py | 7 ++- .../usage/usage_framework_integrations_18.py | 2 +- .../usage/usage_framework_integrations_23.py | 2 + .../usage/usage_framework_integrations_26.py | 8 ++- .../usage/usage_framework_integrations_3.py | 4 +- 8 files changed, 59 insertions(+), 29 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_10.py b/docs/examples/usage/usage_framework_integrations_10.py index 9455efec..187b3e0b 100644 --- a/docs/examples/usage/usage_framework_integrations_10.py +++ b/docs/examples/usage/usage_framework_integrations_10.py @@ -1,4 +1,5 @@ # start-example +from collections.abc import AsyncGenerator from contextlib import asynccontextmanager from fastapi import FastAPI @@ -6,7 +7,7 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig -__all__ = ("lifespan", "test_stub" ) +__all__ = ("lifespan", "test_stub") # Configure database @@ -16,7 +17,7 @@ # Lifespan context manager @asynccontextmanager -async def lifespan(app: FastAPI): +async def lifespan(app: FastAPI) -> AsyncGenerator[None]: # Startup yield # Shutdown diff --git a/docs/examples/usage/usage_framework_integrations_11.py b/docs/examples/usage/usage_framework_integrations_11.py index 40e4d3f7..46abfcbb 100644 --- a/docs/examples/usage/usage_framework_integrations_11.py +++ b/docs/examples/usage/usage_framework_integrations_11.py @@ -2,30 +2,46 @@ from typing import Annotated from fastapi import Depends, FastAPI +from litestar.testing import AsyncTestClient +from pytest_databases.docker.postgres import PostgresService +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase __all__ = ("get_db_session", "get_user", "test_stub") -# start-example -app = FastAPI() - - -async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: +async def test_stub(postgres_service: PostgresService) -> None: + # start-example + app = FastAPI() + + async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: + async with spec.provide_session(config) as session: + yield session + + # Use in route handlers + @app.get("/users/{user_id}") + async def get_user(user_id: int, db: Annotated[AsyncDriverAdapterBase, Depends(get_db_session)]) -> dict: + result = await db.execute("SELECT id, name, email FROM users_fi11 WHERE id = $1", user_id) + return result.one() + + # end-example + spec = SQLSpec() + config = AsyncpgConfig( + pool_config={ + "host": postgres_service.host, + "port": postgres_service.port, + "user": postgres_service.user, + "password": postgres_service.password, + "database": postgres_service.database, + } + ) + spec.add_config(config) async with spec.provide_session(config) as session: - yield session - - -# Use in route handlers -@app.get("/users/{user_id}") -async def get_user(user_id: int, db: Annotated[AsyncDriverAdapterBase, Depends(get_db_session)]) -> dict: - result = await db.execute("SELECT id, name, email FROM users WHERE id = $1", user_id) - return result.one() - - -# end-example - + await session.execute("""CREATE TABLE users_fi11(id integer primary key, name text, email text)""") + await session.execute("""INSERT INTO users_fi11(id,name, email) VALUES (1,'toto','toto@example.com')""") -def test_stub() -> None: - assert True + async with AsyncTestClient(app) as client: + response = await client.get("/users/1") + assert response.json() == {"id": 1, "name": "toto", "email": "toto@example.com"} diff --git a/docs/examples/usage/usage_framework_integrations_13.py b/docs/examples/usage/usage_framework_integrations_13.py index ca200cbb..26e2dc1d 100644 --- a/docs/examples/usage/usage_framework_integrations_13.py +++ b/docs/examples/usage/usage_framework_integrations_13.py @@ -3,6 +3,8 @@ # Main database +from typing import Annotated + from fastapi import Depends, FastAPI from sqlspec import AsyncDriverAdapterBase, SQLSpec @@ -32,8 +34,8 @@ async def get_analytics_db(): # Use in handlers @app.get("/report") async def generate_report( - main_db: AsyncDriverAdapterBase = Depends(get_main_db), - analytics_db: AsyncDriverAdapterBase = Depends(get_analytics_db), + main_db: Annotated[AsyncDriverAdapterBase, Depends(get_main_db)], + analytics_db: Annotated[AsyncDriverAdapterBase, Depends(get_analytics_db)], ) -> dict: users = await main_db.execute("SELECT COUNT(*) FROM users") events = await analytics_db.execute("SELECT COUNT(*) FROM events") diff --git a/docs/examples/usage/usage_framework_integrations_16.py b/docs/examples/usage/usage_framework_integrations_16.py index 0b8f4a8e..dd6d350a 100644 --- a/docs/examples/usage/usage_framework_integrations_16.py +++ b/docs/examples/usage/usage_framework_integrations_16.py @@ -1,5 +1,10 @@ +__all__ = ("add_db_session", "cleanup_db_session", "list_users", "test_stub") # start-example -__all__ = ("add_db_session", "cleanup_db_session", "list_users", "test_stub" ) +import json + +from fastapi import FastAPI, Request + +app = FastAPI() @app.middleware("request") diff --git a/docs/examples/usage/usage_framework_integrations_18.py b/docs/examples/usage/usage_framework_integrations_18.py index 0725eb5c..587a8d65 100644 --- a/docs/examples/usage/usage_framework_integrations_18.py +++ b/docs/examples/usage/usage_framework_integrations_18.py @@ -1,5 +1,5 @@ # start-example -__all__ = ("close_db", "get_db", "get_user", "test_stub" ) +__all__ = ("close_db", "get_db", "get_user", "test_stub") def get_db(): diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py index 025f6919..6cc55e97 100644 --- a/docs/examples/usage/usage_framework_integrations_23.py +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -7,6 +7,8 @@ from fastapi import FastAPI +app = FastAPI() + @asynccontextmanager async def lifespan(app: FastAPI): diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py index 46d03077..46c7eb5e 100644 --- a/docs/examples/usage/usage_framework_integrations_26.py +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -3,10 +3,14 @@ # Good: Separate repository layer -from fastapi import Depends +from typing import Annotated + +from fastapi import Depends, FastAPI from sqlspec import AsyncDriverAdapterBase +app = FastAPI() + class UserRepository: def __init__(self, db: AsyncDriverAdapterBase) -> None: @@ -19,7 +23,7 @@ async def get_user(self, user_id: int): # Use in handlers @app.get("/users/{user_id}") -async def get_user(user_id: int, db: AsyncDriverAdapterBase = Depends(get_db)): +async def get_user(user_id: int, db: Annotated[AsyncDriverAdapterBase, Depends(get_db)]): repo = UserRepository(db) return await repo.get_user(user_id) diff --git a/docs/examples/usage/usage_framework_integrations_3.py b/docs/examples/usage/usage_framework_integrations_3.py index e61d16e7..230db783 100644 --- a/docs/examples/usage/usage_framework_integrations_3.py +++ b/docs/examples/usage/usage_framework_integrations_3.py @@ -1,13 +1,13 @@ -# start-example from litestar import post from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase -__all__ = ("create_user", "test_stub" ) +__all__ = ("create_user", "test_stub") +# start-example spec = SQLSpec() db = spec.add_config( AsyncpgConfig( From d9548b771a633eb0246bafcc75d9b9394b88d5e9 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 19 Nov 2025 18:43:28 +0100 Subject: [PATCH 09/13] fix imports, --- .../usage/usage_framework_integrations_18.py | 5 ++++- .../usage/usage_framework_integrations_23.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_18.py b/docs/examples/usage/usage_framework_integrations_18.py index 587a8d65..3a555a1b 100644 --- a/docs/examples/usage/usage_framework_integrations_18.py +++ b/docs/examples/usage/usage_framework_integrations_18.py @@ -1,5 +1,8 @@ -# start-example __all__ = ("close_db", "get_db", "get_user", "test_stub") +# start-example +from flask import Flask + +app = Flask(__name__) def get_db(): diff --git a/docs/examples/usage/usage_framework_integrations_23.py b/docs/examples/usage/usage_framework_integrations_23.py index 6cc55e97..a6041738 100644 --- a/docs/examples/usage/usage_framework_integrations_23.py +++ b/docs/examples/usage/usage_framework_integrations_23.py @@ -1,13 +1,14 @@ -# start-example __all__ = ("close_pools", "lifespan", "test_stub") - - +# start-example # FastAPI from contextlib import asynccontextmanager from fastapi import FastAPI +from sanic import Sanic -app = FastAPI() +from sqlspec import SQLSpec + +spec = SQLSpec() @asynccontextmanager @@ -17,6 +18,9 @@ async def lifespan(app: FastAPI): # Sanic +app = Sanic("sqlspec") + + @app.before_server_stop async def close_pools(app, loop) -> None: await spec.close_all_pools() From 4ee42a2c3d91e5e9840953cfd1890791ee54a5fc Mon Sep 17 00:00:00 2001 From: euri10 Date: Thu, 20 Nov 2025 14:12:47 +0100 Subject: [PATCH 10/13] lint --- docs/examples/usage/usage_framework_integrations_1.py | 4 +--- docs/examples/usage/usage_framework_integrations_17.py | 2 +- docs/examples/usage/usage_framework_integrations_20.py | 2 +- docs/examples/usage/usage_framework_integrations_24.py | 2 +- docs/examples/usage/usage_framework_integrations_9.py | 4 +--- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_1.py b/docs/examples/usage/usage_framework_integrations_1.py index 344a5a63..cfd8a686 100644 --- a/docs/examples/usage/usage_framework_integrations_1.py +++ b/docs/examples/usage/usage_framework_integrations_1.py @@ -1,3 +1,4 @@ +__all__ = ("index", "test_app_exists") # start-example from litestar import Litestar, get @@ -5,9 +6,6 @@ from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.extensions.litestar import SQLSpecPlugin -__all__ = ("index", "test_app_exists" ) - - # Configure database and create plugin spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/mydb", "min_size": 10, "max_size": 20})) diff --git a/docs/examples/usage/usage_framework_integrations_17.py b/docs/examples/usage/usage_framework_integrations_17.py index f71a1dca..5e099372 100644 --- a/docs/examples/usage/usage_framework_integrations_17.py +++ b/docs/examples/usage/usage_framework_integrations_17.py @@ -4,7 +4,7 @@ from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig -__all__ = ("test_stub", ) +__all__ = ("test_stub",) app = Flask(__name__) diff --git a/docs/examples/usage/usage_framework_integrations_20.py b/docs/examples/usage/usage_framework_integrations_20.py index 8a3ae5d3..a5dfb473 100644 --- a/docs/examples/usage/usage_framework_integrations_20.py +++ b/docs/examples/usage/usage_framework_integrations_20.py @@ -1,7 +1,7 @@ # start-example from contextvars import ContextVar -__all__ = ("cleanup_session", "get_session", "test_stub" ) +__all__ = ("cleanup_session", "get_session", "test_stub") db_session: ContextVar = ContextVar("db_session", default=None) diff --git a/docs/examples/usage/usage_framework_integrations_24.py b/docs/examples/usage/usage_framework_integrations_24.py index fa31dce9..a2d07373 100644 --- a/docs/examples/usage/usage_framework_integrations_24.py +++ b/docs/examples/usage/usage_framework_integrations_24.py @@ -1,5 +1,5 @@ # start-example -__all__ = ("get_db", "test_stub" ) +__all__ = ("get_db", "test_stub") # Inject sessions, not global instances diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py index 14ce7dee..f4258158 100644 --- a/docs/examples/usage/usage_framework_integrations_9.py +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -2,9 +2,7 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig -__all__ = ("test_stub", ) - - +_all__ = ("test_stub",) spec = SQLSpec() db = spec.add_config( AsyncpgConfig( From 0577a90342f282d60e1d93a328e8d7b39005be3d Mon Sep 17 00:00:00 2001 From: euri10 Date: Fri, 21 Nov 2025 09:31:27 +0100 Subject: [PATCH 11/13] tmp --- .../usage/usage_framework_integrations_3.py | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_3.py b/docs/examples/usage/usage_framework_integrations_3.py index 230db783..ab95c9d5 100644 --- a/docs/examples/usage/usage_framework_integrations_3.py +++ b/docs/examples/usage/usage_framework_integrations_3.py @@ -1,8 +1,11 @@ -from litestar import post +from litestar import Litestar, post +from litestar.testing import AsyncTestClient +from pytest_databases.docker.postgres import PostgresService from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.driver import AsyncDriverAdapterBase +from sqlspec.extensions.litestar import SQLSpecPlugin __all__ = ("create_user", "test_stub") @@ -20,16 +23,38 @@ @post("/users") -async def create_user(data: dict, db_session: AsyncDriverAdapterBase) -> dict: +async def create_user(data: dict[str, str], db_session: AsyncDriverAdapterBase) -> dict: async with db_session.begin_transaction(): result = await db_session.execute( "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", data["name"], data["email"] ) - return result.one() + r = result.one() + print(r) + return r # end-example -def test_stub() -> None: - assert True +async def test_stub(postgres_service: PostgresService) -> None: + spec = SQLSpec() + config = AsyncpgConfig( + pool_config={ + "host": postgres_service.host, + "port": postgres_service.port, + "user": postgres_service.user, + "password": postgres_service.password, + "database": postgres_service.database, + } + ) + spec.add_config(config) + + sqlspec_plugin = SQLSpecPlugin(sqlspec=spec) + + async with spec.provide_session(config) as session: + await session.execute("""CREATE TABLE users_fi3(id integer primary key, name text, email text)""") + await session.execute("""insert into users_fi3(id, name, email) values (1, 'Alice', 'alice@example.com')""") + app = Litestar(route_handlers=[create_user], plugins=[sqlspec_plugin], debug=True) + async with AsyncTestClient(app) as client: + response = await client.post("/users", json={"name": "toto", "email": "toto@example.com"}) + assert response.json() == {"id": 1, "name": "toto", "email": "toto@example.com"} From 2d229755436c0dadd8d4ac471bcb4b94a3d2e24a Mon Sep 17 00:00:00 2001 From: euri10 Date: Fri, 21 Nov 2025 13:52:40 +0100 Subject: [PATCH 12/13] begin_transaction exists ? --- .../usage/usage_framework_integrations_12.py | 54 +++++++++++++------ .../usage/usage_framework_integrations_9.py | 3 ++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_12.py b/docs/examples/usage/usage_framework_integrations_12.py index f1c66778..09ba6067 100644 --- a/docs/examples/usage/usage_framework_integrations_12.py +++ b/docs/examples/usage/usage_framework_integrations_12.py @@ -1,32 +1,54 @@ # start-example +from litestar.testing import AsyncTestClient + __all__ = ("create_user", "test_stub") -from typing import Annotated +from collections.abc import AsyncGenerator +from typing import Annotated, Any from fastapi import Depends, FastAPI +from pytest_databases.docker.postgres import PostgresService + +from sqlspec import AsyncDriverAdapterBase, SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig -from sqlspec import AsyncDriverAdapterBase -app = FastAPI() +async def test_stub(postgres_service: PostgresService) -> None: + app = FastAPI() + spec = SQLSpec() -@app.post("/users") -async def create_user(user_data: dict, db: Annotated[AsyncDriverAdapterBase, Depends(get_db_session)]) -> dict: - async with db.begin_transaction(): - result = await db.execute( - "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", user_data["name"], user_data["email"] - ) + config = AsyncpgConfig( + pool_config={ + "host": postgres_service.host, + "port": postgres_service.port, + "user": postgres_service.user, + "password": postgres_service.password, + "database": postgres_service.database, + } + ) - user_id = result.scalar() + async def get_db_session() -> AsyncGenerator[AsyncDriverAdapterBase, None]: + async with spec.provide_session(config) as session: + yield session - # Additional operations in same transaction - await db.execute("INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", "user_created", user_id) + @app.post("/users") + async def create_user( + user_data: dict[str, str], db: Annotated[AsyncDriverAdapterBase, Depends(get_db_session)] + ) -> dict[str, Any]: + async with db.begin_transaction(): + result = await db.execute( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", user_data["name"], user_data["email"] + ) - return result.one() + user_id = result.scalar() + # Additional operations in same transaction + await db.execute("INSERT INTO audit_log (action, user_id) VALUES ($1, $2)", "user_created", user_id) -# end-example + return result.one() + # end-example -def test_stub() -> None: - assert True + async with AsyncTestClient(app) as client: + await client.post("/users", json={"name": "bernard", "email": "bernard@example.com"}) diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py index f4258158..8e631f74 100644 --- a/docs/examples/usage/usage_framework_integrations_9.py +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -2,6 +2,9 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig +__all__ = ("test_stub", ) + + _all__ = ("test_stub",) spec = SQLSpec() db = spec.add_config( From 1754256329ed06a52fc4fb2eb664fb88d1ed8756 Mon Sep 17 00:00:00 2001 From: euri10 Date: Fri, 21 Nov 2025 14:09:42 +0100 Subject: [PATCH 13/13] fix 26 --- .../usage/usage_framework_integrations_26.py | 79 ++++++++++++------- .../usage/usage_framework_integrations_9.py | 2 +- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/docs/examples/usage/usage_framework_integrations_26.py b/docs/examples/usage/usage_framework_integrations_26.py index 46c7eb5e..acf06aa1 100644 --- a/docs/examples/usage/usage_framework_integrations_26.py +++ b/docs/examples/usage/usage_framework_integrations_26.py @@ -1,35 +1,56 @@ # start-example __all__ = ("UserRepository", "get_user", "test_stub") - # Good: Separate repository layer -from typing import Annotated +from collections.abc import AsyncGenerator +from typing import Annotated, Any from fastapi import Depends, FastAPI - -from sqlspec import AsyncDriverAdapterBase - -app = FastAPI() - - -class UserRepository: - def __init__(self, db: AsyncDriverAdapterBase) -> None: - self.db = db - - async def get_user(self, user_id: int): - result = await self.db.execute("SELECT * FROM users WHERE id = $1", user_id) - return result.one() - - -# Use in handlers -@app.get("/users/{user_id}") -async def get_user(user_id: int, db: Annotated[AsyncDriverAdapterBase, Depends(get_db)]): - repo = UserRepository(db) - return await repo.get_user(user_id) - - -# end-example - - -def test_stub() -> None: - assert True +from litestar.testing import AsyncTestClient +from pytest_databases.docker.postgres import PostgresService + +from sqlspec import AsyncDriverAdapterBase, SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + + +async def test_stub(postgres_service: PostgresService) -> None: + + app = FastAPI() + spec = SQLSpec() + + config = AsyncpgConfig( + pool_config={ + "host": postgres_service.host, + "port": postgres_service.port, + "user": postgres_service.user, + "password": postgres_service.password, + "database": postgres_service.database, + } + ) + async with spec.provide_session(config) as session: + await session.execute("""create table users_fi26(id integer primary key, name text, email text)""") + await session.execute("""insert into users_fi26(id,name, email) values (1,'dora','dora@example.com')""") + + async def get_db() -> AsyncGenerator[AsyncDriverAdapterBase]: + async with spec.provide_session(config) as session: + yield session + + class UserRepository: + def __init__(self, db: AsyncDriverAdapterBase) -> None: + self.db = db + + async def get_user(self, user_id: int) -> dict[str, Any]: + result = await self.db.execute("SELECT * FROM users_fi26 WHERE id = $1", user_id) + return result.one() + + # Use in handlers + @app.get("/users/{user_id}") + async def get_user(user_id: int, db: Annotated[AsyncDriverAdapterBase, Depends(get_db)]) -> dict[str, Any]: + repo = UserRepository(db) + return await repo.get_user(user_id) + + # end-example + + async with AsyncTestClient(app) as client: + response = await client.get("/users/1") + assert response.json() == {"id": 1, "name": "dora", "email": "dora@example.com"} diff --git a/docs/examples/usage/usage_framework_integrations_9.py b/docs/examples/usage/usage_framework_integrations_9.py index 8e631f74..e5371486 100644 --- a/docs/examples/usage/usage_framework_integrations_9.py +++ b/docs/examples/usage/usage_framework_integrations_9.py @@ -2,7 +2,7 @@ from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig -__all__ = ("test_stub", ) +__all__ = ("test_stub",) _all__ = ("test_stub",)