Skip to content

Commit 3061f72

Browse files
authored
docs: use literalinclude instead of code-block on quickstart (#163)
- put the `code-block` in their own `quickstart_#` files and use `literalinclude` in the quickstart.rst - add `/docs/examples/quickstart` to pytest discover path - add a test on each block, test that wont be shown in docs thanks to lines and dedent directives
1 parent 9521c6d commit 3061f72

File tree

15 files changed

+662
-500
lines changed

15 files changed

+662
-500
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repos:
1717
- id: mixed-line-ending
1818
- id: trailing-whitespace
1919
- repo: https://github.com/charliermarsh/ruff-pre-commit
20-
rev: "v0.14.3"
20+
rev: "v0.14.4"
2121
hooks:
2222
- id: ruff
2323
args: ["--fix"]

docs/examples/quickstart/__init__.py

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Generator
4+
5+
import pytest
6+
from pytest_databases.docker.postgres import PostgresService
7+
8+
pytest_plugins = ["pytest_databases.docker.postgres"]
9+
10+
11+
@pytest.fixture(scope="session", autouse=True)
12+
def quickstart_postgres_env(postgres_service: PostgresService) -> Generator[None, None, None]:
13+
"""Expose pytest-databases Postgres settings to docs quickstart examples."""
14+
15+
patcher = pytest.MonkeyPatch()
16+
patcher.setenv("SQLSPEC_QUICKSTART_PG_HOST", postgres_service.host)
17+
patcher.setenv("SQLSPEC_QUICKSTART_PG_PORT", str(postgres_service.port))
18+
patcher.setenv("SQLSPEC_QUICKSTART_PG_USER", postgres_service.user)
19+
patcher.setenv("SQLSPEC_QUICKSTART_PG_PASSWORD", postgres_service.password)
20+
patcher.setenv("SQLSPEC_QUICKSTART_PG_DATABASE", postgres_service.database)
21+
yield
22+
patcher.undo()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from sqlspec import SQLSpec
2+
from sqlspec.adapters.sqlite import SqliteConfig
3+
4+
__all__ = ("test_quickstart_1",)
5+
6+
7+
def test_quickstart_1() -> None:
8+
# Create SQLSpec instance and configure database
9+
db_manager = SQLSpec()
10+
db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"}))
11+
12+
# Execute a query
13+
with db_manager.provide_session(db) as session:
14+
result = session.execute("SELECT 'Hello, SQLSpec!' as message")
15+
print(result.get_first()) # {'message': 'Hello, SQLSpec!'}
16+
17+
assert result.get_first() == {"message": "Hello, SQLSpec!"}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from sqlspec import SQLSpec
2+
from sqlspec.adapters.sqlite import SqliteConfig
3+
4+
__all__ = ("test_quickstart_2",)
5+
6+
7+
def test_quickstart_2() -> None:
8+
db_manager = SQLSpec()
9+
db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"}))
10+
11+
with db_manager.provide_session(db) as session:
12+
# Create a table
13+
_ = session.execute("""
14+
CREATE TABLE users (
15+
id INTEGER PRIMARY KEY,
16+
name TEXT NOT NULL,
17+
email TEXT UNIQUE NOT NULL
18+
)
19+
""")
20+
21+
# Insert data
22+
_ = session.execute("INSERT INTO users (name, email) VALUES (?, ?)", "Alice", "alice@example.com")
23+
24+
# Insert multiple rows
25+
_ = session.execute_many(
26+
"INSERT INTO users (name, email) VALUES (?, ?)",
27+
[("Bob", "bob@example.com"), ("Charlie", "charlie@example.com")],
28+
)
29+
30+
# Query all users
31+
users = session.select("SELECT * FROM users")
32+
print(f"All users: {users}")
33+
34+
# Query single user
35+
alice = session.select_one("SELECT * FROM users WHERE name = ?", "Alice")
36+
print(f"Alice: {alice}")
37+
38+
# Query scalar value
39+
count = session.select_value("SELECT COUNT(*) FROM users")
40+
print(f"Total users: {count}")
41+
42+
assert len(users) == 3 # noqa: PLR2004
43+
assert alice == {"id": 1, "name": "Alice", "email": "alice@example.com"}
44+
assert count == 3 # noqa: PLR2004
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from pydantic import BaseModel
2+
3+
from sqlspec import SQLSpec
4+
from sqlspec.adapters.sqlite import SqliteConfig
5+
6+
__all__ = ("User",)
7+
8+
9+
class User(BaseModel):
10+
id: int
11+
name: str
12+
email: str
13+
14+
15+
def test_quickstart_3() -> None:
16+
db_manager = SQLSpec()
17+
db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"}))
18+
19+
with db_manager.provide_session(db) as session:
20+
# Setup
21+
_ = session.execute("""
22+
CREATE TABLE users (id INTEGER, name TEXT, email TEXT)
23+
""")
24+
_ = session.execute("INSERT INTO users VALUES (?, ?, ?)", 1, "Alice", "alice@example.com")
25+
26+
# Type-safe query - returns User instance
27+
user = session.select_one("SELECT * FROM users WHERE id = ?", 1, schema_type=User)
28+
29+
# Now you have type hints and autocomplete!
30+
print(f"User: {user.name} ({user.email})") # IDE knows these fields exist
31+
32+
# Multiple results
33+
all_users = session.select("SELECT * FROM users", schema_type=User)
34+
for u in all_users:
35+
print(f"User: {u.name}") # Each item is a typed User
36+
37+
assert user == User(id=1, name="Alice", email="alice@example.com")
38+
assert len(all_users) == 1
39+
assert isinstance(all_users[0], User)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from pydantic import BaseModel
2+
3+
from sqlspec import SQLSpec
4+
from sqlspec.adapters.aiosqlite import AiosqliteConfig
5+
6+
__all__ = ("User", "test_quickstart_4")
7+
8+
9+
class User(BaseModel):
10+
id: int
11+
name: str
12+
email: str
13+
14+
15+
async def test_quickstart_4() -> None:
16+
db_manager = SQLSpec()
17+
db = db_manager.add_config(AiosqliteConfig(pool_config={"database": ":memory:"}))
18+
19+
async with db_manager.provide_session(db) as session:
20+
# Create table
21+
_ = await session.execute("""
22+
CREATE TABLE users (id INTEGER, name TEXT, email TEXT)
23+
""")
24+
25+
# Insert data
26+
_ = await session.execute("INSERT INTO users VALUES (?, ?, ?)", 1, "Alice", "alice@example.com")
27+
28+
# Type-safe async query
29+
user = await session.select_one("SELECT * FROM users WHERE id = ?", 1, schema_type=User)
30+
31+
print(f"User: {user.name}")
32+
33+
assert user == User(id=1, name="Alice", email="alice@example.com")
34+
assert isinstance(user, User)
35+
assert user.name == "Alice"
36+
assert user.email == "alice@example.com"
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import os
2+
from typing import Any
3+
4+
from pydantic import BaseModel
5+
6+
from sqlspec import SQLSpec
7+
from sqlspec.adapters.asyncpg import AsyncpgConfig
8+
9+
__all__ = ("User", "test_quickstart_5")
10+
11+
12+
class User(BaseModel):
13+
id: int
14+
name: str
15+
email: str
16+
17+
18+
def _pool_config() -> "dict[str, Any]":
19+
return {
20+
"host": os.getenv("SQLSPEC_QUICKSTART_PG_HOST", "localhost"),
21+
"port": int(os.getenv("SQLSPEC_QUICKSTART_PG_PORT", "5432")),
22+
"user": os.getenv("SQLSPEC_QUICKSTART_PG_USER", "postgres"),
23+
"password": os.getenv("SQLSPEC_QUICKSTART_PG_PASSWORD", "postgres"),
24+
"database": os.getenv("SQLSPEC_QUICKSTART_PG_DATABASE", "mydb"),
25+
}
26+
27+
28+
async def _seed_users(session: Any) -> None:
29+
await session.execute(
30+
"""
31+
CREATE TABLE IF NOT EXISTS users (
32+
id INTEGER PRIMARY KEY,
33+
name TEXT NOT NULL,
34+
email TEXT NOT NULL
35+
)
36+
"""
37+
)
38+
await session.execute("TRUNCATE TABLE users")
39+
await session.execute("INSERT INTO users (id, name, email) VALUES ($1, $2, $3)", 1, "Alice", "alice@example.com")
40+
41+
42+
async def test_quickstart_5() -> None:
43+
db_manager = SQLSpec()
44+
db = db_manager.add_config(AsyncpgConfig(pool_config=_pool_config()))
45+
46+
async with db_manager.provide_session(db) as session:
47+
await _seed_users(session)
48+
49+
# PostgreSQL uses $1, $2 for parameters instead of ?
50+
user = await session.select_one("SELECT * FROM users WHERE id = $1", 1, schema_type=User)
51+
print(f"User: {user.name}")
52+
53+
assert user == User(id=1, name="Alice", email="alice@example.com")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from sqlspec import SQLSpec
2+
from sqlspec.adapters.duckdb import DuckDBConfig
3+
from sqlspec.adapters.sqlite import SqliteConfig
4+
5+
__all__ = ("test_quickstart_6",)
6+
7+
8+
def test_quickstart_6() -> None:
9+
db_manager = SQLSpec()
10+
11+
# Register multiple databases
12+
sqlite_db = db_manager.add_config(SqliteConfig(pool_config={"database": "app.db"}))
13+
duckdb_db = db_manager.add_config(DuckDBConfig(pool_config={"database": "analytics.duckdb"}))
14+
15+
# Use different databases
16+
with db_manager.provide_session(sqlite_db) as sqlite_session:
17+
users = sqlite_session.select("SELECT 1")
18+
19+
with db_manager.provide_session(duckdb_db) as duckdb_session:
20+
analytics = duckdb_session.select("SELECT 1")
21+
22+
assert isinstance(users, list)
23+
assert isinstance(analytics, list)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from sqlspec import SQLSpec
2+
from sqlspec.adapters.sqlite import SqliteConfig
3+
4+
__all__ = ("test_quickstart_7",)
5+
6+
7+
def test_quickstart_7() -> None:
8+
db_manager = SQLSpec()
9+
10+
db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"}))
11+
12+
# Transaction committed on successful exit
13+
with db_manager.provide_session(db) as session:
14+
session.begin()
15+
_ = session.execute("""
16+
CREATE TABLE IF NOT EXISTS users (
17+
id INTEGER PRIMARY KEY,
18+
name TEXT NOT NULL
19+
)
20+
""")
21+
_ = session.execute("""
22+
CREATE TABLE IF NOT EXISTS orders (
23+
id INTEGER PRIMARY KEY,
24+
user_name TEXT NOT NULL
25+
)
26+
""")
27+
_ = session.execute("DELETE FROM users")
28+
_ = session.execute("DELETE FROM orders")
29+
_ = session.execute("INSERT INTO users (name) VALUES (?)", "Alice")
30+
_ = session.execute("INSERT INTO orders (user_name) VALUES (?)", "Alice")
31+
session.commit()
32+
33+
with db_manager.provide_session(db) as session:
34+
session.begin()
35+
_ = session.execute("INSERT INTO users (name) VALUES (?)", "Bob")
36+
session.rollback()
37+
38+
with db_manager.provide_session(db) as session:
39+
alice = session.select_one_or_none("SELECT * FROM users WHERE name = ?", "Alice")
40+
bob = session.select_one_or_none("SELECT * FROM users WHERE name = ?", "Bob")
41+
42+
assert alice is not None
43+
assert alice["name"] == "Alice"
44+
assert bob is None

0 commit comments

Comments
 (0)