diff --git a/docs/examples/usage/__init__.py b/docs/examples/usage/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/examples/usage/usage_index_1.py b/docs/examples/usage/usage_index_1.py new file mode 100644 index 00000000..a8a48516 --- /dev/null +++ b/docs/examples/usage/usage_index_1.py @@ -0,0 +1,25 @@ +from sqlspec import SQLSpec +from sqlspec.adapters.sqlite import SqliteConfig + +__all__ = ("test_index_1",) + + +def test_index_1() -> None: + db_manager = SQLSpec() + db = db_manager.add_config(SqliteConfig()) + + with db_manager.provide_session(db) as session: + _ = session.execute( + """ + CREATE TABLE users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL + ) + """ + ) + _ = session.execute("INSERT INTO users VALUES (?, ?)", 1, "alice") + + with db_manager.provide_session(db) as session: + user = session.select_one("SELECT * FROM users WHERE id = ?", 1) + + assert user == {"id": 1, "name": "alice"} diff --git a/docs/examples/usage/usage_index_2.py b/docs/examples/usage/usage_index_2.py new file mode 100644 index 00000000..aac40079 --- /dev/null +++ b/docs/examples/usage/usage_index_2.py @@ -0,0 +1,34 @@ +from sqlspec import SQLSpec, sql +from sqlspec.adapters.sqlite import SqliteConfig + +__all__ = ("test_index_2",) + + +def test_index_2() -> None: + db_manager = SQLSpec() + db = db_manager.add_config(SqliteConfig()) + query = sql.select("id", "name", "email").from_("users").where("active = ?") + + with db_manager.provide_session(db) as session: + _ = session.execute( + """ + CREATE TABLE users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + email TEXT, + active BOOLEAN NOT NULL DEFAULT 1 + ) + """ + ) + _ = session.execute( + """ + INSERT INTO users VALUES + (1, 'alice', 'alice@example.com', 1), + (2, 'bob', 'bob@example.com', 0), + (3, 'carol', 'carol@example.com', 1) + """ + ) + users = session.select(query, True) # noqa: FBT003 + + names = [user["name"] for user in users] + assert names == ["alice", "carol"] diff --git a/docs/examples/usage/usage_index_3.py b/docs/examples/usage/usage_index_3.py new file mode 100644 index 00000000..3e7db4bf --- /dev/null +++ b/docs/examples/usage/usage_index_3.py @@ -0,0 +1,42 @@ +from pathlib import Path + +from sqlspec import SQLSpec +from sqlspec.adapters.sqlite import SqliteConfig +from sqlspec.loader import SQLFileLoader + +__all__ = ("test_index_3",) + + +QUERIES_PATH = Path(__file__).parent.parent / "queries" / "users.sql" + + +def test_index_3() -> None: + db_manager = SQLSpec() + db = db_manager.add_config(SqliteConfig()) + loader = SQLFileLoader() + loader.load_sql(QUERIES_PATH) + get_user_by_id = loader.get_sql("get_user_by_id") + + with db_manager.provide_session(db) as session: + _ = session.execute( + """ + CREATE TABLE users ( + id INTEGER PRIMARY KEY, + username TEXT NOT NULL, + email TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """ + ) + _ = session.execute( + """ + INSERT INTO users(id, username, email) + VALUES (1, 'alice', 'alice@example.com'), + (2, 'bob', 'bob@example.com') + """ + ) + user = session.select_one(get_user_by_id, user_id=2) + + assert user["username"] == "bob" + assert user["email"] == "bob@example.com" diff --git a/docs/usage/index.rst b/docs/usage/index.rst index 3961c70d..2f31063f 100644 --- a/docs/usage/index.rst +++ b/docs/usage/index.rst @@ -51,39 +51,27 @@ Quick Reference **Basic Query Execution** -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig - - spec = SQLSpec() - db = spec.add_config(SqliteConfig()) - - with spec.provide_session(db) as session: - result = session.execute("SELECT * FROM users WHERE id = ?", 1) - user = result.one() +.. literalinclude:: /examples/usage/usage_index_1.py + :language: python + :caption: ``basic query execution`` + :lines: 1-23 + :dedent: 4 **Using the Query Builder** -.. code-block:: python - - from sqlspec import sql - - query = sql.select("id", "name", "email").from_("users").where("active = ?") - result = session.execute(query, True) - users = result.all() +.. literalinclude:: /examples/usage/usage_index_2.py + :language: python + :caption: ``using the query builder`` + :lines: 1-32 + :dedent: 4 **Loading from SQL Files** -.. code-block:: python - - from sqlspec.loader import SQLFileLoader - - loader = SQLFileLoader() - loader.load_sql("queries/users.sql") - - user_query = loader.get_sql("get_user_by_id", user_id=123) - result = session.execute(user_query) +.. literalinclude:: /examples/usage/usage_index_3.py + :language: python + :caption: ``loading from sql files`` + :lines: 1-39 + :dedent: 4 Next Steps ---------- diff --git a/pyproject.toml b/pyproject.toml index be03467e..b6e7631f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -287,7 +287,7 @@ exclude_lines = [ addopts = ["-q", "-ra"] asyncio_default_fixture_loop_scope = "function" asyncio_mode = "auto" -python_files = ["test_*.py", "quickstart_*.py"] +python_files = ["test_*.py", "quickstart_*.py", "usage_*.py"] filterwarnings = [ "ignore::DeprecationWarning:pkg_resources.*", "ignore:pkg_resources is deprecated as an API:DeprecationWarning", @@ -331,7 +331,7 @@ markers = [ "pymysql: marks tests using pymysql", "psqlpy: marks tests using psqlpy", ] -testpaths = ["tests", "docs/examples/quickstart"] +testpaths = ["tests", "docs/examples/quickstart", "docs/examples/usage"] [tool.mypy] exclude = ["tmp/", ".tmp/", ".bugs/"] diff --git a/specs/guides/docs_examples_alignment.md b/specs/guides/docs_examples_alignment.md index 54588335..148ac5c8 100644 --- a/specs/guides/docs_examples_alignment.md +++ b/specs/guides/docs_examples_alignment.md @@ -18,11 +18,11 @@ ## Testing Command Examples ```bash -uv run pytest docs/examples/quickstart -q +uv run pytest docs/examples/quickstart docs/examples/usage -q ``` - `docs/examples/quickstart/conftest.py` sets `SQLSPEC_QUICKSTART_PG_*` from `pytest-databases` so `quickstart_5.py` stays copy/paste friendly in the docs. -- Async or adapter-specific samples no longer need separate commands—pytest collects `quickstart_4.py` and `quickstart_5.py` automatically via `python_files`. +- Usage samples are function-based (`usage_*.py`) and collected automatically because `python_files` now includes that pattern. - Prefer smaller batches (per topic/section) to keep feedback loops fast. ## Review Checklist