From cbd7023f0ea121feb3ca73a1144d6a96671efb32 Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 13:12:22 +0100 Subject: [PATCH 01/18] docs: externalise migration CLI and add psycopg literalinclude - Added docs/examples/usage/test_configuration_22.txt with migration CLI commands - Replaced inline Migration CLI block in docs/usage/configuration.rst with a literalinclude to the new file - Inserted psycopg literalinclude in docs/usage/configuration.rst (reusing examples/usage/test_configuration_5.py) - Updated documentation TODOs accordingly Signed-off-by: euri10 --- AGENTS.md | 26 ++ docs/examples/usage/test_configuration_1.py | 14 + docs/examples/usage/test_configuration_10.py | 12 + docs/examples/usage/test_configuration_11.py | 6 + docs/examples/usage/test_configuration_12.py | 21 + docs/examples/usage/test_configuration_13.py | 21 + docs/examples/usage/test_configuration_14.py | 24 ++ docs/examples/usage/test_configuration_15.py | 22 + docs/examples/usage/test_configuration_16.py | 29 ++ docs/examples/usage/test_configuration_17.py | 28 ++ docs/examples/usage/test_configuration_18.py | 19 + docs/examples/usage/test_configuration_19.py | 27 ++ docs/examples/usage/test_configuration_2.py | 14 + docs/examples/usage/test_configuration_20.py | 29 ++ docs/examples/usage/test_configuration_21.py | 28 ++ docs/examples/usage/test_configuration_22.py | 18 + docs/examples/usage/test_configuration_22.txt | 9 + docs/examples/usage/test_configuration_23.py | 42 ++ docs/examples/usage/test_configuration_24.py | 25 ++ docs/examples/usage/test_configuration_25.py | 15 + docs/examples/usage/test_configuration_26.py | 16 + docs/examples/usage/test_configuration_27.py | 29 ++ docs/examples/usage/test_configuration_3.py | 16 + docs/examples/usage/test_configuration_4.py | 18 + docs/examples/usage/test_configuration_5.py | 6 + docs/examples/usage/test_configuration_6.py | 18 + docs/examples/usage/test_configuration_7.py | 14 + docs/examples/usage/test_configuration_8.py | 14 + docs/examples/usage/test_configuration_9.py | 8 + docs/usage/configuration.rst | 388 ++++-------------- pyproject.toml | 2 +- 31 files changed, 644 insertions(+), 314 deletions(-) create mode 100644 docs/examples/usage/test_configuration_1.py create mode 100644 docs/examples/usage/test_configuration_10.py create mode 100644 docs/examples/usage/test_configuration_11.py create mode 100644 docs/examples/usage/test_configuration_12.py create mode 100644 docs/examples/usage/test_configuration_13.py create mode 100644 docs/examples/usage/test_configuration_14.py create mode 100644 docs/examples/usage/test_configuration_15.py create mode 100644 docs/examples/usage/test_configuration_16.py create mode 100644 docs/examples/usage/test_configuration_17.py create mode 100644 docs/examples/usage/test_configuration_18.py create mode 100644 docs/examples/usage/test_configuration_19.py create mode 100644 docs/examples/usage/test_configuration_2.py create mode 100644 docs/examples/usage/test_configuration_20.py create mode 100644 docs/examples/usage/test_configuration_21.py create mode 100644 docs/examples/usage/test_configuration_22.py create mode 100644 docs/examples/usage/test_configuration_22.txt create mode 100644 docs/examples/usage/test_configuration_23.py create mode 100644 docs/examples/usage/test_configuration_24.py create mode 100644 docs/examples/usage/test_configuration_25.py create mode 100644 docs/examples/usage/test_configuration_26.py create mode 100644 docs/examples/usage/test_configuration_27.py create mode 100644 docs/examples/usage/test_configuration_3.py create mode 100644 docs/examples/usage/test_configuration_4.py create mode 100644 docs/examples/usage/test_configuration_5.py create mode 100644 docs/examples/usage/test_configuration_6.py create mode 100644 docs/examples/usage/test_configuration_7.py create mode 100644 docs/examples/usage/test_configuration_8.py create mode 100644 docs/examples/usage/test_configuration_9.py diff --git a/AGENTS.md b/AGENTS.md index c64e8f46..e339ec7e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -399,6 +399,24 @@ When processing user input that may be incomplete or malformed, use a two-tier a **Implementation Pattern:** +## Recent edit: Configuration examples (2025-11-04) + +- Updated docs/examples/usage examples: consolidated example filenames to + docs/examples/usage/test_configuration_*.py and ensured the documentation + references match the renamed examples. +- Added an explicit :lines: range and a :dedent: directive to the + literalinclude for test_configuration_23.py so Sphinx renders the snippet + with correct indentation. +- Built the Sphinx documentation (make docs) and verified HTML output was + generated successfully. Two minor warnings were reported (dedent and a + missing stylesheet copy) but they did not prevent the build. +- Updated project TODOs to reflect completed steps. + +This summary documents the small documentation and example maintenance +performed on the configuration usage guide and can be expanded into a +longer changelog entry if desired. + + ```python def parse_user_input(content: str, source: str) -> "dict[str, Result]": """Parse user input with two-tier error handling. @@ -1783,6 +1801,14 @@ class GoodDriverFeatures(TypedDict): ### Compliance Table +### Change log: configuration examples + +- Renamed documentation example references to use docs/examples/usage/test_configuration_*.py +- Added explicit :lines: ranges and :dedent: directive for the literalinclude at the top of docs/usage/configuration.rst +- Rebuilt documentation to verify the changes (make docs). Build completed with 2 warnings about dedent and a missing stylesheet; output HTML written to docs/_build/html + +### Compliance Table + Current state of all adapters (as of type-cleanup branch): | Adapter | TypedDict | Auto-Detect | enable_ Prefix | Defaults | Grade | Notes | diff --git a/docs/examples/usage/test_configuration_1.py b/docs/examples/usage/test_configuration_1.py new file mode 100644 index 00000000..5da42ad6 --- /dev/null +++ b/docs/examples/usage/test_configuration_1.py @@ -0,0 +1,14 @@ +def test_sqlite_memory_db(): + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + # Create SQLSpec instance + spec = SQLSpec() + + # Add database configuration + db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) + + # Use the database + with spec.provide_session(db) as session: + result = session.execute("SELECT 1") + assert result.fetchone()[0] == 1 + diff --git a/docs/examples/usage/test_configuration_10.py b/docs/examples/usage/test_configuration_10.py new file mode 100644 index 00000000..77704ebb --- /dev/null +++ b/docs/examples/usage/test_configuration_10.py @@ -0,0 +1,12 @@ +import asyncpg +from sqlspec.adapters.asyncpg import AsyncpgConfig + +def test_manual_pool(): + pool = { + "dsn": "postgresql://localhost/db", + "min_size": 10, + "max_size": 20 + } + db = AsyncpgConfig(pool_instance=pool) + assert db.pool_instance["max_size"] == 20 + diff --git a/docs/examples/usage/test_configuration_11.py b/docs/examples/usage/test_configuration_11.py new file mode 100644 index 00000000..b4cd1b26 --- /dev/null +++ b/docs/examples/usage/test_configuration_11.py @@ -0,0 +1,6 @@ +from sqlspec.adapters.sqlite import SqliteConfig + +def test_thread_local_connections(): + config = SqliteConfig(pool_config={"database": "test.db"}) + assert config.pool_config["database"] == "test.db" + diff --git a/docs/examples/usage/test_configuration_12.py b/docs/examples/usage/test_configuration_12.py new file mode 100644 index 00000000..cd20ecd2 --- /dev/null +++ b/docs/examples/usage/test_configuration_12.py @@ -0,0 +1,21 @@ +from sqlspec.core.statement import StatementConfig +from sqlspec.adapters.asyncpg import AsyncpgConfig + + +def test_basic_statement_config(): + statement_config = StatementConfig( + dialect="postgres", # SQLGlot dialect + enable_parsing=True, # Parse SQL into AST + enable_validation=True, # Run security/performance validators + enable_transformations=True, # Apply AST transformations + enable_caching=True, # Enable multi-tier caching + ) + + # Apply to adapter + config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + statement_config=statement_config + ) + assert config.statement_config.dialect == "postgres" + assert config.statement_config.enable_parsing is True + diff --git a/docs/examples/usage/test_configuration_13.py b/docs/examples/usage/test_configuration_13.py new file mode 100644 index 00000000..0eb2aa33 --- /dev/null +++ b/docs/examples/usage/test_configuration_13.py @@ -0,0 +1,21 @@ +from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig +from sqlspec.core.statement import StatementConfig + + +def test_parameter_style_config(): + param_config = ParameterStyleConfig( + default_parameter_style=ParameterStyle.NUMERIC, # $1, $2, ... + supported_parameter_styles={ + ParameterStyle.NUMERIC, + ParameterStyle.NAMED_COLON, # :name + }, + has_native_list_expansion=False, + needs_static_script_compilation=False, + ) + + statement_config = StatementConfig( + dialect="postgres", + parameter_config=param_config + ) + assert statement_config.parameter_config.default_parameter_style == ParameterStyle.NUMERIC + diff --git a/docs/examples/usage/test_configuration_14.py b/docs/examples/usage/test_configuration_14.py new file mode 100644 index 00000000..32573c1d --- /dev/null +++ b/docs/examples/usage/test_configuration_14.py @@ -0,0 +1,24 @@ +from sqlspec.core.parameters import ParameterStyle + + +def test_parameter_styles(): + # Question mark (SQLite, DuckDB) + qmark = ParameterStyle.QMARK # WHERE id = ? + + # Numeric (PostgreSQL, asyncpg) + numeric = ParameterStyle.NUMERIC # WHERE id = $1 + + # Named colon (Oracle, SQLite) + named_colon = ParameterStyle.NAMED_COLON # WHERE id = :id + + # Named at (BigQuery) + named_at = ParameterStyle.NAMED_AT # WHERE id = @id + + # Format/pyformat (psycopg, MySQL) + positional_pyformat = ParameterStyle.POSITIONAL_PYFORMAT # WHERE id = %s + named_pyformat = ParameterStyle.NAMED_PYFORMAT # WHERE id = %(id)s + + assert qmark == ParameterStyle.QMARK + assert numeric == ParameterStyle.NUMERIC + assert named_colon == ParameterStyle.NAMED_COLON + diff --git a/docs/examples/usage/test_configuration_15.py b/docs/examples/usage/test_configuration_15.py new file mode 100644 index 00000000..2ede1e37 --- /dev/null +++ b/docs/examples/usage/test_configuration_15.py @@ -0,0 +1,22 @@ +"""Test configuration example: Global cache configuration.""" + + +def test_global_cache_config() -> None: + """Test global cache configuration.""" + from sqlspec.core.cache import CacheConfig, update_cache_config + + cache_config = CacheConfig( + compiled_cache_enabled=True, # Cache compiled SQL + sql_cache_enabled=True, # Cache SQL strings + fragment_cache_enabled=True, # Cache SQL fragments + optimized_cache_enabled=True, # Cache optimized AST + sql_cache_size=1000, # Maximum cached SQL items + ) + + # Update global cache configuration + update_cache_config(cache_config) + + # Verify config applied + assert cache_config.sql_cache_enabled is True + assert cache_config.sql_cache_size == 1000 + diff --git a/docs/examples/usage/test_configuration_16.py b/docs/examples/usage/test_configuration_16.py new file mode 100644 index 00000000..3c5f7fd2 --- /dev/null +++ b/docs/examples/usage/test_configuration_16.py @@ -0,0 +1,29 @@ +"""Test configuration example: Per-instance cache configuration.""" + +import tempfile + + +def test_per_instance_cache_config() -> None: + """Test per-instance cache configuration.""" + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + from sqlspec.core.cache import CacheConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + # Configure cache for specific SQLSpec instance + spec = SQLSpec() + spec.update_cache_config( + CacheConfig( + sql_cache_enabled=True, + sql_cache_size=500, + ) + ) + + # Add database config + db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) + + # Use the configured spec + with spec.provide_session(db) as session: + result = session.execute("SELECT 1") + assert result is not None + diff --git a/docs/examples/usage/test_configuration_17.py b/docs/examples/usage/test_configuration_17.py new file mode 100644 index 00000000..fd416dda --- /dev/null +++ b/docs/examples/usage/test_configuration_17.py @@ -0,0 +1,28 @@ +"""Test configuration example: Cache statistics tracking.""" + +import tempfile + + +def test_cache_statistics() -> None: + """Test cache statistics tracking.""" + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + from sqlspec.core.cache import get_cache_statistics, log_cache_stats + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + spec = SQLSpec() + db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) + + # Execute some queries to generate cache activity + with spec.provide_session(db) as session: + session.execute("SELECT 1") + session.execute("SELECT 1") # Should hit cache + + # Get statistics + stats = get_cache_statistics() + assert isinstance(stats, dict) + assert "multi_level" in stats + + # Log statistics (logs to configured logger) + log_cache_stats() + diff --git a/docs/examples/usage/test_configuration_18.py b/docs/examples/usage/test_configuration_18.py new file mode 100644 index 00000000..ba673880 --- /dev/null +++ b/docs/examples/usage/test_configuration_18.py @@ -0,0 +1,19 @@ +"""Test configuration example: Cache clearing operations.""" + + +def test_clear_cache() -> None: + """Test cache clearing operations.""" + from sqlspec.core.cache import clear_all_caches, get_cache_statistics + + # Get initial statistics + stats_before = get_cache_statistics() + assert isinstance(stats_before, dict) + + # Clear all caches and reset statistics + clear_all_caches() + + # Verify caches were cleared + stats_after = get_cache_statistics() + assert isinstance(stats_after, dict) + assert "multi_level" in stats_after + diff --git a/docs/examples/usage/test_configuration_19.py b/docs/examples/usage/test_configuration_19.py new file mode 100644 index 00000000..420b184d --- /dev/null +++ b/docs/examples/usage/test_configuration_19.py @@ -0,0 +1,27 @@ +"""Test configuration example: Binding multiple database configurations.""" + +import tempfile + + +def test_binding_multiple_configs() -> None: + """Test binding multiple database configurations.""" + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + from sqlspec.adapters.asyncpg import AsyncpgConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + spec = SQLSpec() + + # Add multiple configurations + sqlite_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) + postgres_db = spec.add_config( + AsyncpgConfig(pool_config={"dsn": "postgresql://..."}) + ) + + # Use specific configuration + with spec.provide_session(sqlite_db) as session: + session.execute("SELECT 1") + + assert sqlite_db.pool_config["database"] == tmp.name + assert postgres_db.pool_config["dsn"] == "postgresql://..." + diff --git a/docs/examples/usage/test_configuration_2.py b/docs/examples/usage/test_configuration_2.py new file mode 100644 index 00000000..c4be3d42 --- /dev/null +++ b/docs/examples/usage/test_configuration_2.py @@ -0,0 +1,14 @@ +from sqlspec.adapters.sqlite import SqliteConfig + +def test_sqlite_config_setup(): + config = SqliteConfig( + pool_config={ + "database": "myapp.db", # Database file path + "timeout": 5.0, # Lock timeout in seconds + "check_same_thread": False, # Allow multi-thread access + "cached_statements": 100, # Statement cache size + "uri": False, # Enable URI mode + } + ) + assert config.pool_config["database"] == "myapp.db" + diff --git a/docs/examples/usage/test_configuration_20.py b/docs/examples/usage/test_configuration_20.py new file mode 100644 index 00000000..c5ac2bd0 --- /dev/null +++ b/docs/examples/usage/test_configuration_20.py @@ -0,0 +1,29 @@ +"""Test configuration example: Named database bindings.""" + +import tempfile + + +def test_named_bindings() -> None: + """Test named database bindings.""" + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + from sqlspec.adapters.asyncpg import AsyncpgConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + spec = SQLSpec() + + # Add with bind keys + cache_db = spec.add_config( + SqliteConfig(pool_config={"database": tmp.name}), bind_key="cache_db" + ) + main_db = spec.add_config( + AsyncpgConfig(pool_config={"dsn": "postgresql://..."}), bind_key="main_db" + ) + + # Access by bind key + with spec.provide_session("cache_db") as session: + session.execute("SELECT 1") + + assert cache_db.pool_config["database"] == tmp.name + assert main_db.pool_config["dsn"] == "postgresql://..." + diff --git a/docs/examples/usage/test_configuration_21.py b/docs/examples/usage/test_configuration_21.py new file mode 100644 index 00000000..04aed97a --- /dev/null +++ b/docs/examples/usage/test_configuration_21.py @@ -0,0 +1,28 @@ +"""Test configuration example: Basic migration configuration.""" + +import pytest + + +@pytest.mark.skipif( + not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), + reason="AsyncPG integration tests disabled", +) +def test_basic_migration_config() -> None: + """Test basic migration configuration.""" + from sqlspec.adapters.asyncpg import AsyncpgConfig + + config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + extension_config={ + "litestar": {"session_table": "custom_sessions"} # Extension settings + }, + migration_config={ + "script_location": "migrations", # Migration directory + "version_table": "alembic_version", # Version tracking table + "include_extensions": ["litestar"], # Simple string list only + }, + ) + + assert config.migration_config["script_location"] == "migrations" + assert "litestar" in config.migration_config["include_extensions"] + diff --git a/docs/examples/usage/test_configuration_22.py b/docs/examples/usage/test_configuration_22.py new file mode 100644 index 00000000..a4f49ad9 --- /dev/null +++ b/docs/examples/usage/test_configuration_22.py @@ -0,0 +1,18 @@ +from sqlspec.adapters.asyncpg import AsyncpgConfig + + +def test_basic_migration_config(): + config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + extension_config={ + "litestar": {"session_table": "custom_sessions"} # Extension settings + }, + migration_config={ + "script_location": "migrations", # Migration directory + "version_table": "alembic_version", # Version tracking table + "include_extensions": ["litestar"], # Simple string list only + } + ) + assert config.migration_config["script_location"] == "migrations" + assert "litestar" in config.migration_config["include_extensions"] + diff --git a/docs/examples/usage/test_configuration_22.txt b/docs/examples/usage/test_configuration_22.txt new file mode 100644 index 00000000..4d4eb539 --- /dev/null +++ b/docs/examples/usage/test_configuration_22.txt @@ -0,0 +1,9 @@ +# Create migration +sqlspec --config myapp.config create-migration -m "Add users table" + +# Apply migrations +sqlspec --config myapp.config upgrade + +# Rollback +sqlspec --config myapp.config downgrade -1 + diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py new file mode 100644 index 00000000..d2ba6e6f --- /dev/null +++ b/docs/examples/usage/test_configuration_23.py @@ -0,0 +1,42 @@ +"""Test configuration example: Environment-based configuration.""" + +import os +from unittest.mock import patch + +import pytest + + +@pytest.mark.skipif( + not os.getenv("TEST_ASYNCPG", "0") == "1", + reason="AsyncPG integration tests disabled", +) +def test_environment_based_configuration() -> None: + """Test environment-based configuration pattern.""" + from sqlspec.adapters.asyncpg import AsyncpgConfig + + # Mock environment variables + env_vars = { + "DB_HOST": "testhost", + "DB_PORT": "5433", + "DB_USER": "testuser", + "DB_PASSWORD": "testpass", + "DB_NAME": "testdb", + } + + with patch.dict(os.environ, env_vars, clear=False): + config = AsyncpgConfig( + pool_config={ + "host": os.getenv("DB_HOST", "localhost"), + "port": int(os.getenv("DB_PORT", "5432")), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME"), + } + ) + + assert config.pool_config["host"] == "testhost" + assert config.pool_config["port"] == 5433 + assert config.pool_config["user"] == "testuser" + assert config.pool_config["password"] == "testpass" + assert config.pool_config["database"] == "testdb" + diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/test_configuration_24.py new file mode 100644 index 00000000..a2aa9069 --- /dev/null +++ b/docs/examples/usage/test_configuration_24.py @@ -0,0 +1,25 @@ +"""Test configuration example: Best practice - Use connection pooling.""" + +import pytest + + +@pytest.mark.skipif( + not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), + reason="AsyncPG integration tests disabled", +) +def test_connection_pooling_best_practice() -> None: + """Test connection pooling best practice configuration.""" + from sqlspec.adapters.asyncpg import AsyncpgConfig + + config = AsyncpgConfig( + pool_config={ + "dsn": "postgresql://localhost/db", + "min_size": 10, + "max_size": 20, + } + ) + + assert config.pool_config["min_size"] == 10 + assert config.pool_config["max_size"] == 20 + assert config.supports_connection_pooling is True + diff --git a/docs/examples/usage/test_configuration_25.py b/docs/examples/usage/test_configuration_25.py new file mode 100644 index 00000000..75fbe5b7 --- /dev/null +++ b/docs/examples/usage/test_configuration_25.py @@ -0,0 +1,15 @@ +"""Test configuration example: Best practice - Enable caching.""" + + +def test_enable_caching_best_practice() -> None: + """Test caching best practice configuration.""" + from sqlspec.core.statement import StatementConfig + + statement_config = StatementConfig( + dialect="postgres", + enable_caching=True, + ) + + assert statement_config.enable_caching is True + assert statement_config.dialect == "postgres" + diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py new file mode 100644 index 00000000..1f0aef18 --- /dev/null +++ b/docs/examples/usage/test_configuration_26.py @@ -0,0 +1,16 @@ +"""Test configuration example: Best practice - Tune pool sizes.""" + + +def test_tune_pool_sizes_best_practice() -> None: + """Test pool sizing best practices for different workloads.""" + + # CPU-bound workload - smaller pool + cpu_bound_pool_config = {"min_size": 5, "max_size": 10} + assert cpu_bound_pool_config["min_size"] == 5 + assert cpu_bound_pool_config["max_size"] == 10 + + # I/O-bound workload - larger pool + io_bound_pool_config = {"min_size": 20, "max_size": 50} + assert io_bound_pool_config["min_size"] == 20 + assert io_bound_pool_config["max_size"] == 50 + diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py new file mode 100644 index 00000000..a4169c70 --- /dev/null +++ b/docs/examples/usage/test_configuration_27.py @@ -0,0 +1,29 @@ +"""Test configuration example: Best practice - Clean up resources.""" + +import tempfile + +import pytest + + +@pytest.mark.asyncio +async def test_cleanup_resources_best_practice() -> None: + """Test resource cleanup best practice.""" + from sqlspec import SQLSpec + from sqlspec.adapters.aiosqlite import AiosqliteConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + spec = SQLSpec() + db = spec.add_config( + AiosqliteConfig(pool_config={"database": tmp.name}) + ) + + # Use the connection + async with spec.provide_session(db) as session: + await session.execute("CREATE TABLE test (id INTEGER)") + + # Clean up resources - important for async adapters + await spec.close_all_pools() + + # Verify pools are closed + assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") + diff --git a/docs/examples/usage/test_configuration_3.py b/docs/examples/usage/test_configuration_3.py new file mode 100644 index 00000000..5fc92252 --- /dev/null +++ b/docs/examples/usage/test_configuration_3.py @@ -0,0 +1,16 @@ +from sqlspec.adapters.sqlite import SqliteConfig + +def test_memory_databases(): + # In-memory database (isolated per connection) + config = SqliteConfig(pool_config={"database": ":memory:"}) + assert config.pool_config["database"] == ":memory:" + + # Shared memory database + shared_config = SqliteConfig( + pool_config={ + "database": "file:memdb1?mode=memory&cache=shared", + "uri": True + } + ) + assert shared_config.pool_config["database"] == "file:memdb1?mode=memory&cache=shared" + diff --git a/docs/examples/usage/test_configuration_4.py b/docs/examples/usage/test_configuration_4.py new file mode 100644 index 00000000..22172073 --- /dev/null +++ b/docs/examples/usage/test_configuration_4.py @@ -0,0 +1,18 @@ +from sqlspec.adapters.asyncpg import AsyncpgConfig + +def test_asyncpg_config_setup(): + config = AsyncpgConfig( + pool_config={ + "dsn": "postgresql://user:pass@localhost:5432/dbname", + # Other parameters + "host": "localhost", + "port": 5432, + "user": "myuser", + "password": "mypassword", + "database": "mydb", + "min_size": 10, + "max_size": 20, + } + ) + assert config.pool_config["host"] == "localhost" + diff --git a/docs/examples/usage/test_configuration_5.py b/docs/examples/usage/test_configuration_5.py new file mode 100644 index 00000000..72b45243 --- /dev/null +++ b/docs/examples/usage/test_configuration_5.py @@ -0,0 +1,6 @@ +import pytest + +@pytest.mark.skip(reason="PsycopgConfig does not exist in the codebase.") +def test_psycopg_config_setup(): + pass + diff --git a/docs/examples/usage/test_configuration_6.py b/docs/examples/usage/test_configuration_6.py new file mode 100644 index 00000000..c25597a8 --- /dev/null +++ b/docs/examples/usage/test_configuration_6.py @@ -0,0 +1,18 @@ +from sqlspec.adapters.asyncmy import AsyncmyConfig + +def test_asyncmy_config_setup(): + config = AsyncmyConfig( + pool_config={ + "host": "localhost", + "port": 3306, + "user": "myuser", + "password": "mypassword", + "database": "mydb", + "charset": "utf8mb4", + "minsize": 1, + "maxsize": 10, + "pool_recycle": 3600, + } + ) + assert config.pool_config["port"] == 3306 + diff --git a/docs/examples/usage/test_configuration_7.py b/docs/examples/usage/test_configuration_7.py new file mode 100644 index 00000000..f17f7197 --- /dev/null +++ b/docs/examples/usage/test_configuration_7.py @@ -0,0 +1,14 @@ +from sqlspec.adapters.duckdb import DuckDBConfig + +def test_duckdb_config_setup(): + in_memory_config = DuckDBConfig() + assert in_memory_config.pool_config == {} + + persistent_config = DuckDBConfig( + pool_config={ + "database": "analytics.duckdb", + "read_only": False, + } + ) + assert persistent_config.pool_config["read_only"] is False + diff --git a/docs/examples/usage/test_configuration_8.py b/docs/examples/usage/test_configuration_8.py new file mode 100644 index 00000000..9a963f97 --- /dev/null +++ b/docs/examples/usage/test_configuration_8.py @@ -0,0 +1,14 @@ +from sqlspec.adapters.asyncpg import AsyncpgConfig + +def test_asyncpg_pool_setup(): + config = AsyncpgConfig( + pool_config={ + "dsn": "postgresql://localhost/db", + "min_size": 10, + "max_size": 20, + "max_queries": 50000, + "max_inactive_connection_lifetime": 300.0, + } + ) + assert config.pool_config["min_size"] == 10 + diff --git a/docs/examples/usage/test_configuration_9.py b/docs/examples/usage/test_configuration_9.py new file mode 100644 index 00000000..92ecc11d --- /dev/null +++ b/docs/examples/usage/test_configuration_9.py @@ -0,0 +1,8 @@ +from sqlspec import SQLSpec +from sqlspec.adapters.asyncpg import AsyncpgConfig + +def test_pool_lifecycle(): + spec = SQLSpec() + db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) + assert db.pool_config["dsn"] == "postgresql://localhost/db" + diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index d06f8d39..80f0c738 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -18,20 +18,11 @@ Basic Configuration The simplest way to use SQLSpec is with default configuration: -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig - - # Create SQLSpec instance - spec = SQLSpec() - - # Add database configuration - db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) - - # Use the database - with spec.provide_session(db) as session: - result = session.execute("SELECT 1") +.. literalinclude:: /examples/usage/test_configuration_1.py + :language: python + :caption: `basic configuration` + :lines: 2-12 + :dedent: 2 Database Configurations ----------------------- @@ -41,122 +32,47 @@ Each database adapter has its own configuration class with adapter-specific sett SQLite Configuration ^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.sqlite import SqliteConfig - - config = SqliteConfig( - pool_config={ - "database": "myapp.db", # Database file path - "timeout": 5.0, # Lock timeout in seconds - "check_same_thread": False, # Allow multi-thread access - "cached_statements": 100, # Statement cache size - "uri": False, # Enable URI mode - } - ) +.. literalinclude:: /examples/usage/test_configuration_2.py + :language: python + :caption: `sqlite configuration` **Memory Databases** -.. code-block:: python - - # In-memory database (isolated per connection) - config = SqliteConfig(pool_config={"database": ":memory:"}) - - # Shared memory database - config = SqliteConfig( - pool_config={ - "database": "file:memdb1?mode=memory&cache=shared", - "uri": True - } - ) +.. literalinclude:: /examples/usage/test_configuration_3.py + :language: python + :caption: `memory sqlite configuration` PostgreSQL Configuration (asyncpg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={ - "dsn": "postgresql://user:pass@localhost:5432/dbname", - # Or individual parameters: - "host": "localhost", - "port": 5432, - "user": "myuser", - "password": "mypassword", - "database": "mydb", - # Pool settings - "min_size": 10, - "max_size": 20, - "max_queries": 50000, - "max_inactive_connection_lifetime": 300.0, - } - ) +.. literalinclude:: /examples/usage/test_configuration_4.py + :language: python + :caption: `postgres asyncpg configuration` PostgreSQL Configuration (psycopg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python +.. note:: - from sqlspec.adapters.psycopg import PsycopgConfig + The `PsycopgConfig` class referenced here is for documentation purposes only and may not be present in the codebase. Future releases may include this feature if demand warrants. - # Async version - config = PsycopgConfig( - pool_config={ - "conninfo": "postgresql://user:pass@localhost/db", - # Or keyword arguments: - "host": "localhost", - "port": 5432, - "dbname": "mydb", - "user": "myuser", - "password": "mypassword", - # Pool settings - "min_size": 5, - "max_size": 10, - "timeout": 30.0, - } - ) +.. literalinclude:: /examples/usage/test_configuration_5.py + :language: python + :caption: `postgres psycopg configuration` MySQL Configuration (asyncmy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.asyncmy import AsyncmyConfig - - config = AsyncmyConfig( - pool_config={ - "host": "localhost", - "port": 3306, - "user": "myuser", - "password": "mypassword", - "database": "mydb", - "charset": "utf8mb4", - # Pool settings - "minsize": 1, - "maxsize": 10, - "pool_recycle": 3600, - } - ) +.. literalinclude:: /examples/usage/test_configuration_6.py + :language: python + :caption: `mysql asyncmy configuration` DuckDB Configuration ^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.duckdb import DuckDBConfig - - # In-memory database - config = DuckDBConfig() - - # Persistent database - config = DuckDBConfig( - pool_config={ - "database": "analytics.duckdb", - "read_only": False, - } - ) +.. literalinclude:: /examples/usage/test_configuration_7.py + :language: python + :caption: `duckdb configuration` Connection Pooling ------------------ @@ -166,65 +82,29 @@ Connection pooling improves performance by reusing database connections. SQLSpec Pool Configuration ^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/db", - "min_size": 10, # Minimum connections to maintain - "max_size": 20, # Maximum connections allowed - "max_queries": 50000, # Max queries per connection before recycling - "max_inactive_connection_lifetime": 300.0, # Idle timeout - } - ) +.. literalinclude:: /examples/usage/test_configuration_8.py + :language: python + :caption: `pool configuration` **Pool Lifecycle Management** -.. code-block:: python - - # SQLSpec manages pool lifecycle automatically - spec = SQLSpec() - db = spec.add_config(AsyncpgConfig(pool_config={...})) - - # Pool is created on first use - async with spec.provide_session(db) as session: - await session.execute("SELECT 1") - - # Clean up all pools on shutdown - await spec.close_all_pools() +.. literalinclude:: /examples/usage/test_configuration_9.py + :language: python + :caption: `pool lifecycle management` Using Pre-Created Pools ^^^^^^^^^^^^^^^^^^^^^^^^ -You can create and manage pools manually: - -.. code-block:: python - - import asyncpg - - # Create pool manually - pool = await asyncpg.create_pool( - dsn="postgresql://localhost/db", - min_size=10, - max_size=20 - ) - - # Pass to config and add to SQLSpec - db = spec.add_config(AsyncpgConfig(pool_instance=pool)) +.. literalinclude:: /examples/usage/test_configuration_10.py + :language: python + :caption: `using pre-created pools` No-Pooling Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^ -For simple use cases or testing, disable pooling: - -.. code-block:: python - - from sqlspec.adapters.sqlite import SqliteConfig - - # SQLite uses thread-local connections (no traditional pooling) - config = SqliteConfig(pool_config={"database": "test.db"}) +.. literalinclude:: /examples/usage/test_configuration_11.py + :language: python + :caption: `no-pooling configuration` Statement Configuration ----------------------- @@ -234,72 +114,24 @@ Statement configuration controls SQL processing pipeline behavior. Basic Statement Config ^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.core.statement import StatementConfig - from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig - - statement_config = StatementConfig( - dialect="postgres", # SQLGlot dialect - enable_parsing=True, # Parse SQL into AST - enable_validation=True, # Run security/performance validators - enable_transformations=True, # Apply AST transformations - enable_caching=True, # Enable multi-tier caching - ) - - # Apply to adapter - config = AsyncpgConfig( - pool_config={...}, - statement_config=statement_config - ) +.. literalinclude:: /examples/usage/test_configuration_12.py + :language: python + :caption: `basic statement config` Parameter Style Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Control how parameters are handled: - -.. code-block:: python - - from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig - - param_config = ParameterStyleConfig( - default_parameter_style=ParameterStyle.NUMERIC, # $1, $2, ... - supported_parameter_styles={ - ParameterStyle.NUMERIC, - ParameterStyle.NAMED_COLON, # :name - }, - has_native_list_expansion=False, - needs_static_script_compilation=False, - ) - - statement_config = StatementConfig( - dialect="postgres", - parameter_config=param_config - ) +.. literalinclude:: /examples/usage/test_configuration_13.py + :language: python + :caption: `parameter style configuration` **Parameter Styles** SQLSpec supports multiple parameter placeholder styles: -.. code-block:: python - - from sqlspec.core.parameters import ParameterStyle - - # Question mark (SQLite, DuckDB) - ParameterStyle.QMARK # WHERE id = ? - - # Numeric (PostgreSQL, asyncpg) - ParameterStyle.NUMERIC # WHERE id = $1 - - # Named colon (Oracle, SQLite) - ParameterStyle.NAMED_COLON # WHERE id = :id - - # Named at (BigQuery) - ParameterStyle.NAMED_AT # WHERE id = @id - - # Format/pyformat (psycopg, MySQL) - ParameterStyle.POSITIONAL_PYFORMAT # WHERE id = %s - ParameterStyle.NAMED_PYFORMAT # WHERE id = %(id)s +.. literalinclude:: /examples/usage/test_configuration_14.py + :language: python + :caption: `parameter styles` Validation Configuration ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -308,13 +140,9 @@ Configure security and performance validation. Disable validation for performance-critical paths where input is trusted: -.. code-block:: python - - statement_config = StatementConfig( - dialect="postgres", - enable_validation=False, # Skip validation - enable_transformations=False, # Skip transformations - ) +.. literalinclude:: /examples/usage/test_configuration_15.py + :language: python + :caption: `validation configuration` Cache Configuration ------------------- @@ -324,61 +152,32 @@ SQLSpec uses multi-tier caching to avoid recompiling SQL statements. Global Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.core.cache import CacheConfig, update_cache_config - - cache_config = CacheConfig( - enable_sql_cache=True, # Cache compiled SQL strings - enable_optimized_cache=True, # Cache optimized AST - enable_builder_cache=True, # Cache QueryBuilder state - enable_file_cache=True, # Cache loaded SQL files - max_cache_size=1000, # Maximum cached items - ) - - # Update global cache configuration - update_cache_config(cache_config) +.. literalinclude:: /examples/usage/test_configuration_16.py + :language: python + :caption: `global cache configuration` Per-Instance Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - # Configure cache for specific SQLSpec instance - spec = SQLSpec() - spec.update_cache_config( - CacheConfig( - enable_sql_cache=True, - max_cache_size=500 - ) - ) +.. literalinclude:: /examples/usage/test_configuration_17.py + :language: python + :caption: `per-instance cache configuration` Cache Statistics ^^^^^^^^^^^^^^^^ Monitor cache statistics: -.. code-block:: python - - from sqlspec.core.cache import get_cache_statistics, log_cache_stats - - # Get statistics - stats = get_cache_statistics() - print(f"SQL Cache hits: {stats['sql_cache_hits']}") - print(f"File Cache hits: {stats['file_cache_hits']}") - - # Log statistics - log_cache_stats() # Logs to configured logger +.. literalinclude:: /examples/usage/test_configuration_18.py + :language: python + :caption: `cache statistics` Clear Cache ^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.core.cache import reset_cache_stats - - # Clear all caches and reset statistics - reset_cache_stats() +.. literalinclude:: /examples/usage/test_configuration_19.py + :language: python + :caption: `clear cache` Multiple Database Configurations --------------------------------- @@ -388,39 +187,18 @@ SQLSpec supports multiple database configurations in a single application. Binding Multiple Configs ^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig - from sqlspec.adapters.asyncpg import AsyncpgConfig - - spec = SQLSpec() - - # Add multiple configurations - sqlite_db = spec.add_config(SqliteConfig(pool_config={"database": "cache.db"})) - postgres_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) - - # Use specific configuration - with spec.provide_session(sqlite_db) as session: - session.execute("SELECT * FROM cache") - - async with spec.provide_session(postgres_db) as session: - await session.execute("SELECT * FROM users") +.. literalinclude:: /examples/usage/test_configuration_20.py + :language: python + :caption: `binding multiple configurations` Named Bindings ^^^^^^^^^^^^^^ Use bind keys for clearer configuration management: -.. code-block:: python - - # Add with bind keys - cache_db = spec.add_config(SqliteConfig(pool_config={"database": "cache.db"}), bind_key="cache_db") - main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."}), bind_key="main_db") - - # Access by bind key - with spec.provide_session("cache_db") as session: - session.execute("SELECT 1") +.. literalinclude:: /examples/usage/test_configuration_21.py + :language: python + :caption: `named bindings` Migration Configuration ----------------------- @@ -430,34 +208,18 @@ SQLSpec includes a migration system for schema management. Basic Migration Config ^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": {"session_table": "custom_sessions"} # Extension settings - }, - migration_config={ - "script_location": "migrations", # Migration directory - "version_table": "alembic_version", # Version tracking table - "include_extensions": ["litestar"], # Simple string list only - } - ) +.. literalinclude:: /examples/usage/test_configuration_22.py + :language: python + :caption: `basic migration config` + :lines: 12-24 + :dedent: 4 **Migration CLI** -.. code-block:: bash - - # Create migration - sqlspec --config myapp.config create-migration -m "Add users table" - - # Apply migrations - sqlspec --config myapp.config upgrade +.. literalinclude:: /examples/usage/test_configuration_22.txt + :language: text + :caption: `migration CLI` - # Rollback - sqlspec --config myapp.config downgrade -1 Extension Migration Versioning ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pyproject.toml b/pyproject.toml index 90fead10..31c8d4d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -319,7 +319,7 @@ markers = [ "pymysql: marks tests using pymysql", "psqlpy: marks tests using psqlpy", ] -testpaths = ["tests"] +testpaths = ["tests", "docs/examples/usage"] [tool.mypy] exclude = ["tmp/", ".tmp/", ".bugs/"] From 31cbf3130c026823fa77019553ff614e2d774b81 Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 13:46:06 +0100 Subject: [PATCH 02/18] put import in tests and correct the lines accordingly --- docs/examples/usage/__init__.py | 0 docs/examples/usage/test_configuration_2.py | 5 ++--- docs/examples/usage/test_configuration_3.py | 5 ++--- docs/examples/usage/test_configuration_4.py | 3 +-- docs/usage/configuration.rst | 22 +++++++++++++++++++++ 5 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 docs/examples/usage/__init__.py 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/test_configuration_2.py b/docs/examples/usage/test_configuration_2.py index c4be3d42..8794ccc9 100644 --- a/docs/examples/usage/test_configuration_2.py +++ b/docs/examples/usage/test_configuration_2.py @@ -1,6 +1,5 @@ -from sqlspec.adapters.sqlite import SqliteConfig - -def test_sqlite_config_setup(): +def test_sqlite_config_setup()-> None: + from sqlspec.adapters.sqlite import SqliteConfig config = SqliteConfig( pool_config={ "database": "myapp.db", # Database file path diff --git a/docs/examples/usage/test_configuration_3.py b/docs/examples/usage/test_configuration_3.py index 5fc92252..7592f960 100644 --- a/docs/examples/usage/test_configuration_3.py +++ b/docs/examples/usage/test_configuration_3.py @@ -1,6 +1,5 @@ -from sqlspec.adapters.sqlite import SqliteConfig - -def test_memory_databases(): +def test_memory_databases() -> None: + from sqlspec.adapters.sqlite import SqliteConfig # In-memory database (isolated per connection) config = SqliteConfig(pool_config={"database": ":memory:"}) assert config.pool_config["database"] == ":memory:" diff --git a/docs/examples/usage/test_configuration_4.py b/docs/examples/usage/test_configuration_4.py index 22172073..0983c7f5 100644 --- a/docs/examples/usage/test_configuration_4.py +++ b/docs/examples/usage/test_configuration_4.py @@ -1,6 +1,5 @@ -from sqlspec.adapters.asyncpg import AsyncpgConfig - def test_asyncpg_config_setup(): + from sqlspec.adapters.asyncpg import AsyncpgConfig config = AsyncpgConfig( pool_config={ "dsn": "postgresql://user:pass@localhost:5432/dbname", diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index 80f0c738..89c1db49 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -35,12 +35,16 @@ SQLite Configuration .. literalinclude:: /examples/usage/test_configuration_2.py :language: python :caption: `sqlite configuration` + :lines: 2-11 + :dedent: 2 **Memory Databases** .. literalinclude:: /examples/usage/test_configuration_3.py :language: python :caption: `memory sqlite configuration` + :lines: 2-13 + :dedent: 2 PostgreSQL Configuration (asyncpg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,6 +52,7 @@ PostgreSQL Configuration (asyncpg) .. literalinclude:: /examples/usage/test_configuration_4.py :language: python :caption: `postgres asyncpg configuration` + :lines: 2-15 PostgreSQL Configuration (psycopg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,6 +64,7 @@ PostgreSQL Configuration (psycopg) .. literalinclude:: /examples/usage/test_configuration_5.py :language: python :caption: `postgres psycopg configuration` + :lines: 1-6 MySQL Configuration (asyncmy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,6 +72,7 @@ MySQL Configuration (asyncmy) .. literalinclude:: /examples/usage/test_configuration_6.py :language: python :caption: `mysql asyncmy configuration` + :lines: 1-19 DuckDB Configuration ^^^^^^^^^^^^^^^^^^^^ @@ -73,6 +80,7 @@ DuckDB Configuration .. literalinclude:: /examples/usage/test_configuration_7.py :language: python :caption: `duckdb configuration` + :lines: 1-15 Connection Pooling ------------------ @@ -85,12 +93,14 @@ Pool Configuration .. literalinclude:: /examples/usage/test_configuration_8.py :language: python :caption: `pool configuration` + :lines: 1-15 **Pool Lifecycle Management** .. literalinclude:: /examples/usage/test_configuration_9.py :language: python :caption: `pool lifecycle management` + :lines: 1-8 Using Pre-Created Pools ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -98,6 +108,7 @@ Using Pre-Created Pools .. literalinclude:: /examples/usage/test_configuration_10.py :language: python :caption: `using pre-created pools` + :lines: 1-12 No-Pooling Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,6 +116,7 @@ No-Pooling Configuration .. literalinclude:: /examples/usage/test_configuration_11.py :language: python :caption: `no-pooling configuration` + :lines: 1-6 Statement Configuration ----------------------- @@ -117,6 +129,7 @@ Basic Statement Config .. literalinclude:: /examples/usage/test_configuration_12.py :language: python :caption: `basic statement config` + :lines: 1-21 Parameter Style Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,6 +137,7 @@ Parameter Style Configuration .. literalinclude:: /examples/usage/test_configuration_13.py :language: python :caption: `parameter style configuration` + :lines: 1-21 **Parameter Styles** @@ -132,6 +146,7 @@ SQLSpec supports multiple parameter placeholder styles: .. literalinclude:: /examples/usage/test_configuration_14.py :language: python :caption: `parameter styles` + :lines: 1-24 Validation Configuration ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,6 +158,7 @@ Disable validation for performance-critical paths where input is trusted: .. literalinclude:: /examples/usage/test_configuration_15.py :language: python :caption: `validation configuration` + :lines: 1-22 Cache Configuration ------------------- @@ -155,6 +171,7 @@ Global Cache Configuration .. literalinclude:: /examples/usage/test_configuration_16.py :language: python :caption: `global cache configuration` + :lines: 1-29 Per-Instance Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -162,6 +179,7 @@ Per-Instance Cache Configuration .. literalinclude:: /examples/usage/test_configuration_17.py :language: python :caption: `per-instance cache configuration` + :lines: 1-28 Cache Statistics ^^^^^^^^^^^^^^^^ @@ -171,6 +189,7 @@ Monitor cache statistics: .. literalinclude:: /examples/usage/test_configuration_18.py :language: python :caption: `cache statistics` + :lines: 1-19 Clear Cache ^^^^^^^^^^^ @@ -178,6 +197,7 @@ Clear Cache .. literalinclude:: /examples/usage/test_configuration_19.py :language: python :caption: `clear cache` + :lines: 1-27 Multiple Database Configurations --------------------------------- @@ -190,6 +210,7 @@ Binding Multiple Configs .. literalinclude:: /examples/usage/test_configuration_20.py :language: python :caption: `binding multiple configurations` + :lines: 1-30 Named Bindings ^^^^^^^^^^^^^^ @@ -199,6 +220,7 @@ Use bind keys for clearer configuration management: .. literalinclude:: /examples/usage/test_configuration_21.py :language: python :caption: `named bindings` + :lines: 1-29 Migration Configuration ----------------------- From 5dcc42634a4e068d885f13a3eb699948aeada5af Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 14:10:45 +0100 Subject: [PATCH 03/18] lint --- docs/examples/usage/test_configuration_1.py | 4 +- docs/examples/usage/test_configuration_10.py | 12 ++---- docs/examples/usage/test_configuration_11.py | 5 +-- docs/examples/usage/test_configuration_12.py | 23 ++++------ docs/examples/usage/test_configuration_13.py | 13 ++---- docs/examples/usage/test_configuration_14.py | 15 +++---- docs/examples/usage/test_configuration_15.py | 11 +++-- docs/examples/usage/test_configuration_16.py | 12 ++---- docs/examples/usage/test_configuration_17.py | 1 - docs/examples/usage/test_configuration_18.py | 1 - docs/examples/usage/test_configuration_19.py | 7 +-- docs/examples/usage/test_configuration_2.py | 14 +++--- docs/examples/usage/test_configuration_20.py | 11 ++--- docs/examples/usage/test_configuration_21.py | 6 +-- docs/examples/usage/test_configuration_22.py | 7 ++- docs/examples/usage/test_configuration_22.txt | 1 - docs/examples/usage/test_configuration_23.py | 6 +-- docs/examples/usage/test_configuration_24.py | 12 +----- docs/examples/usage/test_configuration_25.py | 6 +-- docs/examples/usage/test_configuration_26.py | 1 - docs/examples/usage/test_configuration_27.py | 5 +-- docs/examples/usage/test_configuration_3.py | 9 +--- docs/examples/usage/test_configuration_4.py | 4 +- docs/examples/usage/test_configuration_5.py | 4 +- docs/examples/usage/test_configuration_6.py | 5 +-- docs/examples/usage/test_configuration_7.py | 12 ++---- docs/examples/usage/test_configuration_8.py | 5 +-- docs/examples/usage/test_configuration_9.py | 7 ++- docs/usage/configuration.rst | 43 +++++++++++++------ 29 files changed, 102 insertions(+), 160 deletions(-) diff --git a/docs/examples/usage/test_configuration_1.py b/docs/examples/usage/test_configuration_1.py index 5da42ad6..df9f5428 100644 --- a/docs/examples/usage/test_configuration_1.py +++ b/docs/examples/usage/test_configuration_1.py @@ -1,6 +1,7 @@ -def test_sqlite_memory_db(): +def test_sqlite_memory_db() -> None: from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig + # Create SQLSpec instance spec = SQLSpec() @@ -11,4 +12,3 @@ def test_sqlite_memory_db(): with spec.provide_session(db) as session: result = session.execute("SELECT 1") assert result.fetchone()[0] == 1 - diff --git a/docs/examples/usage/test_configuration_10.py b/docs/examples/usage/test_configuration_10.py index 77704ebb..0f7385db 100644 --- a/docs/examples/usage/test_configuration_10.py +++ b/docs/examples/usage/test_configuration_10.py @@ -1,12 +1,6 @@ -import asyncpg -from sqlspec.adapters.asyncpg import AsyncpgConfig +def test_manual_pool() -> None: + from sqlspec.adapters.asyncpg import AsyncpgConfig -def test_manual_pool(): - pool = { - "dsn": "postgresql://localhost/db", - "min_size": 10, - "max_size": 20 - } + pool = {"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": 20} db = AsyncpgConfig(pool_instance=pool) assert db.pool_instance["max_size"] == 20 - diff --git a/docs/examples/usage/test_configuration_11.py b/docs/examples/usage/test_configuration_11.py index b4cd1b26..e1545362 100644 --- a/docs/examples/usage/test_configuration_11.py +++ b/docs/examples/usage/test_configuration_11.py @@ -1,6 +1,5 @@ -from sqlspec.adapters.sqlite import SqliteConfig +def test_thread_local_connections() -> None: + from sqlspec.adapters.sqlite import SqliteConfig -def test_thread_local_connections(): config = SqliteConfig(pool_config={"database": "test.db"}) assert config.pool_config["database"] == "test.db" - diff --git a/docs/examples/usage/test_configuration_12.py b/docs/examples/usage/test_configuration_12.py index cd20ecd2..a85a9c4b 100644 --- a/docs/examples/usage/test_configuration_12.py +++ b/docs/examples/usage/test_configuration_12.py @@ -1,21 +1,16 @@ -from sqlspec.core.statement import StatementConfig -from sqlspec.adapters.asyncpg import AsyncpgConfig +def test_basic_statement_config() -> None: + from sqlspec.adapters.asyncpg import AsyncpgConfig + from sqlspec.core.statement import StatementConfig - -def test_basic_statement_config(): statement_config = StatementConfig( - dialect="postgres", # SQLGlot dialect - enable_parsing=True, # Parse SQL into AST - enable_validation=True, # Run security/performance validators - enable_transformations=True, # Apply AST transformations - enable_caching=True, # Enable multi-tier caching + dialect="postgres", # SQLGlot dialect + enable_parsing=True, # Parse SQL into AST + enable_validation=True, # Run security/performance validators + enable_transformations=True, # Apply AST transformations + enable_caching=True, # Enable multi-tier caching ) # Apply to adapter - config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - statement_config=statement_config - ) + config = AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}, statement_config=statement_config) assert config.statement_config.dialect == "postgres" assert config.statement_config.enable_parsing is True - diff --git a/docs/examples/usage/test_configuration_13.py b/docs/examples/usage/test_configuration_13.py index 0eb2aa33..f95775d1 100644 --- a/docs/examples/usage/test_configuration_13.py +++ b/docs/examples/usage/test_configuration_13.py @@ -1,8 +1,7 @@ -from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig -from sqlspec.core.statement import StatementConfig +def test_parameter_style_config() -> None: + from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig + from sqlspec.core.statement import StatementConfig - -def test_parameter_style_config(): param_config = ParameterStyleConfig( default_parameter_style=ParameterStyle.NUMERIC, # $1, $2, ... supported_parameter_styles={ @@ -13,9 +12,5 @@ def test_parameter_style_config(): needs_static_script_compilation=False, ) - statement_config = StatementConfig( - dialect="postgres", - parameter_config=param_config - ) + statement_config = StatementConfig(dialect="postgres", parameter_config=param_config) assert statement_config.parameter_config.default_parameter_style == ParameterStyle.NUMERIC - diff --git a/docs/examples/usage/test_configuration_14.py b/docs/examples/usage/test_configuration_14.py index 32573c1d..8b30ec37 100644 --- a/docs/examples/usage/test_configuration_14.py +++ b/docs/examples/usage/test_configuration_14.py @@ -1,24 +1,19 @@ -from sqlspec.core.parameters import ParameterStyle +def test_parameter_styles() -> None: + from sqlspec.core.parameters import ParameterStyle - -def test_parameter_styles(): # Question mark (SQLite, DuckDB) - qmark = ParameterStyle.QMARK # WHERE id = ? + qmark = ParameterStyle.QMARK # WHERE id = ? # Numeric (PostgreSQL, asyncpg) - numeric = ParameterStyle.NUMERIC # WHERE id = $1 + numeric = ParameterStyle.NUMERIC # WHERE id = $1 # Named colon (Oracle, SQLite) - named_colon = ParameterStyle.NAMED_COLON # WHERE id = :id + named_colon = ParameterStyle.NAMED_COLON # WHERE id = :id # Named at (BigQuery) - named_at = ParameterStyle.NAMED_AT # WHERE id = @id # Format/pyformat (psycopg, MySQL) - positional_pyformat = ParameterStyle.POSITIONAL_PYFORMAT # WHERE id = %s - named_pyformat = ParameterStyle.NAMED_PYFORMAT # WHERE id = %(id)s assert qmark == ParameterStyle.QMARK assert numeric == ParameterStyle.NUMERIC assert named_colon == ParameterStyle.NAMED_COLON - diff --git a/docs/examples/usage/test_configuration_15.py b/docs/examples/usage/test_configuration_15.py index 2ede1e37..b61246e8 100644 --- a/docs/examples/usage/test_configuration_15.py +++ b/docs/examples/usage/test_configuration_15.py @@ -6,11 +6,11 @@ def test_global_cache_config() -> None: from sqlspec.core.cache import CacheConfig, update_cache_config cache_config = CacheConfig( - compiled_cache_enabled=True, # Cache compiled SQL - sql_cache_enabled=True, # Cache SQL strings - fragment_cache_enabled=True, # Cache SQL fragments - optimized_cache_enabled=True, # Cache optimized AST - sql_cache_size=1000, # Maximum cached SQL items + compiled_cache_enabled=True, # Cache compiled SQL + sql_cache_enabled=True, # Cache SQL strings + fragment_cache_enabled=True, # Cache SQL fragments + optimized_cache_enabled=True, # Cache optimized AST + sql_cache_size=1000, # Maximum cached SQL items ) # Update global cache configuration @@ -19,4 +19,3 @@ def test_global_cache_config() -> None: # Verify config applied assert cache_config.sql_cache_enabled is True assert cache_config.sql_cache_size == 1000 - diff --git a/docs/examples/usage/test_configuration_16.py b/docs/examples/usage/test_configuration_16.py index 3c5f7fd2..4245e8e9 100644 --- a/docs/examples/usage/test_configuration_16.py +++ b/docs/examples/usage/test_configuration_16.py @@ -1,10 +1,10 @@ """Test configuration example: Per-instance cache configuration.""" -import tempfile - def test_per_instance_cache_config() -> None: """Test per-instance cache configuration.""" + import tempfile + from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig from sqlspec.core.cache import CacheConfig @@ -12,12 +12,7 @@ def test_per_instance_cache_config() -> None: with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: # Configure cache for specific SQLSpec instance spec = SQLSpec() - spec.update_cache_config( - CacheConfig( - sql_cache_enabled=True, - sql_cache_size=500, - ) - ) + spec.update_cache_config(CacheConfig(sql_cache_enabled=True, sql_cache_size=500)) # Add database config db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) @@ -26,4 +21,3 @@ def test_per_instance_cache_config() -> None: with spec.provide_session(db) as session: result = session.execute("SELECT 1") assert result is not None - diff --git a/docs/examples/usage/test_configuration_17.py b/docs/examples/usage/test_configuration_17.py index fd416dda..f122e44a 100644 --- a/docs/examples/usage/test_configuration_17.py +++ b/docs/examples/usage/test_configuration_17.py @@ -25,4 +25,3 @@ def test_cache_statistics() -> None: # Log statistics (logs to configured logger) log_cache_stats() - diff --git a/docs/examples/usage/test_configuration_18.py b/docs/examples/usage/test_configuration_18.py index ba673880..d17b693d 100644 --- a/docs/examples/usage/test_configuration_18.py +++ b/docs/examples/usage/test_configuration_18.py @@ -16,4 +16,3 @@ def test_clear_cache() -> None: stats_after = get_cache_statistics() assert isinstance(stats_after, dict) assert "multi_level" in stats_after - diff --git a/docs/examples/usage/test_configuration_19.py b/docs/examples/usage/test_configuration_19.py index 420b184d..088d81e2 100644 --- a/docs/examples/usage/test_configuration_19.py +++ b/docs/examples/usage/test_configuration_19.py @@ -6,17 +6,15 @@ def test_binding_multiple_configs() -> None: """Test binding multiple database configurations.""" from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig from sqlspec.adapters.asyncpg import AsyncpgConfig + from sqlspec.adapters.sqlite import SqliteConfig with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: spec = SQLSpec() # Add multiple configurations sqlite_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) - postgres_db = spec.add_config( - AsyncpgConfig(pool_config={"dsn": "postgresql://..."}) - ) + postgres_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) # Use specific configuration with spec.provide_session(sqlite_db) as session: @@ -24,4 +22,3 @@ def test_binding_multiple_configs() -> None: assert sqlite_db.pool_config["database"] == tmp.name assert postgres_db.pool_config["dsn"] == "postgresql://..." - diff --git a/docs/examples/usage/test_configuration_2.py b/docs/examples/usage/test_configuration_2.py index 8794ccc9..2cc4e66e 100644 --- a/docs/examples/usage/test_configuration_2.py +++ b/docs/examples/usage/test_configuration_2.py @@ -1,13 +1,13 @@ -def test_sqlite_config_setup()-> None: +def test_sqlite_config_setup() -> None: from sqlspec.adapters.sqlite import SqliteConfig + config = SqliteConfig( pool_config={ - "database": "myapp.db", # Database file path - "timeout": 5.0, # Lock timeout in seconds - "check_same_thread": False, # Allow multi-thread access - "cached_statements": 100, # Statement cache size - "uri": False, # Enable URI mode + "database": "myapp.db", # Database file path + "timeout": 5.0, # Lock timeout in seconds + "check_same_thread": False, # Allow multi-thread access + "cached_statements": 100, # Statement cache size + "uri": False, # Enable URI mode } ) assert config.pool_config["database"] == "myapp.db" - diff --git a/docs/examples/usage/test_configuration_20.py b/docs/examples/usage/test_configuration_20.py index c5ac2bd0..cb14d803 100644 --- a/docs/examples/usage/test_configuration_20.py +++ b/docs/examples/usage/test_configuration_20.py @@ -6,19 +6,15 @@ def test_named_bindings() -> None: """Test named database bindings.""" from sqlspec import SQLSpec - from sqlspec.adapters.sqlite import SqliteConfig from sqlspec.adapters.asyncpg import AsyncpgConfig + from sqlspec.adapters.sqlite import SqliteConfig with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: spec = SQLSpec() # Add with bind keys - cache_db = spec.add_config( - SqliteConfig(pool_config={"database": tmp.name}), bind_key="cache_db" - ) - main_db = spec.add_config( - AsyncpgConfig(pool_config={"dsn": "postgresql://..."}), bind_key="main_db" - ) + cache_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name}), bind_key="cache_db") + main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."}), bind_key="main_db") # Access by bind key with spec.provide_session("cache_db") as session: @@ -26,4 +22,3 @@ def test_named_bindings() -> None: assert cache_db.pool_config["database"] == tmp.name assert main_db.pool_config["dsn"] == "postgresql://..." - diff --git a/docs/examples/usage/test_configuration_21.py b/docs/examples/usage/test_configuration_21.py index 04aed97a..7c53e1a8 100644 --- a/docs/examples/usage/test_configuration_21.py +++ b/docs/examples/usage/test_configuration_21.py @@ -4,8 +4,7 @@ @pytest.mark.skipif( - not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), - reason="AsyncPG integration tests disabled", + not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" ) def test_basic_migration_config() -> None: """Test basic migration configuration.""" @@ -17,7 +16,7 @@ def test_basic_migration_config() -> None: "litestar": {"session_table": "custom_sessions"} # Extension settings }, migration_config={ - "script_location": "migrations", # Migration directory + "script_location": "migrations", # Migration directory "version_table": "alembic_version", # Version tracking table "include_extensions": ["litestar"], # Simple string list only }, @@ -25,4 +24,3 @@ def test_basic_migration_config() -> None: assert config.migration_config["script_location"] == "migrations" assert "litestar" in config.migration_config["include_extensions"] - diff --git a/docs/examples/usage/test_configuration_22.py b/docs/examples/usage/test_configuration_22.py index a4f49ad9..42a497c6 100644 --- a/docs/examples/usage/test_configuration_22.py +++ b/docs/examples/usage/test_configuration_22.py @@ -1,18 +1,17 @@ from sqlspec.adapters.asyncpg import AsyncpgConfig -def test_basic_migration_config(): +def test_basic_migration_config() -> None: config = AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/db"}, extension_config={ "litestar": {"session_table": "custom_sessions"} # Extension settings }, migration_config={ - "script_location": "migrations", # Migration directory + "script_location": "migrations", # Migration directory "version_table": "alembic_version", # Version tracking table "include_extensions": ["litestar"], # Simple string list only - } + }, ) assert config.migration_config["script_location"] == "migrations" assert "litestar" in config.migration_config["include_extensions"] - diff --git a/docs/examples/usage/test_configuration_22.txt b/docs/examples/usage/test_configuration_22.txt index 4d4eb539..f64aafec 100644 --- a/docs/examples/usage/test_configuration_22.txt +++ b/docs/examples/usage/test_configuration_22.txt @@ -6,4 +6,3 @@ sqlspec --config myapp.config upgrade # Rollback sqlspec --config myapp.config downgrade -1 - diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py index d2ba6e6f..8696337a 100644 --- a/docs/examples/usage/test_configuration_23.py +++ b/docs/examples/usage/test_configuration_23.py @@ -6,10 +6,7 @@ import pytest -@pytest.mark.skipif( - not os.getenv("TEST_ASYNCPG", "0") == "1", - reason="AsyncPG integration tests disabled", -) +@pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") def test_environment_based_configuration() -> None: """Test environment-based configuration pattern.""" from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -39,4 +36,3 @@ def test_environment_based_configuration() -> None: assert config.pool_config["user"] == "testuser" assert config.pool_config["password"] == "testpass" assert config.pool_config["database"] == "testdb" - diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/test_configuration_24.py index a2aa9069..e5d9bc0e 100644 --- a/docs/examples/usage/test_configuration_24.py +++ b/docs/examples/usage/test_configuration_24.py @@ -4,22 +4,14 @@ @pytest.mark.skipif( - not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), - reason="AsyncPG integration tests disabled", + not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" ) def test_connection_pooling_best_practice() -> None: """Test connection pooling best practice configuration.""" from sqlspec.adapters.asyncpg import AsyncpgConfig - config = AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/db", - "min_size": 10, - "max_size": 20, - } - ) + config = AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": 20}) assert config.pool_config["min_size"] == 10 assert config.pool_config["max_size"] == 20 assert config.supports_connection_pooling is True - diff --git a/docs/examples/usage/test_configuration_25.py b/docs/examples/usage/test_configuration_25.py index 75fbe5b7..bab5d292 100644 --- a/docs/examples/usage/test_configuration_25.py +++ b/docs/examples/usage/test_configuration_25.py @@ -5,11 +5,7 @@ def test_enable_caching_best_practice() -> None: """Test caching best practice configuration.""" from sqlspec.core.statement import StatementConfig - statement_config = StatementConfig( - dialect="postgres", - enable_caching=True, - ) + statement_config = StatementConfig(dialect="postgres", enable_caching=True) assert statement_config.enable_caching is True assert statement_config.dialect == "postgres" - diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py index 1f0aef18..7516f8e3 100644 --- a/docs/examples/usage/test_configuration_26.py +++ b/docs/examples/usage/test_configuration_26.py @@ -13,4 +13,3 @@ def test_tune_pool_sizes_best_practice() -> None: io_bound_pool_config = {"min_size": 20, "max_size": 50} assert io_bound_pool_config["min_size"] == 20 assert io_bound_pool_config["max_size"] == 50 - diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py index a4169c70..d023786e 100644 --- a/docs/examples/usage/test_configuration_27.py +++ b/docs/examples/usage/test_configuration_27.py @@ -13,9 +13,7 @@ async def test_cleanup_resources_best_practice() -> None: with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: spec = SQLSpec() - db = spec.add_config( - AiosqliteConfig(pool_config={"database": tmp.name}) - ) + db = spec.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) # Use the connection async with spec.provide_session(db) as session: @@ -26,4 +24,3 @@ async def test_cleanup_resources_best_practice() -> None: # Verify pools are closed assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") - diff --git a/docs/examples/usage/test_configuration_3.py b/docs/examples/usage/test_configuration_3.py index 7592f960..646322f3 100644 --- a/docs/examples/usage/test_configuration_3.py +++ b/docs/examples/usage/test_configuration_3.py @@ -1,15 +1,10 @@ def test_memory_databases() -> None: from sqlspec.adapters.sqlite import SqliteConfig + # In-memory database (isolated per connection) config = SqliteConfig(pool_config={"database": ":memory:"}) assert config.pool_config["database"] == ":memory:" # Shared memory database - shared_config = SqliteConfig( - pool_config={ - "database": "file:memdb1?mode=memory&cache=shared", - "uri": True - } - ) + shared_config = SqliteConfig(pool_config={"database": "file:memdb1?mode=memory&cache=shared", "uri": True}) assert shared_config.pool_config["database"] == "file:memdb1?mode=memory&cache=shared" - diff --git a/docs/examples/usage/test_configuration_4.py b/docs/examples/usage/test_configuration_4.py index 0983c7f5..a521d5d3 100644 --- a/docs/examples/usage/test_configuration_4.py +++ b/docs/examples/usage/test_configuration_4.py @@ -1,5 +1,6 @@ -def test_asyncpg_config_setup(): +def test_asyncpg_config_setup() -> None: from sqlspec.adapters.asyncpg import AsyncpgConfig + config = AsyncpgConfig( pool_config={ "dsn": "postgresql://user:pass@localhost:5432/dbname", @@ -14,4 +15,3 @@ def test_asyncpg_config_setup(): } ) assert config.pool_config["host"] == "localhost" - diff --git a/docs/examples/usage/test_configuration_5.py b/docs/examples/usage/test_configuration_5.py index 72b45243..3f5ac8b7 100644 --- a/docs/examples/usage/test_configuration_5.py +++ b/docs/examples/usage/test_configuration_5.py @@ -1,6 +1,6 @@ import pytest + @pytest.mark.skip(reason="PsycopgConfig does not exist in the codebase.") -def test_psycopg_config_setup(): +def test_psycopg_config_setup() -> None: pass - diff --git a/docs/examples/usage/test_configuration_6.py b/docs/examples/usage/test_configuration_6.py index c25597a8..8bade4d6 100644 --- a/docs/examples/usage/test_configuration_6.py +++ b/docs/examples/usage/test_configuration_6.py @@ -1,6 +1,6 @@ -from sqlspec.adapters.asyncmy import AsyncmyConfig +def test_asyncmy_config_setup() -> None: + from sqlspec.adapters.asyncmy import AsyncmyConfig -def test_asyncmy_config_setup(): config = AsyncmyConfig( pool_config={ "host": "localhost", @@ -15,4 +15,3 @@ def test_asyncmy_config_setup(): } ) assert config.pool_config["port"] == 3306 - diff --git a/docs/examples/usage/test_configuration_7.py b/docs/examples/usage/test_configuration_7.py index f17f7197..f8c2df3b 100644 --- a/docs/examples/usage/test_configuration_7.py +++ b/docs/examples/usage/test_configuration_7.py @@ -1,14 +1,8 @@ -from sqlspec.adapters.duckdb import DuckDBConfig +def test_duckdb_config_setup() -> None: + from sqlspec.adapters.duckdb import DuckDBConfig -def test_duckdb_config_setup(): in_memory_config = DuckDBConfig() assert in_memory_config.pool_config == {} - persistent_config = DuckDBConfig( - pool_config={ - "database": "analytics.duckdb", - "read_only": False, - } - ) + persistent_config = DuckDBConfig(pool_config={"database": "analytics.duckdb", "read_only": False}) assert persistent_config.pool_config["read_only"] is False - diff --git a/docs/examples/usage/test_configuration_8.py b/docs/examples/usage/test_configuration_8.py index 9a963f97..6dff6ad5 100644 --- a/docs/examples/usage/test_configuration_8.py +++ b/docs/examples/usage/test_configuration_8.py @@ -1,6 +1,6 @@ -from sqlspec.adapters.asyncpg import AsyncpgConfig +def test_asyncpg_pool_setup() -> None: + from sqlspec.adapters.asyncpg import AsyncpgConfig -def test_asyncpg_pool_setup(): config = AsyncpgConfig( pool_config={ "dsn": "postgresql://localhost/db", @@ -11,4 +11,3 @@ def test_asyncpg_pool_setup(): } ) assert config.pool_config["min_size"] == 10 - diff --git a/docs/examples/usage/test_configuration_9.py b/docs/examples/usage/test_configuration_9.py index 92ecc11d..da08c23c 100644 --- a/docs/examples/usage/test_configuration_9.py +++ b/docs/examples/usage/test_configuration_9.py @@ -1,8 +1,7 @@ -from sqlspec import SQLSpec -from sqlspec.adapters.asyncpg import AsyncpgConfig +def test_pool_lifecycle() -> None: + from sqlspec import SQLSpec + from sqlspec.adapters.asyncpg import AsyncpgConfig -def test_pool_lifecycle(): spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) assert db.pool_config["dsn"] == "postgresql://localhost/db" - diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index 89c1db49..8fecd67e 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -52,7 +52,8 @@ PostgreSQL Configuration (asyncpg) .. literalinclude:: /examples/usage/test_configuration_4.py :language: python :caption: `postgres asyncpg configuration` - :lines: 2-15 + :lines: 2-16 + :dedent: 2 PostgreSQL Configuration (psycopg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,6 +66,7 @@ PostgreSQL Configuration (psycopg) :language: python :caption: `postgres psycopg configuration` :lines: 1-6 + :dedent: 2 MySQL Configuration (asyncmy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +74,8 @@ MySQL Configuration (asyncmy) .. literalinclude:: /examples/usage/test_configuration_6.py :language: python :caption: `mysql asyncmy configuration` - :lines: 1-19 + :lines: 2-15 + :dedent: 2 DuckDB Configuration ^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +83,8 @@ DuckDB Configuration .. literalinclude:: /examples/usage/test_configuration_7.py :language: python :caption: `duckdb configuration` - :lines: 1-15 + :lines: 2-11 + :dedent: 2 Connection Pooling ------------------ @@ -93,14 +97,16 @@ Pool Configuration .. literalinclude:: /examples/usage/test_configuration_8.py :language: python :caption: `pool configuration` - :lines: 1-15 + :lines: 2-11 + :dedent: 2 **Pool Lifecycle Management** .. literalinclude:: /examples/usage/test_configuration_9.py :language: python :caption: `pool lifecycle management` - :lines: 1-8 + :lines: 2-7 + :dedent: 2 Using Pre-Created Pools ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +114,8 @@ Using Pre-Created Pools .. literalinclude:: /examples/usage/test_configuration_10.py :language: python :caption: `using pre-created pools` - :lines: 1-12 + :lines: 2-9 + :dedent: 2 No-Pooling Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +123,8 @@ No-Pooling Configuration .. literalinclude:: /examples/usage/test_configuration_11.py :language: python :caption: `no-pooling configuration` - :lines: 1-6 + :lines: 2-4 + :dedent: 2 Statement Configuration ----------------------- @@ -129,7 +137,8 @@ Basic Statement Config .. literalinclude:: /examples/usage/test_configuration_12.py :language: python :caption: `basic statement config` - :lines: 1-21 + :lines: 2-16 + :dedent: 2 Parameter Style Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,7 +146,8 @@ Parameter Style Configuration .. literalinclude:: /examples/usage/test_configuration_13.py :language: python :caption: `parameter style configuration` - :lines: 1-21 + :lines: 2-17 + :dedent: 2 **Parameter Styles** @@ -146,7 +156,8 @@ SQLSpec supports multiple parameter placeholder styles: .. literalinclude:: /examples/usage/test_configuration_14.py :language: python :caption: `parameter styles` - :lines: 1-24 + :lines: 2-21 + :dedent: 2 Validation Configuration ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -158,7 +169,8 @@ Disable validation for performance-critical paths where input is trusted: .. literalinclude:: /examples/usage/test_configuration_15.py :language: python :caption: `validation configuration` - :lines: 1-22 + :lines: 5-17 + :dedent: 2 Cache Configuration ------------------- @@ -171,7 +183,8 @@ Global Cache Configuration .. literalinclude:: /examples/usage/test_configuration_16.py :language: python :caption: `global cache configuration` - :lines: 1-29 + :lines: 6-28 + :dedent: 2 Per-Instance Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -180,6 +193,7 @@ Per-Instance Cache Configuration :language: python :caption: `per-instance cache configuration` :lines: 1-28 + :dedent: 2 Cache Statistics ^^^^^^^^^^^^^^^^ @@ -190,6 +204,7 @@ Monitor cache statistics: :language: python :caption: `cache statistics` :lines: 1-19 + :dedent: 2 Clear Cache ^^^^^^^^^^^ @@ -198,6 +213,7 @@ Clear Cache :language: python :caption: `clear cache` :lines: 1-27 + :dedent: 2 Multiple Database Configurations --------------------------------- @@ -211,6 +227,7 @@ Binding Multiple Configs :language: python :caption: `binding multiple configurations` :lines: 1-30 + :dedent: 2 Named Bindings ^^^^^^^^^^^^^^ @@ -221,6 +238,7 @@ Use bind keys for clearer configuration management: :language: python :caption: `named bindings` :lines: 1-29 + :dedent: 2 Migration Configuration ----------------------- @@ -241,6 +259,7 @@ Basic Migration Config .. literalinclude:: /examples/usage/test_configuration_22.txt :language: text :caption: `migration CLI` + :dedent: 2 Extension Migration Versioning From da7a9ad0bd65845b43241781ccdcfd4d101f3bcf Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 14:21:49 +0100 Subject: [PATCH 04/18] more correct lines --- docs/examples/usage/test_configuration_17.py | 4 ++-- docs/examples/usage/test_configuration_19.py | 4 ++-- docs/examples/usage/test_configuration_20.py | 4 ++-- docs/examples/usage/test_configuration_22.py | 5 ++--- docs/usage/configuration.rst | 22 +++++++------------- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/examples/usage/test_configuration_17.py b/docs/examples/usage/test_configuration_17.py index f122e44a..4a3ac587 100644 --- a/docs/examples/usage/test_configuration_17.py +++ b/docs/examples/usage/test_configuration_17.py @@ -1,10 +1,10 @@ """Test configuration example: Cache statistics tracking.""" -import tempfile - def test_cache_statistics() -> None: """Test cache statistics tracking.""" + import tempfile + from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig from sqlspec.core.cache import get_cache_statistics, log_cache_stats diff --git a/docs/examples/usage/test_configuration_19.py b/docs/examples/usage/test_configuration_19.py index 088d81e2..bc254e66 100644 --- a/docs/examples/usage/test_configuration_19.py +++ b/docs/examples/usage/test_configuration_19.py @@ -1,10 +1,10 @@ """Test configuration example: Binding multiple database configurations.""" -import tempfile - def test_binding_multiple_configs() -> None: """Test binding multiple database configurations.""" + import tempfile + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.adapters.sqlite import SqliteConfig diff --git a/docs/examples/usage/test_configuration_20.py b/docs/examples/usage/test_configuration_20.py index cb14d803..02d4fa6a 100644 --- a/docs/examples/usage/test_configuration_20.py +++ b/docs/examples/usage/test_configuration_20.py @@ -1,10 +1,10 @@ """Test configuration example: Named database bindings.""" -import tempfile - def test_named_bindings() -> None: """Test named database bindings.""" + import tempfile + from sqlspec import SQLSpec from sqlspec.adapters.asyncpg import AsyncpgConfig from sqlspec.adapters.sqlite import SqliteConfig diff --git a/docs/examples/usage/test_configuration_22.py b/docs/examples/usage/test_configuration_22.py index 42a497c6..93ef2185 100644 --- a/docs/examples/usage/test_configuration_22.py +++ b/docs/examples/usage/test_configuration_22.py @@ -1,7 +1,6 @@ -from sqlspec.adapters.asyncpg import AsyncpgConfig - - def test_basic_migration_config() -> None: + from sqlspec.adapters.asyncpg import AsyncpgConfig + config = AsyncpgConfig( pool_config={"dsn": "postgresql://localhost/db"}, extension_config={ diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index 8fecd67e..a0b920c7 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -21,7 +21,7 @@ The simplest way to use SQLSpec is with default configuration: .. literalinclude:: /examples/usage/test_configuration_1.py :language: python :caption: `basic configuration` - :lines: 2-12 + :lines: 2-14 :dedent: 2 Database Configurations @@ -62,11 +62,6 @@ PostgreSQL Configuration (psycopg) The `PsycopgConfig` class referenced here is for documentation purposes only and may not be present in the codebase. Future releases may include this feature if demand warrants. -.. literalinclude:: /examples/usage/test_configuration_5.py - :language: python - :caption: `postgres psycopg configuration` - :lines: 1-6 - :dedent: 2 MySQL Configuration (asyncmy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -192,7 +187,7 @@ Per-Instance Cache Configuration .. literalinclude:: /examples/usage/test_configuration_17.py :language: python :caption: `per-instance cache configuration` - :lines: 1-28 + :lines: 6-27 :dedent: 2 Cache Statistics @@ -203,7 +198,7 @@ Monitor cache statistics: .. literalinclude:: /examples/usage/test_configuration_18.py :language: python :caption: `cache statistics` - :lines: 1-19 + :lines: 6-18 :dedent: 2 Clear Cache @@ -212,7 +207,7 @@ Clear Cache .. literalinclude:: /examples/usage/test_configuration_19.py :language: python :caption: `clear cache` - :lines: 1-27 + :lines: 6-24 :dedent: 2 Multiple Database Configurations @@ -226,7 +221,7 @@ Binding Multiple Configs .. literalinclude:: /examples/usage/test_configuration_20.py :language: python :caption: `binding multiple configurations` - :lines: 1-30 + :lines: 6-24 :dedent: 2 Named Bindings @@ -237,7 +232,7 @@ Use bind keys for clearer configuration management: .. literalinclude:: /examples/usage/test_configuration_21.py :language: python :caption: `named bindings` - :lines: 1-29 + :lines: 11-26 :dedent: 2 Migration Configuration @@ -251,15 +246,14 @@ Basic Migration Config .. literalinclude:: /examples/usage/test_configuration_22.py :language: python :caption: `basic migration config` - :lines: 12-24 - :dedent: 4 + :lines: 2-15 + :dedent: 2 **Migration CLI** .. literalinclude:: /examples/usage/test_configuration_22.txt :language: text :caption: `migration CLI` - :dedent: 2 Extension Migration Versioning From 0ff5c15656f2dd1f41174b448e6b4a899b0386be Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 14:36:29 +0100 Subject: [PATCH 05/18] delete --- docs/examples/usage/test_configuration_23.py | 38 -------------------- docs/examples/usage/test_configuration_24.py | 17 --------- docs/examples/usage/test_configuration_25.py | 11 ------ docs/examples/usage/test_configuration_26.py | 15 -------- docs/examples/usage/test_configuration_27.py | 26 -------------- 5 files changed, 107 deletions(-) delete mode 100644 docs/examples/usage/test_configuration_23.py delete mode 100644 docs/examples/usage/test_configuration_24.py delete mode 100644 docs/examples/usage/test_configuration_25.py delete mode 100644 docs/examples/usage/test_configuration_26.py delete mode 100644 docs/examples/usage/test_configuration_27.py diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py deleted file mode 100644 index 8696337a..00000000 --- a/docs/examples/usage/test_configuration_23.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Test configuration example: Environment-based configuration.""" - -import os -from unittest.mock import patch - -import pytest - - -@pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") -def test_environment_based_configuration() -> None: - """Test environment-based configuration pattern.""" - from sqlspec.adapters.asyncpg import AsyncpgConfig - - # Mock environment variables - env_vars = { - "DB_HOST": "testhost", - "DB_PORT": "5433", - "DB_USER": "testuser", - "DB_PASSWORD": "testpass", - "DB_NAME": "testdb", - } - - with patch.dict(os.environ, env_vars, clear=False): - config = AsyncpgConfig( - pool_config={ - "host": os.getenv("DB_HOST", "localhost"), - "port": int(os.getenv("DB_PORT", "5432")), - "user": os.getenv("DB_USER"), - "password": os.getenv("DB_PASSWORD"), - "database": os.getenv("DB_NAME"), - } - ) - - assert config.pool_config["host"] == "testhost" - assert config.pool_config["port"] == 5433 - assert config.pool_config["user"] == "testuser" - assert config.pool_config["password"] == "testpass" - assert config.pool_config["database"] == "testdb" diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/test_configuration_24.py deleted file mode 100644 index e5d9bc0e..00000000 --- a/docs/examples/usage/test_configuration_24.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Test configuration example: Best practice - Use connection pooling.""" - -import pytest - - -@pytest.mark.skipif( - not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" -) -def test_connection_pooling_best_practice() -> None: - """Test connection pooling best practice configuration.""" - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": 20}) - - assert config.pool_config["min_size"] == 10 - assert config.pool_config["max_size"] == 20 - assert config.supports_connection_pooling is True diff --git a/docs/examples/usage/test_configuration_25.py b/docs/examples/usage/test_configuration_25.py deleted file mode 100644 index bab5d292..00000000 --- a/docs/examples/usage/test_configuration_25.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Test configuration example: Best practice - Enable caching.""" - - -def test_enable_caching_best_practice() -> None: - """Test caching best practice configuration.""" - from sqlspec.core.statement import StatementConfig - - statement_config = StatementConfig(dialect="postgres", enable_caching=True) - - assert statement_config.enable_caching is True - assert statement_config.dialect == "postgres" diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py deleted file mode 100644 index 7516f8e3..00000000 --- a/docs/examples/usage/test_configuration_26.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Test configuration example: Best practice - Tune pool sizes.""" - - -def test_tune_pool_sizes_best_practice() -> None: - """Test pool sizing best practices for different workloads.""" - - # CPU-bound workload - smaller pool - cpu_bound_pool_config = {"min_size": 5, "max_size": 10} - assert cpu_bound_pool_config["min_size"] == 5 - assert cpu_bound_pool_config["max_size"] == 10 - - # I/O-bound workload - larger pool - io_bound_pool_config = {"min_size": 20, "max_size": 50} - assert io_bound_pool_config["min_size"] == 20 - assert io_bound_pool_config["max_size"] == 50 diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py deleted file mode 100644 index d023786e..00000000 --- a/docs/examples/usage/test_configuration_27.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Test configuration example: Best practice - Clean up resources.""" - -import tempfile - -import pytest - - -@pytest.mark.asyncio -async def test_cleanup_resources_best_practice() -> None: - """Test resource cleanup best practice.""" - from sqlspec import SQLSpec - from sqlspec.adapters.aiosqlite import AiosqliteConfig - - with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: - spec = SQLSpec() - db = spec.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) - - # Use the connection - async with spec.provide_session(db) as session: - await session.execute("CREATE TABLE test (id INTEGER)") - - # Clean up resources - important for async adapters - await spec.close_all_pools() - - # Verify pools are closed - assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") From 2d28f08e570a6824d6b5da0ae07c95eae2cf1fb9 Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 14:37:50 +0100 Subject: [PATCH 06/18] Revert "delete" This reverts commit 0ff5c15656f2dd1f41174b448e6b4a899b0386be. --- docs/examples/usage/test_configuration_23.py | 38 ++++++++++++++++++++ docs/examples/usage/test_configuration_24.py | 17 +++++++++ docs/examples/usage/test_configuration_25.py | 11 ++++++ docs/examples/usage/test_configuration_26.py | 15 ++++++++ docs/examples/usage/test_configuration_27.py | 26 ++++++++++++++ 5 files changed, 107 insertions(+) create mode 100644 docs/examples/usage/test_configuration_23.py create mode 100644 docs/examples/usage/test_configuration_24.py create mode 100644 docs/examples/usage/test_configuration_25.py create mode 100644 docs/examples/usage/test_configuration_26.py create mode 100644 docs/examples/usage/test_configuration_27.py diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py new file mode 100644 index 00000000..8696337a --- /dev/null +++ b/docs/examples/usage/test_configuration_23.py @@ -0,0 +1,38 @@ +"""Test configuration example: Environment-based configuration.""" + +import os +from unittest.mock import patch + +import pytest + + +@pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") +def test_environment_based_configuration() -> None: + """Test environment-based configuration pattern.""" + from sqlspec.adapters.asyncpg import AsyncpgConfig + + # Mock environment variables + env_vars = { + "DB_HOST": "testhost", + "DB_PORT": "5433", + "DB_USER": "testuser", + "DB_PASSWORD": "testpass", + "DB_NAME": "testdb", + } + + with patch.dict(os.environ, env_vars, clear=False): + config = AsyncpgConfig( + pool_config={ + "host": os.getenv("DB_HOST", "localhost"), + "port": int(os.getenv("DB_PORT", "5432")), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME"), + } + ) + + assert config.pool_config["host"] == "testhost" + assert config.pool_config["port"] == 5433 + assert config.pool_config["user"] == "testuser" + assert config.pool_config["password"] == "testpass" + assert config.pool_config["database"] == "testdb" diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/test_configuration_24.py new file mode 100644 index 00000000..e5d9bc0e --- /dev/null +++ b/docs/examples/usage/test_configuration_24.py @@ -0,0 +1,17 @@ +"""Test configuration example: Best practice - Use connection pooling.""" + +import pytest + + +@pytest.mark.skipif( + not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" +) +def test_connection_pooling_best_practice() -> None: + """Test connection pooling best practice configuration.""" + from sqlspec.adapters.asyncpg import AsyncpgConfig + + config = AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": 20}) + + assert config.pool_config["min_size"] == 10 + assert config.pool_config["max_size"] == 20 + assert config.supports_connection_pooling is True diff --git a/docs/examples/usage/test_configuration_25.py b/docs/examples/usage/test_configuration_25.py new file mode 100644 index 00000000..bab5d292 --- /dev/null +++ b/docs/examples/usage/test_configuration_25.py @@ -0,0 +1,11 @@ +"""Test configuration example: Best practice - Enable caching.""" + + +def test_enable_caching_best_practice() -> None: + """Test caching best practice configuration.""" + from sqlspec.core.statement import StatementConfig + + statement_config = StatementConfig(dialect="postgres", enable_caching=True) + + assert statement_config.enable_caching is True + assert statement_config.dialect == "postgres" diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py new file mode 100644 index 00000000..7516f8e3 --- /dev/null +++ b/docs/examples/usage/test_configuration_26.py @@ -0,0 +1,15 @@ +"""Test configuration example: Best practice - Tune pool sizes.""" + + +def test_tune_pool_sizes_best_practice() -> None: + """Test pool sizing best practices for different workloads.""" + + # CPU-bound workload - smaller pool + cpu_bound_pool_config = {"min_size": 5, "max_size": 10} + assert cpu_bound_pool_config["min_size"] == 5 + assert cpu_bound_pool_config["max_size"] == 10 + + # I/O-bound workload - larger pool + io_bound_pool_config = {"min_size": 20, "max_size": 50} + assert io_bound_pool_config["min_size"] == 20 + assert io_bound_pool_config["max_size"] == 50 diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py new file mode 100644 index 00000000..d023786e --- /dev/null +++ b/docs/examples/usage/test_configuration_27.py @@ -0,0 +1,26 @@ +"""Test configuration example: Best practice - Clean up resources.""" + +import tempfile + +import pytest + + +@pytest.mark.asyncio +async def test_cleanup_resources_best_practice() -> None: + """Test resource cleanup best practice.""" + from sqlspec import SQLSpec + from sqlspec.adapters.aiosqlite import AiosqliteConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + spec = SQLSpec() + db = spec.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) + + # Use the connection + async with spec.provide_session(db) as session: + await session.execute("CREATE TABLE test (id INTEGER)") + + # Clean up resources - important for async adapters + await spec.close_all_pools() + + # Verify pools are closed + assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") From 2ca1def2d4dab104e80d984e650cc67037df6df4 Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 4 Nov 2025 17:35:54 +0100 Subject: [PATCH 07/18] add leftovers --- docs/examples/usage/test_configuration_22.txt | 8 -- docs/examples/usage/test_configuration_23.py | 29 ++++- docs/examples/usage/test_configuration_26.py | 13 +- docs/examples/usage/test_configuration_27.py | 3 +- docs/usage/configuration.rst | 111 ++++++++---------- 5 files changed, 88 insertions(+), 76 deletions(-) delete mode 100644 docs/examples/usage/test_configuration_22.txt diff --git a/docs/examples/usage/test_configuration_22.txt b/docs/examples/usage/test_configuration_22.txt deleted file mode 100644 index f64aafec..00000000 --- a/docs/examples/usage/test_configuration_22.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Create migration -sqlspec --config myapp.config create-migration -m "Add users table" - -# Apply migrations -sqlspec --config myapp.config upgrade - -# Rollback -sqlspec --config myapp.config downgrade -1 diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py index 8696337a..598a73ed 100644 --- a/docs/examples/usage/test_configuration_23.py +++ b/docs/examples/usage/test_configuration_23.py @@ -6,10 +6,35 @@ import pytest +def test_extension_config() -> None: + from sqlspec.adapters.asyncpg import AsyncpgConfig + + config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db"}, + extension_config={ + "litestar": { + "connection_key": "db_connection", + "session_key": "db_session", + "pool_key": "db_pool", + "commit_mode": "autocommit", + "enable_correlation_middleware": True, + } + }, + ) + assert config.extension_config == { + "litestar": { + "connection_key": "db_connection", + "session_key": "db_session", + "pool_key": "db_pool", + "commit_mode": "autocommit", + "enable_correlation_middleware": True, + } + } + + @pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") def test_environment_based_configuration() -> None: """Test environment-based configuration pattern.""" - from sqlspec.adapters.asyncpg import AsyncpgConfig # Mock environment variables env_vars = { @@ -21,6 +46,8 @@ def test_environment_based_configuration() -> None: } with patch.dict(os.environ, env_vars, clear=False): + from sqlspec.adapters.asyncpg import AsyncpgConfig + config = AsyncpgConfig( pool_config={ "host": os.getenv("DB_HOST", "localhost"), diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py index 7516f8e3..af6fbe4a 100644 --- a/docs/examples/usage/test_configuration_26.py +++ b/docs/examples/usage/test_configuration_26.py @@ -1,6 +1,5 @@ """Test configuration example: Best practice - Tune pool sizes.""" - def test_tune_pool_sizes_best_practice() -> None: """Test pool sizing best practices for different workloads.""" @@ -13,3 +12,15 @@ def test_tune_pool_sizes_best_practice() -> None: io_bound_pool_config = {"min_size": 20, "max_size": 50} assert io_bound_pool_config["min_size"] == 20 assert io_bound_pool_config["max_size"] == 50 + +def test_disable_security_checks_best_practice() -> None: + """Test disabling security checks when necessary.""" + + from sqlspec.core.statement import StatementConfig + + # Example: Disabling security checks for trusted internal queries + statement_config = StatementConfig( + dialect="postgres", + enable_validation=False, # Skip security checks + ) + assert statement_config.enable_validation is False diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py index d023786e..a0190eff 100644 --- a/docs/examples/usage/test_configuration_27.py +++ b/docs/examples/usage/test_configuration_27.py @@ -1,6 +1,5 @@ """Test configuration example: Best practice - Clean up resources.""" -import tempfile import pytest @@ -8,6 +7,8 @@ @pytest.mark.asyncio async def test_cleanup_resources_best_practice() -> None: """Test resource cleanup best practice.""" + import tempfile + from sqlspec import SQLSpec from sqlspec.adapters.aiosqlite import AiosqliteConfig diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index a0b920c7..fdd11572 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -251,9 +251,17 @@ Basic Migration Config **Migration CLI** -.. literalinclude:: /examples/usage/test_configuration_22.txt - :language: text - :caption: `migration CLI` +.. code-block:: bash + + # Create migration + sqlspec --config myapp.config create-migration -m "Add users table" + + # Apply migrations + sqlspec --config myapp.config upgrade + + # Rollback + sqlspec --config myapp.config downgrade -1 + Extension Migration Versioning @@ -281,42 +289,22 @@ Framework integrations can be configured via ``extension_config``. Litestar Plugin Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": { - "connection_key": "db_connection", - "session_key": "db_session", - "pool_key": "db_pool", - "commit_mode": "autocommit", - "enable_correlation_middleware": True, - } - } - ) +.. literalinclude:: /examples/usage/test_configuration_23.py + :language: python + :caption: `litestar plugin configuration` + :lines: 10-31 + :dedent: 2 Environment-Based Configuration ------------------------------- Use environment variables for configuration: -.. code-block:: python - - import os - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={ - "host": os.getenv("DB_HOST", "localhost"), - "port": int(os.getenv("DB_PORT", "5432")), - "user": os.getenv("DB_USER"), - "password": os.getenv("DB_PASSWORD"), - "database": os.getenv("DB_NAME"), - } - ) +.. literalinclude:: /examples/usage/test_configuration_23.py + :language: python + :caption: `environnment-based configuration` + :lines: 49-59 + :dedent: 4 Configuration Best Practices ----------------------------- @@ -325,59 +313,52 @@ Configuration Best Practices Always use pooling in production: -.. code-block:: python - - config = AsyncpgConfig( - pool_config={ - "dsn": "postgresql://localhost/db", - "min_size": 10, - "max_size": 20, - } - ) +.. literalinclude:: /examples/usage/test_configuration_24.py + :language: python + :caption: `connection pooling` + :lines: 11-13 + :dedent: 2 **2. Enable Caching** Enable caching to avoid recompiling SQL statements: -.. code-block:: python - - statement_config = StatementConfig( - dialect="postgres", - enable_caching=True - ) +.. literalinclude:: /examples/usage/test_configuration_25.py + :language: python + :caption: `enable caching` + :lines: 6-8 + :dedent: 2 **3. Tune Pool Sizes** Size pools based on your workload: -.. code-block:: python - - # CPU-bound workload - pool_config = {"min_size": 5, "max_size": 10} - - # I/O-bound workload - pool_config = {"min_size": 20, "max_size": 50} +.. literalinclude:: /examples/usage/test_configuration_26.py + :language: python + :caption: `tune pool sizes` + :lines: 6-14 + :dedent: 2 **4. Disable Validation in Production** For trusted, performance-critical queries: -.. code-block:: python +.. literalinclude:: /examples/usage/test_configuration_26.py + :language: python + :caption: `no validation` + :lines: 19-25 + :dedent: 2 - statement_config = StatementConfig( - dialect="postgres", - enable_validation=False, # Skip security checks - ) **5. Clean Up Resources** Always close pools on shutdown: -.. code-block:: python - - # Synchronous cleanup (automatic with atexit) - # Asynchronous cleanup (manual) - await spec.close_all_pools() +.. literalinclude:: /examples/usage/test_configuration_27.py + :language: python + :caption: `cleanup resources` + :lines: 10-27 + :dedent: 2 Next Steps ---------- From a6256a6e319e8617193ba471f1e8900dd922e475 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 5 Nov 2025 07:35:16 +0100 Subject: [PATCH 08/18] fix tests, one not passing when using provide_session by bind_key --- docs/examples/usage/test_configuration_1.py | 2 +- docs/examples/usage/test_configuration_19.py | 6 +++--- docs/examples/usage/test_configuration_20.py | 8 ++++---- docs/examples/usage/test_configuration_26.py | 8 +++++--- docs/examples/usage/test_configuration_27.py | 1 - docs/examples/usage/test_configuration_3.py | 2 +- docs/examples/usage/test_configuration_7.py | 2 +- docs/examples/usage/test_configuration_9.py | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/examples/usage/test_configuration_1.py b/docs/examples/usage/test_configuration_1.py index df9f5428..1e64acfd 100644 --- a/docs/examples/usage/test_configuration_1.py +++ b/docs/examples/usage/test_configuration_1.py @@ -11,4 +11,4 @@ def test_sqlite_memory_db() -> None: # Use the database with spec.provide_session(db) as session: result = session.execute("SELECT 1") - assert result.fetchone()[0] == 1 + assert result[0] == {"1": 1} diff --git a/docs/examples/usage/test_configuration_19.py b/docs/examples/usage/test_configuration_19.py index bc254e66..dc94d0a9 100644 --- a/docs/examples/usage/test_configuration_19.py +++ b/docs/examples/usage/test_configuration_19.py @@ -14,11 +14,11 @@ def test_binding_multiple_configs() -> None: # Add multiple configurations sqlite_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) - postgres_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) + spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) # Use specific configuration with spec.provide_session(sqlite_db) as session: session.execute("SELECT 1") - assert sqlite_db.pool_config["database"] == tmp.name - assert postgres_db.pool_config["dsn"] == "postgresql://..." + assert spec.configs[SqliteConfig].pool_config["database"] == tmp.name + assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://..." diff --git a/docs/examples/usage/test_configuration_20.py b/docs/examples/usage/test_configuration_20.py index 02d4fa6a..47763e08 100644 --- a/docs/examples/usage/test_configuration_20.py +++ b/docs/examples/usage/test_configuration_20.py @@ -13,12 +13,12 @@ def test_named_bindings() -> None: spec = SQLSpec() # Add with bind keys - cache_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name}), bind_key="cache_db") - main_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."}), bind_key="main_db") + spec.add_config(SqliteConfig(pool_config={"database": tmp.name}, bind_key="cache_db")) + spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."}, bind_key="main_db")) # Access by bind key with spec.provide_session("cache_db") as session: session.execute("SELECT 1") - assert cache_db.pool_config["database"] == tmp.name - assert main_db.pool_config["dsn"] == "postgresql://..." + assert spec.configs[SqliteConfig].pool_config["database"] == tmp.name + assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://..." diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py index af6fbe4a..780f724e 100644 --- a/docs/examples/usage/test_configuration_26.py +++ b/docs/examples/usage/test_configuration_26.py @@ -1,5 +1,6 @@ """Test configuration example: Best practice - Tune pool sizes.""" + def test_tune_pool_sizes_best_practice() -> None: """Test pool sizing best practices for different workloads.""" @@ -13,6 +14,7 @@ def test_tune_pool_sizes_best_practice() -> None: assert io_bound_pool_config["min_size"] == 20 assert io_bound_pool_config["max_size"] == 50 + def test_disable_security_checks_best_practice() -> None: """Test disabling security checks when necessary.""" @@ -20,7 +22,7 @@ def test_disable_security_checks_best_practice() -> None: # Example: Disabling security checks for trusted internal queries statement_config = StatementConfig( - dialect="postgres", - enable_validation=False, # Skip security checks - ) + dialect="postgres", + enable_validation=False, # Skip security checks + ) assert statement_config.enable_validation is False diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py index a0190eff..ad871368 100644 --- a/docs/examples/usage/test_configuration_27.py +++ b/docs/examples/usage/test_configuration_27.py @@ -1,6 +1,5 @@ """Test configuration example: Best practice - Clean up resources.""" - import pytest diff --git a/docs/examples/usage/test_configuration_3.py b/docs/examples/usage/test_configuration_3.py index 646322f3..e53b38e3 100644 --- a/docs/examples/usage/test_configuration_3.py +++ b/docs/examples/usage/test_configuration_3.py @@ -3,7 +3,7 @@ def test_memory_databases() -> None: # In-memory database (isolated per connection) config = SqliteConfig(pool_config={"database": ":memory:"}) - assert config.pool_config["database"] == ":memory:" + assert ":memory_" in config.pool_config["database"] # Shared memory database shared_config = SqliteConfig(pool_config={"database": "file:memdb1?mode=memory&cache=shared", "uri": True}) diff --git a/docs/examples/usage/test_configuration_7.py b/docs/examples/usage/test_configuration_7.py index f8c2df3b..3152d6ed 100644 --- a/docs/examples/usage/test_configuration_7.py +++ b/docs/examples/usage/test_configuration_7.py @@ -2,7 +2,7 @@ def test_duckdb_config_setup() -> None: from sqlspec.adapters.duckdb import DuckDBConfig in_memory_config = DuckDBConfig() - assert in_memory_config.pool_config == {} + assert in_memory_config.pool_config.get("database") == ":memory:shared_db" persistent_config = DuckDBConfig(pool_config={"database": "analytics.duckdb", "read_only": False}) assert persistent_config.pool_config["read_only"] is False diff --git a/docs/examples/usage/test_configuration_9.py b/docs/examples/usage/test_configuration_9.py index da08c23c..2d365505 100644 --- a/docs/examples/usage/test_configuration_9.py +++ b/docs/examples/usage/test_configuration_9.py @@ -4,4 +4,4 @@ def test_pool_lifecycle() -> None: spec = SQLSpec() db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) - assert db.pool_config["dsn"] == "postgresql://localhost/db" + assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://localhost/db" From b85c152839899558625fd2ef4846d7fac500cdc5 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 5 Nov 2025 07:47:10 +0100 Subject: [PATCH 09/18] linter issues --- docs/examples/usage/test_configuration_10.py | 7 +++++-- docs/examples/usage/test_configuration_15.py | 6 ++++-- docs/examples/usage/test_configuration_23.py | 5 ++++- docs/examples/usage/test_configuration_24.py | 11 ++++++++--- docs/examples/usage/test_configuration_26.py | 17 +++++++++++------ docs/examples/usage/test_configuration_6.py | 7 +++++-- docs/examples/usage/test_configuration_8.py | 5 ++++- docs/examples/usage/test_configuration_9.py | 2 +- 8 files changed, 42 insertions(+), 18 deletions(-) diff --git a/docs/examples/usage/test_configuration_10.py b/docs/examples/usage/test_configuration_10.py index 0f7385db..e012888d 100644 --- a/docs/examples/usage/test_configuration_10.py +++ b/docs/examples/usage/test_configuration_10.py @@ -1,6 +1,9 @@ +POOL_INSTANCE = 20 + + def test_manual_pool() -> None: from sqlspec.adapters.asyncpg import AsyncpgConfig - pool = {"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": 20} + pool = {"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": POOL_INSTANCE} db = AsyncpgConfig(pool_instance=pool) - assert db.pool_instance["max_size"] == 20 + assert db.pool_instance["max_size"] == POOL_INSTANCE diff --git a/docs/examples/usage/test_configuration_15.py b/docs/examples/usage/test_configuration_15.py index b61246e8..359a4968 100644 --- a/docs/examples/usage/test_configuration_15.py +++ b/docs/examples/usage/test_configuration_15.py @@ -1,5 +1,7 @@ """Test configuration example: Global cache configuration.""" +SQL_CACHE_SIZE = 1000 + def test_global_cache_config() -> None: """Test global cache configuration.""" @@ -10,7 +12,7 @@ def test_global_cache_config() -> None: sql_cache_enabled=True, # Cache SQL strings fragment_cache_enabled=True, # Cache SQL fragments optimized_cache_enabled=True, # Cache optimized AST - sql_cache_size=1000, # Maximum cached SQL items + sql_cache_size=SQL_CACHE_SIZE, # Maximum cached SQL items ) # Update global cache configuration @@ -18,4 +20,4 @@ def test_global_cache_config() -> None: # Verify config applied assert cache_config.sql_cache_enabled is True - assert cache_config.sql_cache_size == 1000 + assert cache_config.sql_cache_size == SQL_CACHE_SIZE diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py index 598a73ed..b4bb9dc1 100644 --- a/docs/examples/usage/test_configuration_23.py +++ b/docs/examples/usage/test_configuration_23.py @@ -32,6 +32,9 @@ def test_extension_config() -> None: } +POSTGRES_PORT = 5433 + + @pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") def test_environment_based_configuration() -> None: """Test environment-based configuration pattern.""" @@ -59,7 +62,7 @@ def test_environment_based_configuration() -> None: ) assert config.pool_config["host"] == "testhost" - assert config.pool_config["port"] == 5433 + assert config.pool_config["port"] == POSTGRES_PORT assert config.pool_config["user"] == "testuser" assert config.pool_config["password"] == "testpass" assert config.pool_config["database"] == "testdb" diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/test_configuration_24.py index e5d9bc0e..1e012638 100644 --- a/docs/examples/usage/test_configuration_24.py +++ b/docs/examples/usage/test_configuration_24.py @@ -2,6 +2,9 @@ import pytest +MIN_POOL_SIZE = 10 +MAX_POOL_SIZE = 20 + @pytest.mark.skipif( not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" @@ -10,8 +13,10 @@ def test_connection_pooling_best_practice() -> None: """Test connection pooling best practice configuration.""" from sqlspec.adapters.asyncpg import AsyncpgConfig - config = AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": 20}) + config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db", "min_size": MIN_POOL_SIZE, "max_size": MAX_POOL_SIZE} + ) - assert config.pool_config["min_size"] == 10 - assert config.pool_config["max_size"] == 20 + assert config.pool_config["min_size"] == MIN_POOL_SIZE + assert config.pool_config["max_size"] == MAX_POOL_SIZE assert config.supports_connection_pooling is True diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py index 780f724e..7ffec874 100644 --- a/docs/examples/usage/test_configuration_26.py +++ b/docs/examples/usage/test_configuration_26.py @@ -1,18 +1,23 @@ """Test configuration example: Best practice - Tune pool sizes.""" +MIN_POOL_SIZE_CPU = 5 +MAX_POOL_SIZE_CPU = 10 +MIN_IO_BOUND_POOL_SIZE = 20 +MAX_IO_BOUND_POOL_SIZE = 50 + def test_tune_pool_sizes_best_practice() -> None: """Test pool sizing best practices for different workloads.""" # CPU-bound workload - smaller pool - cpu_bound_pool_config = {"min_size": 5, "max_size": 10} - assert cpu_bound_pool_config["min_size"] == 5 - assert cpu_bound_pool_config["max_size"] == 10 + cpu_bound_pool_config = {"min_size": MIN_POOL_SIZE_CPU, "max_size": MAX_POOL_SIZE_CPU} + assert cpu_bound_pool_config["min_size"] == MIN_POOL_SIZE_CPU + assert cpu_bound_pool_config["max_size"] == MAX_POOL_SIZE_CPU # I/O-bound workload - larger pool - io_bound_pool_config = {"min_size": 20, "max_size": 50} - assert io_bound_pool_config["min_size"] == 20 - assert io_bound_pool_config["max_size"] == 50 + io_bound_pool_config = {"min_size": MIN_IO_BOUND_POOL_SIZE, "max_size": MAX_IO_BOUND_POOL_SIZE} + assert io_bound_pool_config["min_size"] == MIN_IO_BOUND_POOL_SIZE + assert io_bound_pool_config["max_size"] == MAX_IO_BOUND_POOL_SIZE def test_disable_security_checks_best_practice() -> None: diff --git a/docs/examples/usage/test_configuration_6.py b/docs/examples/usage/test_configuration_6.py index 8bade4d6..9355cb4c 100644 --- a/docs/examples/usage/test_configuration_6.py +++ b/docs/examples/usage/test_configuration_6.py @@ -1,10 +1,13 @@ +MYSQL_PORT = 3306 + + def test_asyncmy_config_setup() -> None: from sqlspec.adapters.asyncmy import AsyncmyConfig config = AsyncmyConfig( pool_config={ "host": "localhost", - "port": 3306, + "port": MYSQL_PORT, "user": "myuser", "password": "mypassword", "database": "mydb", @@ -14,4 +17,4 @@ def test_asyncmy_config_setup() -> None: "pool_recycle": 3600, } ) - assert config.pool_config["port"] == 3306 + assert config.pool_config["port"] == MYSQL_PORT diff --git a/docs/examples/usage/test_configuration_8.py b/docs/examples/usage/test_configuration_8.py index 6dff6ad5..6cd0a16d 100644 --- a/docs/examples/usage/test_configuration_8.py +++ b/docs/examples/usage/test_configuration_8.py @@ -1,3 +1,6 @@ +MIN_POOL_SIZE = 10 + + def test_asyncpg_pool_setup() -> None: from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -10,4 +13,4 @@ def test_asyncpg_pool_setup() -> None: "max_inactive_connection_lifetime": 300.0, } ) - assert config.pool_config["min_size"] == 10 + assert config.pool_config["min_size"] == MIN_POOL_SIZE diff --git a/docs/examples/usage/test_configuration_9.py b/docs/examples/usage/test_configuration_9.py index 2d365505..6133ab2b 100644 --- a/docs/examples/usage/test_configuration_9.py +++ b/docs/examples/usage/test_configuration_9.py @@ -3,5 +3,5 @@ def test_pool_lifecycle() -> None: from sqlspec.adapters.asyncpg import AsyncpgConfig spec = SQLSpec() - db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) + spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://localhost/db" From 642816ac7d5d1e7703574076b234017729e3446e Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 5 Nov 2025 09:37:08 +0100 Subject: [PATCH 10/18] 12 --- docs/usage/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index fdd11572..933d1f7b 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -132,7 +132,7 @@ Basic Statement Config .. literalinclude:: /examples/usage/test_configuration_12.py :language: python :caption: `basic statement config` - :lines: 2-16 + :lines: 2-14 :dedent: 2 Parameter Style Configuration From 5dd81faa6879e01a7c6de9ef0a3a6ebbf89b24ca Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 5 Nov 2025 09:45:53 +0100 Subject: [PATCH 11/18] one test per block --- docs/examples/usage/test_configuration_23.py | 41 ---------------- docs/examples/usage/test_configuration_24.py | 49 ++++++++++++++------ docs/examples/usage/test_configuration_25.py | 25 +++++++--- docs/examples/usage/test_configuration_26.py | 36 +++----------- docs/examples/usage/test_configuration_27.py | 36 ++++++-------- docs/examples/usage/test_configuration_28.py | 13 ++++++ docs/examples/usage/test_configuration_29.py | 26 +++++++++++ docs/usage/configuration.rst | 20 ++++---- 8 files changed, 123 insertions(+), 123 deletions(-) create mode 100644 docs/examples/usage/test_configuration_28.py create mode 100644 docs/examples/usage/test_configuration_29.py diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py index b4bb9dc1..0333748c 100644 --- a/docs/examples/usage/test_configuration_23.py +++ b/docs/examples/usage/test_configuration_23.py @@ -1,10 +1,5 @@ """Test configuration example: Environment-based configuration.""" -import os -from unittest.mock import patch - -import pytest - def test_extension_config() -> None: from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -30,39 +25,3 @@ def test_extension_config() -> None: "enable_correlation_middleware": True, } } - - -POSTGRES_PORT = 5433 - - -@pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") -def test_environment_based_configuration() -> None: - """Test environment-based configuration pattern.""" - - # Mock environment variables - env_vars = { - "DB_HOST": "testhost", - "DB_PORT": "5433", - "DB_USER": "testuser", - "DB_PASSWORD": "testpass", - "DB_NAME": "testdb", - } - - with patch.dict(os.environ, env_vars, clear=False): - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={ - "host": os.getenv("DB_HOST", "localhost"), - "port": int(os.getenv("DB_PORT", "5432")), - "user": os.getenv("DB_USER"), - "password": os.getenv("DB_PASSWORD"), - "database": os.getenv("DB_NAME"), - } - ) - - assert config.pool_config["host"] == "testhost" - assert config.pool_config["port"] == POSTGRES_PORT - assert config.pool_config["user"] == "testuser" - assert config.pool_config["password"] == "testpass" - assert config.pool_config["database"] == "testdb" diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/test_configuration_24.py index 1e012638..f38b551e 100644 --- a/docs/examples/usage/test_configuration_24.py +++ b/docs/examples/usage/test_configuration_24.py @@ -1,22 +1,41 @@ -"""Test configuration example: Best practice - Use connection pooling.""" +"""Test configuration example: Environment-based configuration.""" + +import os +from unittest.mock import patch import pytest -MIN_POOL_SIZE = 10 -MAX_POOL_SIZE = 20 +POSTGRES_PORT = 5433 + + +@pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") +def test_environment_based_configuration() -> None: + """Test environment-based configuration pattern.""" + # Mock environment variables + env_vars = { + "DB_HOST": "testhost", + "DB_PORT": "5433", + "DB_USER": "testuser", + "DB_PASSWORD": "testpass", + "DB_NAME": "testdb", + } -@pytest.mark.skipif( - not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" -) -def test_connection_pooling_best_practice() -> None: - """Test connection pooling best practice configuration.""" - from sqlspec.adapters.asyncpg import AsyncpgConfig + with patch.dict(os.environ, env_vars, clear=False): + from sqlspec.adapters.asyncpg import AsyncpgConfig - config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db", "min_size": MIN_POOL_SIZE, "max_size": MAX_POOL_SIZE} - ) + config = AsyncpgConfig( + pool_config={ + "host": os.getenv("DB_HOST", "localhost"), + "port": int(os.getenv("DB_PORT", "5432")), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME"), + } + ) - assert config.pool_config["min_size"] == MIN_POOL_SIZE - assert config.pool_config["max_size"] == MAX_POOL_SIZE - assert config.supports_connection_pooling is True + assert config.pool_config["host"] == "testhost" + assert config.pool_config["port"] == POSTGRES_PORT + assert config.pool_config["user"] == "testuser" + assert config.pool_config["password"] == "testpass" + assert config.pool_config["database"] == "testdb" diff --git a/docs/examples/usage/test_configuration_25.py b/docs/examples/usage/test_configuration_25.py index bab5d292..1e012638 100644 --- a/docs/examples/usage/test_configuration_25.py +++ b/docs/examples/usage/test_configuration_25.py @@ -1,11 +1,22 @@ -"""Test configuration example: Best practice - Enable caching.""" +"""Test configuration example: Best practice - Use connection pooling.""" +import pytest -def test_enable_caching_best_practice() -> None: - """Test caching best practice configuration.""" - from sqlspec.core.statement import StatementConfig +MIN_POOL_SIZE = 10 +MAX_POOL_SIZE = 20 - statement_config = StatementConfig(dialect="postgres", enable_caching=True) - assert statement_config.enable_caching is True - assert statement_config.dialect == "postgres" +@pytest.mark.skipif( + not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" +) +def test_connection_pooling_best_practice() -> None: + """Test connection pooling best practice configuration.""" + from sqlspec.adapters.asyncpg import AsyncpgConfig + + config = AsyncpgConfig( + pool_config={"dsn": "postgresql://localhost/db", "min_size": MIN_POOL_SIZE, "max_size": MAX_POOL_SIZE} + ) + + assert config.pool_config["min_size"] == MIN_POOL_SIZE + assert config.pool_config["max_size"] == MAX_POOL_SIZE + assert config.supports_connection_pooling is True diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/test_configuration_26.py index 7ffec874..bab5d292 100644 --- a/docs/examples/usage/test_configuration_26.py +++ b/docs/examples/usage/test_configuration_26.py @@ -1,33 +1,11 @@ -"""Test configuration example: Best practice - Tune pool sizes.""" +"""Test configuration example: Best practice - Enable caching.""" -MIN_POOL_SIZE_CPU = 5 -MAX_POOL_SIZE_CPU = 10 -MIN_IO_BOUND_POOL_SIZE = 20 -MAX_IO_BOUND_POOL_SIZE = 50 - - -def test_tune_pool_sizes_best_practice() -> None: - """Test pool sizing best practices for different workloads.""" - - # CPU-bound workload - smaller pool - cpu_bound_pool_config = {"min_size": MIN_POOL_SIZE_CPU, "max_size": MAX_POOL_SIZE_CPU} - assert cpu_bound_pool_config["min_size"] == MIN_POOL_SIZE_CPU - assert cpu_bound_pool_config["max_size"] == MAX_POOL_SIZE_CPU - - # I/O-bound workload - larger pool - io_bound_pool_config = {"min_size": MIN_IO_BOUND_POOL_SIZE, "max_size": MAX_IO_BOUND_POOL_SIZE} - assert io_bound_pool_config["min_size"] == MIN_IO_BOUND_POOL_SIZE - assert io_bound_pool_config["max_size"] == MAX_IO_BOUND_POOL_SIZE - - -def test_disable_security_checks_best_practice() -> None: - """Test disabling security checks when necessary.""" +def test_enable_caching_best_practice() -> None: + """Test caching best practice configuration.""" from sqlspec.core.statement import StatementConfig - # Example: Disabling security checks for trusted internal queries - statement_config = StatementConfig( - dialect="postgres", - enable_validation=False, # Skip security checks - ) - assert statement_config.enable_validation is False + statement_config = StatementConfig(dialect="postgres", enable_caching=True) + + assert statement_config.enable_caching is True + assert statement_config.dialect == "postgres" diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/test_configuration_27.py index ad871368..921c0cad 100644 --- a/docs/examples/usage/test_configuration_27.py +++ b/docs/examples/usage/test_configuration_27.py @@ -1,26 +1,20 @@ -"""Test configuration example: Best practice - Clean up resources.""" +"""Test configuration example: Best practice - Tune pool sizes.""" -import pytest +MIN_POOL_SIZE_CPU = 5 +MAX_POOL_SIZE_CPU = 10 +MIN_IO_BOUND_POOL_SIZE = 20 +MAX_IO_BOUND_POOL_SIZE = 50 -@pytest.mark.asyncio -async def test_cleanup_resources_best_practice() -> None: - """Test resource cleanup best practice.""" - import tempfile +def test_tune_pool_sizes_best_practice() -> None: + """Test pool sizing best practices for different workloads.""" - from sqlspec import SQLSpec - from sqlspec.adapters.aiosqlite import AiosqliteConfig + # CPU-bound workload - smaller pool + cpu_bound_pool_config = {"min_size": MIN_POOL_SIZE_CPU, "max_size": MAX_POOL_SIZE_CPU} + assert cpu_bound_pool_config["min_size"] == MIN_POOL_SIZE_CPU + assert cpu_bound_pool_config["max_size"] == MAX_POOL_SIZE_CPU - with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: - spec = SQLSpec() - db = spec.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) - - # Use the connection - async with spec.provide_session(db) as session: - await session.execute("CREATE TABLE test (id INTEGER)") - - # Clean up resources - important for async adapters - await spec.close_all_pools() - - # Verify pools are closed - assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") + # I/O-bound workload - larger pool + io_bound_pool_config = {"min_size": MIN_IO_BOUND_POOL_SIZE, "max_size": MAX_IO_BOUND_POOL_SIZE} + assert io_bound_pool_config["min_size"] == MIN_IO_BOUND_POOL_SIZE + assert io_bound_pool_config["max_size"] == MAX_IO_BOUND_POOL_SIZE diff --git a/docs/examples/usage/test_configuration_28.py b/docs/examples/usage/test_configuration_28.py new file mode 100644 index 00000000..dce71f58 --- /dev/null +++ b/docs/examples/usage/test_configuration_28.py @@ -0,0 +1,13 @@ +"""Test configuration example: Best practice - Tune pool sizes.""" + +def test_disable_security_checks_best_practice() -> None: + """Test disabling security checks when necessary.""" + + from sqlspec.core.statement import StatementConfig + + # Example: Disabling security checks for trusted internal queries + statement_config = StatementConfig( + dialect="postgres", + enable_validation=False, # Skip security checks + ) + assert statement_config.enable_validation is False diff --git a/docs/examples/usage/test_configuration_29.py b/docs/examples/usage/test_configuration_29.py new file mode 100644 index 00000000..ad871368 --- /dev/null +++ b/docs/examples/usage/test_configuration_29.py @@ -0,0 +1,26 @@ +"""Test configuration example: Best practice - Clean up resources.""" + +import pytest + + +@pytest.mark.asyncio +async def test_cleanup_resources_best_practice() -> None: + """Test resource cleanup best practice.""" + import tempfile + + from sqlspec import SQLSpec + from sqlspec.adapters.aiosqlite import AiosqliteConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + spec = SQLSpec() + db = spec.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) + + # Use the connection + async with spec.provide_session(db) as session: + await session.execute("CREATE TABLE test (id INTEGER)") + + # Clean up resources - important for async adapters + await spec.close_all_pools() + + # Verify pools are closed + assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index 933d1f7b..916bc599 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -292,7 +292,7 @@ Litestar Plugin Configuration .. literalinclude:: /examples/usage/test_configuration_23.py :language: python :caption: `litestar plugin configuration` - :lines: 10-31 + :lines: 2-26 :dedent: 2 Environment-Based Configuration @@ -300,10 +300,10 @@ Environment-Based Configuration Use environment variables for configuration: -.. literalinclude:: /examples/usage/test_configuration_23.py +.. literalinclude:: /examples/usage/test_configuration_24.py :language: python :caption: `environnment-based configuration` - :lines: 49-59 + :lines: 25-41 :dedent: 4 Configuration Best Practices @@ -313,7 +313,7 @@ Configuration Best Practices Always use pooling in production: -.. literalinclude:: /examples/usage/test_configuration_24.py +.. literalinclude:: /examples/usage/test_configuration_25.py :language: python :caption: `connection pooling` :lines: 11-13 @@ -323,7 +323,7 @@ Always use pooling in production: Enable caching to avoid recompiling SQL statements: -.. literalinclude:: /examples/usage/test_configuration_25.py +.. literalinclude:: /examples/usage/test_configuration_26.py :language: python :caption: `enable caching` :lines: 6-8 @@ -333,20 +333,20 @@ Enable caching to avoid recompiling SQL statements: Size pools based on your workload: -.. literalinclude:: /examples/usage/test_configuration_26.py +.. literalinclude:: /examples/usage/test_configuration_27.py :language: python :caption: `tune pool sizes` - :lines: 6-14 + :lines: 3-20 :dedent: 2 **4. Disable Validation in Production** For trusted, performance-critical queries: -.. literalinclude:: /examples/usage/test_configuration_26.py +.. literalinclude:: /examples/usage/test_configuration_28.py :language: python :caption: `no validation` - :lines: 19-25 + :lines: 6-12 :dedent: 2 @@ -354,7 +354,7 @@ For trusted, performance-critical queries: Always close pools on shutdown: -.. literalinclude:: /examples/usage/test_configuration_27.py +.. literalinclude:: /examples/usage/test_configuration_29.py :language: python :caption: `cleanup resources` :lines: 10-27 From 9c7c68917fb2c1e6095b7cba8c42220e99b14d32 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 5 Nov 2025 09:56:40 +0100 Subject: [PATCH 12/18] wrong psygopg note, replaced by correct config --- docs/examples/usage/test_configuration_28.py | 1 + docs/examples/usage/test_configuration_5.py | 24 ++++++++++++++++---- docs/usage/configuration.rst | 9 ++++---- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/docs/examples/usage/test_configuration_28.py b/docs/examples/usage/test_configuration_28.py index dce71f58..88e27089 100644 --- a/docs/examples/usage/test_configuration_28.py +++ b/docs/examples/usage/test_configuration_28.py @@ -1,5 +1,6 @@ """Test configuration example: Best practice - Tune pool sizes.""" + def test_disable_security_checks_best_practice() -> None: """Test disabling security checks when necessary.""" diff --git a/docs/examples/usage/test_configuration_5.py b/docs/examples/usage/test_configuration_5.py index 3f5ac8b7..a2a14b9f 100644 --- a/docs/examples/usage/test_configuration_5.py +++ b/docs/examples/usage/test_configuration_5.py @@ -1,6 +1,20 @@ -import pytest - - -@pytest.mark.skip(reason="PsycopgConfig does not exist in the codebase.") def test_psycopg_config_setup() -> None: - pass + from sqlspec.adapters.psycopg import PsycopgAsyncConfig + + # Async version + config = PsycopgAsyncConfig( + pool_config={ + "conninfo": "postgresql://user:pass@localhost/db", + # Or keyword arguments: + "host": "localhost", + "port": 5432, + "dbname": "mydb", + "user": "myuser", + "password": "mypassword", + # Pool settings + "min_size": 5, + "max_size": 10, + "timeout": 30.0, + } + ) + assert config.pool_config is not None diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index 916bc599..075014f3 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -58,10 +58,11 @@ PostgreSQL Configuration (asyncpg) PostgreSQL Configuration (psycopg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. note:: - - The `PsycopgConfig` class referenced here is for documentation purposes only and may not be present in the codebase. Future releases may include this feature if demand warrants. - +.. literalinclude:: /examples/usage/test_configuration_5.py + :language: python + :caption: `psycopg async configuration` + :lines: 2-19 + :dedent: 2 MySQL Configuration (asyncmy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From f6d748f21af5bd0dc7f152beaf853e88409e5ba5 Mon Sep 17 00:00:00 2001 From: euri10 Date: Fri, 7 Nov 2025 07:23:15 +0100 Subject: [PATCH 13/18] Update docs/examples/usage/test_configuration_12.py Co-authored-by: Cody Fincher <204685+cofin@users.noreply.github.com> --- docs/examples/usage/test_configuration_12.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/usage/test_configuration_12.py b/docs/examples/usage/test_configuration_12.py index a85a9c4b..584dd2c3 100644 --- a/docs/examples/usage/test_configuration_12.py +++ b/docs/examples/usage/test_configuration_12.py @@ -1,6 +1,6 @@ def test_basic_statement_config() -> None: from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.core.statement import StatementConfig + from sqlspec.core import StatementConfig statement_config = StatementConfig( dialect="postgres", # SQLGlot dialect From fffec01df46d18712700455231031d4cdce2e63e Mon Sep 17 00:00:00 2001 From: euri10 Date: Fri, 7 Nov 2025 07:23:24 +0100 Subject: [PATCH 14/18] Update docs/examples/usage/test_configuration_13.py Co-authored-by: Cody Fincher <204685+cofin@users.noreply.github.com> --- docs/examples/usage/test_configuration_13.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/examples/usage/test_configuration_13.py b/docs/examples/usage/test_configuration_13.py index f95775d1..1cd9fd4d 100644 --- a/docs/examples/usage/test_configuration_13.py +++ b/docs/examples/usage/test_configuration_13.py @@ -1,6 +1,5 @@ def test_parameter_style_config() -> None: - from sqlspec.core.parameters import ParameterStyle, ParameterStyleConfig - from sqlspec.core.statement import StatementConfig + from sqlspec.core import ParameterStyle, ParameterStyleConfig, StatementConfig param_config = ParameterStyleConfig( default_parameter_style=ParameterStyle.NUMERIC, # $1, $2, ... From 91c6d0eecbfe9efaa2db5eadfb4d63358e94693c Mon Sep 17 00:00:00 2001 From: euri10 Date: Fri, 7 Nov 2025 07:23:41 +0100 Subject: [PATCH 15/18] Update docs/examples/usage/test_configuration_19.py Co-authored-by: Cody Fincher <204685+cofin@users.noreply.github.com> --- docs/examples/usage/test_configuration_19.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/usage/test_configuration_19.py b/docs/examples/usage/test_configuration_19.py index dc94d0a9..cdd77248 100644 --- a/docs/examples/usage/test_configuration_19.py +++ b/docs/examples/usage/test_configuration_19.py @@ -14,7 +14,7 @@ def test_binding_multiple_configs() -> None: # Add multiple configurations sqlite_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) - spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) + pg_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) # Use specific configuration with spec.provide_session(sqlite_db) as session: From 8d2c24c31eeb253cbbc4843da0d90e39f9453937 Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 10 Nov 2025 03:09:16 +0000 Subject: [PATCH 16/18] Refactor example configurations and tests - Deleted outdated test configuration examples for asyncpg and psycopg. - Added new example configurations for SQLite, asyncpg, psycopg, asyncmy, and DuckDB. - Introduced environment-based configuration examples for better flexibility. - Updated documentation references to point to the new example files. - Enhanced test cases to cover various best practices, including connection pooling, caching, and resource cleanup. - Improved clarity and organization of configuration examples for better usability. --- AGENTS.md | 1 - docs/examples/usage/conftest.py | 27 ++++++ docs/examples/usage/test_configuration_19.py | 24 ----- docs/examples/usage/test_configuration_20.py | 24 ----- docs/examples/usage/test_configuration_23.py | 27 ------ docs/examples/usage/test_configuration_4.py | 17 ---- docs/examples/usage/test_configuration_5.py | 20 ---- docs/examples/usage/test_configuration_9.py | 7 -- ...guration_1.py => usage_configuration_1.py} | 6 +- ...ration_10.py => usage_configuration_10.py} | 5 +- ...ration_11.py => usage_configuration_11.py} | 0 ...ration_12.py => usage_configuration_12.py} | 7 +- ...ration_13.py => usage_configuration_13.py} | 2 +- ...ration_14.py => usage_configuration_14.py} | 2 +- ...ration_15.py => usage_configuration_15.py} | 0 ...ration_16.py => usage_configuration_16.py} | 8 +- ...ration_17.py => usage_configuration_17.py} | 6 +- ...ration_18.py => usage_configuration_18.py} | 0 docs/examples/usage/usage_configuration_19.py | 29 ++++++ ...guration_2.py => usage_configuration_2.py} | 0 docs/examples/usage/usage_configuration_20.py | 31 +++++++ ...ration_21.py => usage_configuration_21.py} | 5 +- ...ration_22.py => usage_configuration_22.py} | 5 +- docs/examples/usage/usage_configuration_23.py | 31 +++++++ ...ration_24.py => usage_configuration_24.py} | 3 - ...ration_25.py => usage_configuration_25.py} | 7 +- ...ration_26.py => usage_configuration_26.py} | 2 +- ...ration_27.py => usage_configuration_27.py} | 0 ...ration_28.py => usage_configuration_28.py} | 2 +- ...ration_29.py => usage_configuration_29.py} | 8 +- ...guration_3.py => usage_configuration_3.py} | 0 docs/examples/usage/usage_configuration_30.py | 17 ++++ docs/examples/usage/usage_configuration_4.py | 25 +++++ docs/examples/usage/usage_configuration_5.py | 29 ++++++ ...guration_6.py => usage_configuration_6.py} | 0 ...guration_7.py => usage_configuration_7.py} | 0 ...guration_8.py => usage_configuration_8.py} | 6 +- docs/examples/usage/usage_configuration_9.py | 12 +++ docs/usage/configuration.rst | 91 +++++++++++-------- 39 files changed, 297 insertions(+), 189 deletions(-) create mode 100644 docs/examples/usage/conftest.py delete mode 100644 docs/examples/usage/test_configuration_19.py delete mode 100644 docs/examples/usage/test_configuration_20.py delete mode 100644 docs/examples/usage/test_configuration_23.py delete mode 100644 docs/examples/usage/test_configuration_4.py delete mode 100644 docs/examples/usage/test_configuration_5.py delete mode 100644 docs/examples/usage/test_configuration_9.py rename docs/examples/usage/{test_configuration_1.py => usage_configuration_1.py} (64%) rename docs/examples/usage/{test_configuration_10.py => usage_configuration_10.py} (57%) rename docs/examples/usage/{test_configuration_11.py => usage_configuration_11.py} (100%) rename docs/examples/usage/{test_configuration_12.py => usage_configuration_12.py} (72%) rename docs/examples/usage/{test_configuration_13.py => usage_configuration_13.py} (87%) rename docs/examples/usage/{test_configuration_14.py => usage_configuration_14.py} (90%) rename docs/examples/usage/{test_configuration_15.py => usage_configuration_15.py} (100%) rename docs/examples/usage/{test_configuration_16.py => usage_configuration_16.py} (69%) rename docs/examples/usage/{test_configuration_17.py => usage_configuration_17.py} (81%) rename docs/examples/usage/{test_configuration_18.py => usage_configuration_18.py} (100%) create mode 100644 docs/examples/usage/usage_configuration_19.py rename docs/examples/usage/{test_configuration_2.py => usage_configuration_2.py} (100%) create mode 100644 docs/examples/usage/usage_configuration_20.py rename docs/examples/usage/{test_configuration_21.py => usage_configuration_21.py} (88%) rename docs/examples/usage/{test_configuration_22.py => usage_configuration_22.py} (84%) create mode 100644 docs/examples/usage/usage_configuration_23.py rename docs/examples/usage/{test_configuration_24.py => usage_configuration_24.py} (91%) rename docs/examples/usage/{test_configuration_25.py => usage_configuration_25.py} (75%) rename docs/examples/usage/{test_configuration_26.py => usage_configuration_26.py} (86%) rename docs/examples/usage/{test_configuration_27.py => usage_configuration_27.py} (100%) rename docs/examples/usage/{test_configuration_28.py => usage_configuration_28.py} (88%) rename docs/examples/usage/{test_configuration_29.py => usage_configuration_29.py} (75%) rename docs/examples/usage/{test_configuration_3.py => usage_configuration_3.py} (100%) create mode 100644 docs/examples/usage/usage_configuration_30.py create mode 100644 docs/examples/usage/usage_configuration_4.py create mode 100644 docs/examples/usage/usage_configuration_5.py rename docs/examples/usage/{test_configuration_6.py => usage_configuration_6.py} (100%) rename docs/examples/usage/{test_configuration_7.py => usage_configuration_7.py} (100%) rename docs/examples/usage/{test_configuration_8.py => usage_configuration_8.py} (77%) create mode 100644 docs/examples/usage/usage_configuration_9.py diff --git a/AGENTS.md b/AGENTS.md index 33baea25..1134fa17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -524,7 +524,6 @@ This summary documents the small documentation and example maintenance performed on the configuration usage guide and can be expanded into a longer changelog entry if desired. - ```python def parse_user_input(content: str, source: str) -> "dict[str, Result]": """Parse user input with two-tier error handling. diff --git a/docs/examples/usage/conftest.py b/docs/examples/usage/conftest.py new file mode 100644 index 00000000..4f247707 --- /dev/null +++ b/docs/examples/usage/conftest.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from collections.abc import Generator + +import pytest +from pytest_databases.docker.postgres import PostgresService + +pytest_plugins = ["pytest_databases.docker.postgres"] + + +@pytest.fixture(scope="session", autouse=True) +def usage_postgres_env(postgres_service: PostgresService) -> Generator[None, None, None]: + """Expose Postgres connection settings via env vars for docs examples.""" + + patcher = pytest.MonkeyPatch() + dsn = ( + f"postgresql://{postgres_service.user}:{postgres_service.password}" + f"@{postgres_service.host}:{postgres_service.port}/{postgres_service.database}" + ) + patcher.setenv("SQLSPEC_USAGE_PG_DSN", dsn) + patcher.setenv("SQLSPEC_USAGE_PG_HOST", postgres_service.host) + patcher.setenv("SQLSPEC_USAGE_PG_PORT", str(postgres_service.port)) + patcher.setenv("SQLSPEC_USAGE_PG_USER", postgres_service.user) + patcher.setenv("SQLSPEC_USAGE_PG_PASSWORD", postgres_service.password) + patcher.setenv("SQLSPEC_USAGE_PG_DATABASE", postgres_service.database) + yield + patcher.undo() diff --git a/docs/examples/usage/test_configuration_19.py b/docs/examples/usage/test_configuration_19.py deleted file mode 100644 index cdd77248..00000000 --- a/docs/examples/usage/test_configuration_19.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Test configuration example: Binding multiple database configurations.""" - - -def test_binding_multiple_configs() -> None: - """Test binding multiple database configurations.""" - import tempfile - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.adapters.sqlite import SqliteConfig - - with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: - spec = SQLSpec() - - # Add multiple configurations - sqlite_db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) - pg_db = spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."})) - - # Use specific configuration - with spec.provide_session(sqlite_db) as session: - session.execute("SELECT 1") - - assert spec.configs[SqliteConfig].pool_config["database"] == tmp.name - assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://..." diff --git a/docs/examples/usage/test_configuration_20.py b/docs/examples/usage/test_configuration_20.py deleted file mode 100644 index 47763e08..00000000 --- a/docs/examples/usage/test_configuration_20.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Test configuration example: Named database bindings.""" - - -def test_named_bindings() -> None: - """Test named database bindings.""" - import tempfile - - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.adapters.sqlite import SqliteConfig - - with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: - spec = SQLSpec() - - # Add with bind keys - spec.add_config(SqliteConfig(pool_config={"database": tmp.name}, bind_key="cache_db")) - spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://..."}, bind_key="main_db")) - - # Access by bind key - with spec.provide_session("cache_db") as session: - session.execute("SELECT 1") - - assert spec.configs[SqliteConfig].pool_config["database"] == tmp.name - assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://..." diff --git a/docs/examples/usage/test_configuration_23.py b/docs/examples/usage/test_configuration_23.py deleted file mode 100644 index 0333748c..00000000 --- a/docs/examples/usage/test_configuration_23.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Test configuration example: Environment-based configuration.""" - - -def test_extension_config() -> None: - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, - extension_config={ - "litestar": { - "connection_key": "db_connection", - "session_key": "db_session", - "pool_key": "db_pool", - "commit_mode": "autocommit", - "enable_correlation_middleware": True, - } - }, - ) - assert config.extension_config == { - "litestar": { - "connection_key": "db_connection", - "session_key": "db_session", - "pool_key": "db_pool", - "commit_mode": "autocommit", - "enable_correlation_middleware": True, - } - } diff --git a/docs/examples/usage/test_configuration_4.py b/docs/examples/usage/test_configuration_4.py deleted file mode 100644 index a521d5d3..00000000 --- a/docs/examples/usage/test_configuration_4.py +++ /dev/null @@ -1,17 +0,0 @@ -def test_asyncpg_config_setup() -> None: - from sqlspec.adapters.asyncpg import AsyncpgConfig - - config = AsyncpgConfig( - pool_config={ - "dsn": "postgresql://user:pass@localhost:5432/dbname", - # Other parameters - "host": "localhost", - "port": 5432, - "user": "myuser", - "password": "mypassword", - "database": "mydb", - "min_size": 10, - "max_size": 20, - } - ) - assert config.pool_config["host"] == "localhost" diff --git a/docs/examples/usage/test_configuration_5.py b/docs/examples/usage/test_configuration_5.py deleted file mode 100644 index a2a14b9f..00000000 --- a/docs/examples/usage/test_configuration_5.py +++ /dev/null @@ -1,20 +0,0 @@ -def test_psycopg_config_setup() -> None: - from sqlspec.adapters.psycopg import PsycopgAsyncConfig - - # Async version - config = PsycopgAsyncConfig( - pool_config={ - "conninfo": "postgresql://user:pass@localhost/db", - # Or keyword arguments: - "host": "localhost", - "port": 5432, - "dbname": "mydb", - "user": "myuser", - "password": "mypassword", - # Pool settings - "min_size": 5, - "max_size": 10, - "timeout": 30.0, - } - ) - assert config.pool_config is not None diff --git a/docs/examples/usage/test_configuration_9.py b/docs/examples/usage/test_configuration_9.py deleted file mode 100644 index 6133ab2b..00000000 --- a/docs/examples/usage/test_configuration_9.py +++ /dev/null @@ -1,7 +0,0 @@ -def test_pool_lifecycle() -> None: - from sqlspec import SQLSpec - from sqlspec.adapters.asyncpg import AsyncpgConfig - - spec = SQLSpec() - spec.add_config(AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"})) - assert spec.configs[AsyncpgConfig].pool_config["dsn"] == "postgresql://localhost/db" diff --git a/docs/examples/usage/test_configuration_1.py b/docs/examples/usage/usage_configuration_1.py similarity index 64% rename from docs/examples/usage/test_configuration_1.py rename to docs/examples/usage/usage_configuration_1.py index 1e64acfd..dbe871f5 100644 --- a/docs/examples/usage/test_configuration_1.py +++ b/docs/examples/usage/usage_configuration_1.py @@ -3,12 +3,12 @@ def test_sqlite_memory_db() -> None: from sqlspec.adapters.sqlite import SqliteConfig # Create SQLSpec instance - spec = SQLSpec() + db_manager = SQLSpec() # Add database configuration - db = spec.add_config(SqliteConfig(pool_config={"database": ":memory:"})) + db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) # Use the database - with spec.provide_session(db) as session: + with db_manager.provide_session(db) as session: result = session.execute("SELECT 1") assert result[0] == {"1": 1} diff --git a/docs/examples/usage/test_configuration_10.py b/docs/examples/usage/usage_configuration_10.py similarity index 57% rename from docs/examples/usage/test_configuration_10.py rename to docs/examples/usage/usage_configuration_10.py index e012888d..0e78544a 100644 --- a/docs/examples/usage/test_configuration_10.py +++ b/docs/examples/usage/usage_configuration_10.py @@ -2,8 +2,11 @@ def test_manual_pool() -> None: + import os + from sqlspec.adapters.asyncpg import AsyncpgConfig - pool = {"dsn": "postgresql://localhost/db", "min_size": 10, "max_size": POOL_INSTANCE} + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + pool = {"dsn": dsn, "min_size": 10, "max_size": POOL_INSTANCE} db = AsyncpgConfig(pool_instance=pool) assert db.pool_instance["max_size"] == POOL_INSTANCE diff --git a/docs/examples/usage/test_configuration_11.py b/docs/examples/usage/usage_configuration_11.py similarity index 100% rename from docs/examples/usage/test_configuration_11.py rename to docs/examples/usage/usage_configuration_11.py diff --git a/docs/examples/usage/test_configuration_12.py b/docs/examples/usage/usage_configuration_12.py similarity index 72% rename from docs/examples/usage/test_configuration_12.py rename to docs/examples/usage/usage_configuration_12.py index 584dd2c3..42ea8a86 100644 --- a/docs/examples/usage/test_configuration_12.py +++ b/docs/examples/usage/usage_configuration_12.py @@ -1,6 +1,8 @@ def test_basic_statement_config() -> None: + import os + + from sqlspec import StatementConfig from sqlspec.adapters.asyncpg import AsyncpgConfig - from sqlspec.core import StatementConfig statement_config = StatementConfig( dialect="postgres", # SQLGlot dialect @@ -11,6 +13,7 @@ def test_basic_statement_config() -> None: ) # Apply to adapter - config = AsyncpgConfig(pool_config={"dsn": "postgresql://localhost/db"}, statement_config=statement_config) + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + config = AsyncpgConfig(pool_config={"dsn": dsn}, statement_config=statement_config) assert config.statement_config.dialect == "postgres" assert config.statement_config.enable_parsing is True diff --git a/docs/examples/usage/test_configuration_13.py b/docs/examples/usage/usage_configuration_13.py similarity index 87% rename from docs/examples/usage/test_configuration_13.py rename to docs/examples/usage/usage_configuration_13.py index 1cd9fd4d..4c5e8eb9 100644 --- a/docs/examples/usage/test_configuration_13.py +++ b/docs/examples/usage/usage_configuration_13.py @@ -1,5 +1,5 @@ def test_parameter_style_config() -> None: - from sqlspec.core import ParameterStyle, ParameterStyleConfig, StatementConfig + from sqlspec import ParameterStyle, ParameterStyleConfig, StatementConfig param_config = ParameterStyleConfig( default_parameter_style=ParameterStyle.NUMERIC, # $1, $2, ... diff --git a/docs/examples/usage/test_configuration_14.py b/docs/examples/usage/usage_configuration_14.py similarity index 90% rename from docs/examples/usage/test_configuration_14.py rename to docs/examples/usage/usage_configuration_14.py index 8b30ec37..0bfda1f2 100644 --- a/docs/examples/usage/test_configuration_14.py +++ b/docs/examples/usage/usage_configuration_14.py @@ -1,5 +1,5 @@ def test_parameter_styles() -> None: - from sqlspec.core.parameters import ParameterStyle + from sqlspec import ParameterStyle # Question mark (SQLite, DuckDB) qmark = ParameterStyle.QMARK # WHERE id = ? diff --git a/docs/examples/usage/test_configuration_15.py b/docs/examples/usage/usage_configuration_15.py similarity index 100% rename from docs/examples/usage/test_configuration_15.py rename to docs/examples/usage/usage_configuration_15.py diff --git a/docs/examples/usage/test_configuration_16.py b/docs/examples/usage/usage_configuration_16.py similarity index 69% rename from docs/examples/usage/test_configuration_16.py rename to docs/examples/usage/usage_configuration_16.py index 4245e8e9..f37009eb 100644 --- a/docs/examples/usage/test_configuration_16.py +++ b/docs/examples/usage/usage_configuration_16.py @@ -11,13 +11,13 @@ def test_per_instance_cache_config() -> None: with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: # Configure cache for specific SQLSpec instance - spec = SQLSpec() - spec.update_cache_config(CacheConfig(sql_cache_enabled=True, sql_cache_size=500)) + db_manager = SQLSpec() + db_manager.update_cache_config(CacheConfig(sql_cache_enabled=True, sql_cache_size=500)) # Add database config - db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) + db = db_manager.add_config(SqliteConfig(pool_config={"database": tmp.name})) # Use the configured spec - with spec.provide_session(db) as session: + with db_manager.provide_session(db) as session: result = session.execute("SELECT 1") assert result is not None diff --git a/docs/examples/usage/test_configuration_17.py b/docs/examples/usage/usage_configuration_17.py similarity index 81% rename from docs/examples/usage/test_configuration_17.py rename to docs/examples/usage/usage_configuration_17.py index 4a3ac587..c3a988c4 100644 --- a/docs/examples/usage/test_configuration_17.py +++ b/docs/examples/usage/usage_configuration_17.py @@ -10,11 +10,11 @@ def test_cache_statistics() -> None: from sqlspec.core.cache import get_cache_statistics, log_cache_stats with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: - spec = SQLSpec() - db = spec.add_config(SqliteConfig(pool_config={"database": tmp.name})) + db_manager = SQLSpec() + db = db_manager.add_config(SqliteConfig(pool_config={"database": tmp.name})) # Execute some queries to generate cache activity - with spec.provide_session(db) as session: + with db_manager.provide_session(db) as session: session.execute("SELECT 1") session.execute("SELECT 1") # Should hit cache diff --git a/docs/examples/usage/test_configuration_18.py b/docs/examples/usage/usage_configuration_18.py similarity index 100% rename from docs/examples/usage/test_configuration_18.py rename to docs/examples/usage/usage_configuration_18.py diff --git a/docs/examples/usage/usage_configuration_19.py b/docs/examples/usage/usage_configuration_19.py new file mode 100644 index 00000000..6020d20c --- /dev/null +++ b/docs/examples/usage/usage_configuration_19.py @@ -0,0 +1,29 @@ +"""Test configuration example: Binding multiple database configurations.""" + + +def test_binding_multiple_configs() -> None: + """Test binding multiple database configurations.""" + import os + import tempfile + + from sqlspec import SQLSpec + from sqlspec.adapters.asyncpg import AsyncpgConfig + from sqlspec.adapters.sqlite import SqliteConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + db_manager = SQLSpec() + + # Add multiple configurations + sqlite_key = db_manager.add_config(SqliteConfig(pool_config={"database": tmp.name})) + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + asyncpg_key = db_manager.add_config(AsyncpgConfig(pool_config={"dsn": dsn})) + + # Use specific configuration + with db_manager.provide_session(sqlite_key) as session: + session.execute("SELECT 1") + + sqlite_config = db_manager.get_config(sqlite_key) + pg_config = db_manager.get_config(asyncpg_key) + + assert sqlite_config.pool_config["database"] == tmp.name + assert pg_config.pool_config["dsn"] == os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") diff --git a/docs/examples/usage/test_configuration_2.py b/docs/examples/usage/usage_configuration_2.py similarity index 100% rename from docs/examples/usage/test_configuration_2.py rename to docs/examples/usage/usage_configuration_2.py diff --git a/docs/examples/usage/usage_configuration_20.py b/docs/examples/usage/usage_configuration_20.py new file mode 100644 index 00000000..06a26634 --- /dev/null +++ b/docs/examples/usage/usage_configuration_20.py @@ -0,0 +1,31 @@ +"""Test configuration example: Named database bindings.""" + + +def test_named_bindings() -> None: + """Test named database bindings.""" + import os + import tempfile + + from sqlspec import SQLSpec + from sqlspec.adapters.asyncpg import AsyncpgConfig + from sqlspec.adapters.sqlite import SqliteConfig + + with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: + db_manager = SQLSpec() + + # Add with bind keys + cache_key = db_manager.add_config( + SqliteConfig(pool_config={"database": tmp.name}, bind_key="cache_db") + ) + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + main_key = db_manager.add_config(AsyncpgConfig(pool_config={"dsn": dsn}, bind_key="main_db")) + + # Access by bind key + with db_manager.provide_session(cache_key) as session: + session.execute("SELECT 1") + + cache_config = db_manager.get_config(cache_key) + main_config = db_manager.get_config(main_key) + + assert cache_config.bind_key == "cache_db" + assert main_config.bind_key == "main_db" diff --git a/docs/examples/usage/test_configuration_21.py b/docs/examples/usage/usage_configuration_21.py similarity index 88% rename from docs/examples/usage/test_configuration_21.py rename to docs/examples/usage/usage_configuration_21.py index 7c53e1a8..183a9b5f 100644 --- a/docs/examples/usage/test_configuration_21.py +++ b/docs/examples/usage/usage_configuration_21.py @@ -8,10 +8,13 @@ ) def test_basic_migration_config() -> None: """Test basic migration configuration.""" + import os + from sqlspec.adapters.asyncpg import AsyncpgConfig + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, + pool_config={"dsn": dsn}, extension_config={ "litestar": {"session_table": "custom_sessions"} # Extension settings }, diff --git a/docs/examples/usage/test_configuration_22.py b/docs/examples/usage/usage_configuration_22.py similarity index 84% rename from docs/examples/usage/test_configuration_22.py rename to docs/examples/usage/usage_configuration_22.py index 93ef2185..d6986916 100644 --- a/docs/examples/usage/test_configuration_22.py +++ b/docs/examples/usage/usage_configuration_22.py @@ -1,8 +1,11 @@ def test_basic_migration_config() -> None: + import os + from sqlspec.adapters.asyncpg import AsyncpgConfig + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db"}, + pool_config={"dsn": dsn}, extension_config={ "litestar": {"session_table": "custom_sessions"} # Extension settings }, diff --git a/docs/examples/usage/usage_configuration_23.py b/docs/examples/usage/usage_configuration_23.py new file mode 100644 index 00000000..a04f57c3 --- /dev/null +++ b/docs/examples/usage/usage_configuration_23.py @@ -0,0 +1,31 @@ +"""Test configuration example: Environment-based configuration.""" + + +def test_extension_config() -> None: + import os + + from sqlspec.adapters.asyncpg import AsyncpgConfig + + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + config = AsyncpgConfig( + pool_config={"dsn": dsn}, + extension_config={ + "litestar": { + "connection_key": "db_connection", + "session_key": "db_session", + "pool_key": "db_pool", + "commit_mode": "autocommit_include_redirect", + "extra_commit_statuses": {201}, + "extra_rollback_statuses": {422}, + "enable_correlation_middleware": True, + "correlation_header": "x-request-id", + "correlation_headers": ["traceparent", "x-correlation-id"], + "auto_trace_headers": False, + "disable_di": False, + } + }, + ) + + assert config.extension_config["litestar"]["commit_mode"] == "autocommit_include_redirect" + assert config.extension_config["litestar"]["extra_commit_statuses"] == {201} + assert config.extension_config["litestar"]["correlation_header"] == "x-request-id" diff --git a/docs/examples/usage/test_configuration_24.py b/docs/examples/usage/usage_configuration_24.py similarity index 91% rename from docs/examples/usage/test_configuration_24.py rename to docs/examples/usage/usage_configuration_24.py index f38b551e..bcad9ddf 100644 --- a/docs/examples/usage/test_configuration_24.py +++ b/docs/examples/usage/usage_configuration_24.py @@ -3,12 +3,9 @@ import os from unittest.mock import patch -import pytest - POSTGRES_PORT = 5433 -@pytest.mark.skipif(os.getenv("TEST_ASYNCPG", "0") != "1", reason="AsyncPG integration tests disabled") def test_environment_based_configuration() -> None: """Test environment-based configuration pattern.""" diff --git a/docs/examples/usage/test_configuration_25.py b/docs/examples/usage/usage_configuration_25.py similarity index 75% rename from docs/examples/usage/test_configuration_25.py rename to docs/examples/usage/usage_configuration_25.py index 1e012638..c4e2110a 100644 --- a/docs/examples/usage/test_configuration_25.py +++ b/docs/examples/usage/usage_configuration_25.py @@ -11,11 +11,12 @@ ) def test_connection_pooling_best_practice() -> None: """Test connection pooling best practice configuration.""" + import os + from sqlspec.adapters.asyncpg import AsyncpgConfig - config = AsyncpgConfig( - pool_config={"dsn": "postgresql://localhost/db", "min_size": MIN_POOL_SIZE, "max_size": MAX_POOL_SIZE} - ) + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + config = AsyncpgConfig(pool_config={"dsn": dsn, "min_size": MIN_POOL_SIZE, "max_size": MAX_POOL_SIZE}) assert config.pool_config["min_size"] == MIN_POOL_SIZE assert config.pool_config["max_size"] == MAX_POOL_SIZE diff --git a/docs/examples/usage/test_configuration_26.py b/docs/examples/usage/usage_configuration_26.py similarity index 86% rename from docs/examples/usage/test_configuration_26.py rename to docs/examples/usage/usage_configuration_26.py index bab5d292..ad19e70c 100644 --- a/docs/examples/usage/test_configuration_26.py +++ b/docs/examples/usage/usage_configuration_26.py @@ -3,7 +3,7 @@ def test_enable_caching_best_practice() -> None: """Test caching best practice configuration.""" - from sqlspec.core.statement import StatementConfig + from sqlspec import StatementConfig statement_config = StatementConfig(dialect="postgres", enable_caching=True) diff --git a/docs/examples/usage/test_configuration_27.py b/docs/examples/usage/usage_configuration_27.py similarity index 100% rename from docs/examples/usage/test_configuration_27.py rename to docs/examples/usage/usage_configuration_27.py diff --git a/docs/examples/usage/test_configuration_28.py b/docs/examples/usage/usage_configuration_28.py similarity index 88% rename from docs/examples/usage/test_configuration_28.py rename to docs/examples/usage/usage_configuration_28.py index 88e27089..cc0ece0a 100644 --- a/docs/examples/usage/test_configuration_28.py +++ b/docs/examples/usage/usage_configuration_28.py @@ -4,7 +4,7 @@ def test_disable_security_checks_best_practice() -> None: """Test disabling security checks when necessary.""" - from sqlspec.core.statement import StatementConfig + from sqlspec import StatementConfig # Example: Disabling security checks for trusted internal queries statement_config = StatementConfig( diff --git a/docs/examples/usage/test_configuration_29.py b/docs/examples/usage/usage_configuration_29.py similarity index 75% rename from docs/examples/usage/test_configuration_29.py rename to docs/examples/usage/usage_configuration_29.py index ad871368..120c81a7 100644 --- a/docs/examples/usage/test_configuration_29.py +++ b/docs/examples/usage/usage_configuration_29.py @@ -12,15 +12,15 @@ async def test_cleanup_resources_best_practice() -> None: from sqlspec.adapters.aiosqlite import AiosqliteConfig with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: - spec = SQLSpec() - db = spec.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) + db_manager = SQLSpec() + db = db_manager.add_config(AiosqliteConfig(pool_config={"database": tmp.name})) # Use the connection - async with spec.provide_session(db) as session: + async with db_manager.provide_session(db) as session: await session.execute("CREATE TABLE test (id INTEGER)") # Clean up resources - important for async adapters - await spec.close_all_pools() + await db_manager.close_all_pools() # Verify pools are closed assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") diff --git a/docs/examples/usage/test_configuration_3.py b/docs/examples/usage/usage_configuration_3.py similarity index 100% rename from docs/examples/usage/test_configuration_3.py rename to docs/examples/usage/usage_configuration_3.py diff --git a/docs/examples/usage/usage_configuration_30.py b/docs/examples/usage/usage_configuration_30.py new file mode 100644 index 00000000..9b653400 --- /dev/null +++ b/docs/examples/usage/usage_configuration_30.py @@ -0,0 +1,17 @@ +"""Telemetry snapshot example.""" + + +def test_telemetry_snapshot() -> None: + """Demonstrate SQLSpec.telemetry_snapshot().""" + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + + db_manager = SQLSpec() + db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) + + with db_manager.provide_session(db) as session: + session.execute("SELECT 1") + + snapshot = db_manager.telemetry_snapshot() + assert "SqliteConfig.lifecycle.query_start" in snapshot + _ = snapshot.get("storage_bridge.bytes_written", 0) diff --git a/docs/examples/usage/usage_configuration_4.py b/docs/examples/usage/usage_configuration_4.py new file mode 100644 index 00000000..a8de0ded --- /dev/null +++ b/docs/examples/usage/usage_configuration_4.py @@ -0,0 +1,25 @@ +def test_asyncpg_config_setup() -> None: + import os + + from sqlspec.adapters.asyncpg import AsyncpgConfig + + host = os.getenv("SQLSPEC_USAGE_PG_HOST", "localhost") + port = int(os.getenv("SQLSPEC_USAGE_PG_PORT", "5432")) + user = os.getenv("SQLSPEC_USAGE_PG_USER", "user") + password = os.getenv("SQLSPEC_USAGE_PG_PASSWORD", "password") + database = os.getenv("SQLSPEC_USAGE_PG_DATABASE", "db" ) + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", f"postgresql://{user}:{password}@{host}:{port}/{database}") + + config = AsyncpgConfig( + pool_config={ + "dsn": dsn, + "min_size": 10, + "max_size": 20, + "host": host, + "port": port, + "user": user, + "password": password, + "database": database, + } + ) + assert config.pool_config["host"] == host diff --git a/docs/examples/usage/usage_configuration_5.py b/docs/examples/usage/usage_configuration_5.py new file mode 100644 index 00000000..9cd3377e --- /dev/null +++ b/docs/examples/usage/usage_configuration_5.py @@ -0,0 +1,29 @@ +def test_psycopg_config_setup() -> None: + import os + + from sqlspec.adapters.psycopg import PsycopgAsyncConfig + + host = os.getenv("SQLSPEC_USAGE_PG_HOST", "localhost") + port = int(os.getenv("SQLSPEC_USAGE_PG_PORT", "5432")) + database = os.getenv("SQLSPEC_USAGE_PG_DATABASE", "db") + user = os.getenv("SQLSPEC_USAGE_PG_USER", "user") + password = os.getenv("SQLSPEC_USAGE_PG_PASSWORD", "password") + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", f"postgresql://{user}:{password}@{host}:{port}/{database}") + + # Async version + config = PsycopgAsyncConfig( + pool_config={ + "conninfo": dsn, + # Or keyword arguments: + "host": host, + "port": port, + "dbname": database, + "user": user, + "password": password, + # Pool settings + "min_size": 5, + "max_size": 10, + "timeout": 30.0, + } + ) + assert config.pool_config is not None diff --git a/docs/examples/usage/test_configuration_6.py b/docs/examples/usage/usage_configuration_6.py similarity index 100% rename from docs/examples/usage/test_configuration_6.py rename to docs/examples/usage/usage_configuration_6.py diff --git a/docs/examples/usage/test_configuration_7.py b/docs/examples/usage/usage_configuration_7.py similarity index 100% rename from docs/examples/usage/test_configuration_7.py rename to docs/examples/usage/usage_configuration_7.py diff --git a/docs/examples/usage/test_configuration_8.py b/docs/examples/usage/usage_configuration_8.py similarity index 77% rename from docs/examples/usage/test_configuration_8.py rename to docs/examples/usage/usage_configuration_8.py index 6cd0a16d..acf7624a 100644 --- a/docs/examples/usage/test_configuration_8.py +++ b/docs/examples/usage/usage_configuration_8.py @@ -2,11 +2,15 @@ def test_asyncpg_pool_setup() -> None: + import os + from sqlspec.adapters.asyncpg import AsyncpgConfig + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + config = AsyncpgConfig( pool_config={ - "dsn": "postgresql://localhost/db", + "dsn": dsn, "min_size": 10, "max_size": 20, "max_queries": 50000, diff --git a/docs/examples/usage/usage_configuration_9.py b/docs/examples/usage/usage_configuration_9.py new file mode 100644 index 00000000..0e254495 --- /dev/null +++ b/docs/examples/usage/usage_configuration_9.py @@ -0,0 +1,12 @@ +def test_pool_lifecycle() -> None: + import os + + from sqlspec import SQLSpec + from sqlspec.adapters.asyncpg import AsyncpgConfig + + db_manager = SQLSpec() + dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + asyncpg_key = db_manager.add_config(AsyncpgConfig(pool_config={"dsn": dsn})) + + asyncpg_config = db_manager.get_config(asyncpg_key) + assert asyncpg_config.pool_config["dsn"] == dsn diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index 45685f62..dfb81ed0 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -18,7 +18,7 @@ Basic Configuration The simplest way to use SQLSpec is with default configuration: -.. literalinclude:: /examples/usage/test_configuration_1.py +.. literalinclude:: /examples/usage/usage_configuration_1.py :language: python :caption: `basic configuration` :lines: 2-14 @@ -29,10 +29,17 @@ Database Configurations Each database adapter has its own configuration class with adapter-specific settings. +.. note:: + + Async PostgreSQL examples in this guide read their connection details from + ``SQLSPEC_USAGE_PG_*`` environment variables. The test suite populates these + variables (host, port, user, password, database, DSN) automatically so the + literalincluded snippets can stay identical to the documentation. + SQLite Configuration ^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_2.py +.. literalinclude:: /examples/usage/usage_configuration_2.py :language: python :caption: `sqlite configuration` :lines: 2-11 @@ -40,7 +47,7 @@ SQLite Configuration **Memory Databases** -.. literalinclude:: /examples/usage/test_configuration_3.py +.. literalinclude:: /examples/usage/usage_configuration_3.py :language: python :caption: `memory sqlite configuration` :lines: 2-13 @@ -49,7 +56,7 @@ SQLite Configuration PostgreSQL Configuration (asyncpg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_4.py +.. literalinclude:: /examples/usage/usage_configuration_4.py :language: python :caption: `postgres asyncpg configuration` :lines: 2-16 @@ -58,7 +65,7 @@ PostgreSQL Configuration (asyncpg) PostgreSQL Configuration (psycopg) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_5.py +.. literalinclude:: /examples/usage/usage_configuration_5.py :language: python :caption: `psycopg async configuration` :lines: 2-19 @@ -67,7 +74,7 @@ PostgreSQL Configuration (psycopg) MySQL Configuration (asyncmy) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_6.py +.. literalinclude:: /examples/usage/usage_configuration_6.py :language: python :caption: `mysql asyncmy configuration` :lines: 2-15 @@ -76,7 +83,7 @@ MySQL Configuration (asyncmy) DuckDB Configuration ^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_7.py +.. literalinclude:: /examples/usage/usage_configuration_7.py :language: python :caption: `duckdb configuration` :lines: 2-11 @@ -90,7 +97,7 @@ Connection pooling improves performance by reusing database connections. SQLSpec Pool Configuration ^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_8.py +.. literalinclude:: /examples/usage/usage_configuration_8.py :language: python :caption: `pool configuration` :lines: 2-11 @@ -98,7 +105,7 @@ Pool Configuration **Pool Lifecycle Management** -.. literalinclude:: /examples/usage/test_configuration_9.py +.. literalinclude:: /examples/usage/usage_configuration_9.py :language: python :caption: `pool lifecycle management` :lines: 2-7 @@ -107,7 +114,7 @@ Pool Configuration Using Pre-Created Pools ^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_10.py +.. literalinclude:: /examples/usage/usage_configuration_10.py :language: python :caption: `using pre-created pools` :lines: 2-9 @@ -116,7 +123,7 @@ Using Pre-Created Pools No-Pooling Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_11.py +.. literalinclude:: /examples/usage/usage_configuration_11.py :language: python :caption: `no-pooling configuration` :lines: 2-4 @@ -130,7 +137,7 @@ Statement configuration controls SQL processing pipeline behavior. Basic Statement Config ^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_12.py +.. literalinclude:: /examples/usage/usage_configuration_12.py :language: python :caption: `basic statement config` :lines: 2-14 @@ -139,7 +146,7 @@ Basic Statement Config Parameter Style Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_13.py +.. literalinclude:: /examples/usage/usage_configuration_13.py :language: python :caption: `parameter style configuration` :lines: 2-17 @@ -149,7 +156,7 @@ Parameter Style Configuration SQLSpec supports multiple parameter placeholder styles: -.. literalinclude:: /examples/usage/test_configuration_14.py +.. literalinclude:: /examples/usage/usage_configuration_14.py :language: python :caption: `parameter styles` :lines: 2-21 @@ -162,7 +169,7 @@ Configure security and performance validation. Disable validation for performance-critical paths where input is trusted: -.. literalinclude:: /examples/usage/test_configuration_15.py +.. literalinclude:: /examples/usage/usage_configuration_15.py :language: python :caption: `validation configuration` :lines: 5-17 @@ -176,7 +183,7 @@ SQLSpec uses multi-tier caching to avoid recompiling SQL statements. Global Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_16.py +.. literalinclude:: /examples/usage/usage_configuration_16.py :language: python :caption: `global cache configuration` :lines: 6-28 @@ -185,7 +192,7 @@ Global Cache Configuration Per-Instance Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_17.py +.. literalinclude:: /examples/usage/usage_configuration_17.py :language: python :caption: `per-instance cache configuration` :lines: 6-27 @@ -196,7 +203,7 @@ Cache Statistics Monitor cache statistics: -.. literalinclude:: /examples/usage/test_configuration_18.py +.. literalinclude:: /examples/usage/usage_configuration_18.py :language: python :caption: `cache statistics` :lines: 6-18 @@ -205,7 +212,7 @@ Monitor cache statistics: Clear Cache ^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_19.py +.. literalinclude:: /examples/usage/usage_configuration_19.py :language: python :caption: `clear cache` :lines: 6-24 @@ -219,10 +226,10 @@ SQLSpec supports multiple database configurations in a single application. Binding Multiple Configs ^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_20.py +.. literalinclude:: /examples/usage/usage_configuration_19.py :language: python :caption: `binding multiple configurations` - :lines: 6-24 + :lines: 4-27 :dedent: 2 Named Bindings @@ -230,12 +237,19 @@ Named Bindings Use bind keys for clearer configuration management: -.. literalinclude:: /examples/usage/test_configuration_21.py +.. literalinclude:: /examples/usage/usage_configuration_20.py :language: python :caption: `named bindings` - :lines: 11-26 + :lines: 6-25 :dedent: 2 +``SQLSpec.add_config()`` returns a key for the registered configuration. +Store that value (as shown above) and reuse it when calling +``provide_session()`` or ``get_config()``. The config registry holds a single +instance per config type, so creating multiple variants of the same adapter +requires defining lightweight subclasses or binding unique config classes for +each database. + Migration Configuration ----------------------- @@ -244,7 +258,7 @@ SQLSpec includes a migration system for schema management. Basic Migration Config ^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_22.py +.. literalinclude:: /examples/usage/usage_configuration_22.py :language: python :caption: `basic migration config` :lines: 2-15 @@ -290,10 +304,10 @@ Framework integrations can be configured via ``extension_config``. Litestar Plugin Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/test_configuration_23.py +.. literalinclude:: /examples/usage/usage_configuration_23.py :language: python :caption: `litestar plugin configuration` - :lines: 2-26 + :lines: 4-28 :dedent: 2 Telemetry Snapshot @@ -301,21 +315,20 @@ Telemetry Snapshot Call ``SQLSpec.telemetry_snapshot()`` to inspect lifecycle counters, serializer metrics, and recent storage jobs: -.. code-block:: python - - snapshot = spec.telemetry_snapshot() - print(snapshot["storage_bridge.bytes_written"]) - for job in snapshot.get("storage_bridge.recent_jobs", []): - print(job["destination"], job.get("correlation_id")) +.. literalinclude:: /examples/usage/usage_configuration_30.py + :language: python + :caption: `telemetry snapshot` + :lines: 1-15 + :dedent: 0 Environment-Based Configuration ------------------------------- Use environment variables for configuration: -.. literalinclude:: /examples/usage/test_configuration_24.py +.. literalinclude:: /examples/usage/usage_configuration_24.py :language: python - :caption: `environnment-based configuration` + :caption: `environment-based configuration` :lines: 25-41 :dedent: 4 @@ -326,7 +339,7 @@ Configuration Best Practices Always use pooling in production: -.. literalinclude:: /examples/usage/test_configuration_25.py +.. literalinclude:: /examples/usage/usage_configuration_25.py :language: python :caption: `connection pooling` :lines: 11-13 @@ -336,7 +349,7 @@ Always use pooling in production: Enable caching to avoid recompiling SQL statements: -.. literalinclude:: /examples/usage/test_configuration_26.py +.. literalinclude:: /examples/usage/usage_configuration_26.py :language: python :caption: `enable caching` :lines: 6-8 @@ -346,7 +359,7 @@ Enable caching to avoid recompiling SQL statements: Size pools based on your workload: -.. literalinclude:: /examples/usage/test_configuration_27.py +.. literalinclude:: /examples/usage/usage_configuration_27.py :language: python :caption: `tune pool sizes` :lines: 3-20 @@ -356,7 +369,7 @@ Size pools based on your workload: For trusted, performance-critical queries: -.. literalinclude:: /examples/usage/test_configuration_28.py +.. literalinclude:: /examples/usage/usage_configuration_28.py :language: python :caption: `no validation` :lines: 6-12 @@ -367,7 +380,7 @@ For trusted, performance-critical queries: Always close pools on shutdown: -.. literalinclude:: /examples/usage/test_configuration_29.py +.. literalinclude:: /examples/usage/usage_configuration_29.py :language: python :caption: `cleanup resources` :lines: 10-27 From 9c75fed6b4e1b27385fe9a5c523ec104c3de551f Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 10 Nov 2025 04:24:45 +0000 Subject: [PATCH 17/18] docs: update usage examples and configurations for clarity and consistency --- docs/examples/quickstart/quickstart_1.py | 12 +-- docs/examples/quickstart/quickstart_2.py | 26 +++-- docs/examples/quickstart/quickstart_3.py | 38 ++++--- docs/examples/quickstart/quickstart_4.py | 42 ++++---- docs/examples/quickstart/quickstart_5.py | 83 +++++++------- docs/examples/quickstart/quickstart_6.py | 13 ++- docs/examples/quickstart/quickstart_7.py | 36 ++++--- docs/examples/quickstart/quickstart_8.py | 20 ++-- docs/examples/usage/usage_configuration_1.py | 7 ++ docs/examples/usage/usage_configuration_10.py | 15 ++- docs/examples/usage/usage_configuration_11.py | 7 ++ docs/examples/usage/usage_configuration_12.py | 7 ++ docs/examples/usage/usage_configuration_13.py | 7 ++ docs/examples/usage/usage_configuration_14.py | 7 ++ docs/examples/usage/usage_configuration_15.py | 9 +- docs/examples/usage/usage_configuration_16.py | 8 +- docs/examples/usage/usage_configuration_17.py | 7 +- docs/examples/usage/usage_configuration_18.py | 7 +- docs/examples/usage/usage_configuration_19.py | 5 + docs/examples/usage/usage_configuration_2.py | 7 ++ docs/examples/usage/usage_configuration_20.py | 9 +- docs/examples/usage/usage_configuration_21.py | 8 +- docs/examples/usage/usage_configuration_22.py | 7 ++ docs/examples/usage/usage_configuration_23.py | 5 + docs/examples/usage/usage_configuration_24.py | 13 ++- docs/examples/usage/usage_configuration_25.py | 17 ++- docs/examples/usage/usage_configuration_26.py | 5 + docs/examples/usage/usage_configuration_27.py | 18 ++-- docs/examples/usage/usage_configuration_28.py | 5 + docs/examples/usage/usage_configuration_29.py | 6 ++ docs/examples/usage/usage_configuration_3.py | 7 ++ docs/examples/usage/usage_configuration_30.py | 5 + docs/examples/usage/usage_configuration_4.py | 9 +- docs/examples/usage/usage_configuration_5.py | 7 ++ docs/examples/usage/usage_configuration_6.py | 9 +- docs/examples/usage/usage_configuration_7.py | 7 ++ docs/examples/usage/usage_configuration_8.py | 7 +- docs/examples/usage/usage_configuration_9.py | 7 ++ docs/examples/usage/usage_index_1.py | 12 ++- docs/examples/usage/usage_index_2.py | 12 ++- docs/examples/usage/usage_index_3.py | 23 ++-- docs/getting_started/quickstart.rst | 26 +++-- docs/usage/configuration.rst | 102 +++++++++++------- docs/usage/index.rst | 9 +- pyproject.toml | 4 +- 45 files changed, 452 insertions(+), 250 deletions(-) diff --git a/docs/examples/quickstart/quickstart_1.py b/docs/examples/quickstart/quickstart_1.py index b19c3d7d..1987e0aa 100644 --- a/docs/examples/quickstart/quickstart_1.py +++ b/docs/examples/quickstart/quickstart_1.py @@ -1,17 +1,17 @@ -from sqlspec import SQLSpec -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_quickstart_1",) def test_quickstart_1() -> None: - # Create SQLSpec instance and configure database + # start-example + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + db_manager = SQLSpec() db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) - # Execute a query with db_manager.provide_session(db) as session: result = session.execute("SELECT 'Hello, SQLSpec!' as message") - print(result.get_first()) # {'message': 'Hello, SQLSpec!'} + print(result.get_first()) + # end-example assert result.get_first() == {"message": "Hello, SQLSpec!"} diff --git a/docs/examples/quickstart/quickstart_2.py b/docs/examples/quickstart/quickstart_2.py index 857a05c5..60fb2ffb 100644 --- a/docs/examples/quickstart/quickstart_2.py +++ b/docs/examples/quickstart/quickstart_2.py @@ -1,44 +1,42 @@ -from sqlspec import SQLSpec -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_quickstart_2",) def test_quickstart_2() -> None: + # start-example + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + db_manager = SQLSpec() db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) with db_manager.provide_session(db) as session: - # Create a table - _ = session.execute(""" + _ = session.execute( + """ CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL ) - """) + """ + ) - # Insert data _ = session.execute("INSERT INTO users (name, email) VALUES (?, ?)", "Alice", "alice@example.com") - # Insert multiple rows _ = session.execute_many( "INSERT INTO users (name, email) VALUES (?, ?)", [("Bob", "bob@example.com"), ("Charlie", "charlie@example.com")], ) - # Query all users users = session.select("SELECT * FROM users") print(f"All users: {users}") - # Query single user - alice = session.select_one("SELECT * FROM users WHERE name = ?", "Alice") + alice = session.select_one_or_none("SELECT * FROM users WHERE name = ?", "Alice") print(f"Alice: {alice}") - # Query scalar value count = session.select_value("SELECT COUNT(*) FROM users") print(f"Total users: {count}") + # end-example - assert len(users) == 3 # noqa: PLR2004 + assert len(users) == 3 assert alice == {"id": 1, "name": "Alice", "email": "alice@example.com"} - assert count == 3 # noqa: PLR2004 + assert count == 3 diff --git a/docs/examples/quickstart/quickstart_3.py b/docs/examples/quickstart/quickstart_3.py index 98f7fe5f..5917342f 100644 --- a/docs/examples/quickstart/quickstart_3.py +++ b/docs/examples/quickstart/quickstart_3.py @@ -1,38 +1,36 @@ -from pydantic import BaseModel +__all__ = ("test_quickstart_3",) -from sqlspec import SQLSpec -from sqlspec.adapters.sqlite import SqliteConfig - -__all__ = ("User",) +def test_quickstart_3() -> None: + # start-example + from pydantic import BaseModel -class User(BaseModel): - id: int - name: str - email: str + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + class User(BaseModel): + id: int + name: str + email: str -def test_quickstart_3() -> None: db_manager = SQLSpec() db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) with db_manager.provide_session(db) as session: - # Setup - _ = session.execute(""" + _ = session.execute( + """ CREATE TABLE users (id INTEGER, name TEXT, email TEXT) - """) + """ + ) _ = session.execute("INSERT INTO users VALUES (?, ?, ?)", 1, "Alice", "alice@example.com") - # Type-safe query - returns User instance user = session.select_one("SELECT * FROM users WHERE id = ?", 1, schema_type=User) + print(f"User: {user.name} ({user.email})") - # Now you have type hints and autocomplete! - print(f"User: {user.name} ({user.email})") # IDE knows these fields exist - - # Multiple results all_users = session.select("SELECT * FROM users", schema_type=User) - for u in all_users: - print(f"User: {u.name}") # Each item is a typed User + for typed_user in all_users: + print(f"User: {typed_user.name}") + # end-example assert user == User(id=1, name="Alice", email="alice@example.com") assert len(all_users) == 1 diff --git a/docs/examples/quickstart/quickstart_4.py b/docs/examples/quickstart/quickstart_4.py index 535d9574..d6b0db81 100644 --- a/docs/examples/quickstart/quickstart_4.py +++ b/docs/examples/quickstart/quickstart_4.py @@ -1,36 +1,36 @@ -from pydantic import BaseModel +"""Async quickstart example.""" -from sqlspec import SQLSpec -from sqlspec.adapters.aiosqlite import AiosqliteConfig +import pytest -__all__ = ("User", "test_quickstart_4") +__all__ = ("test_quickstart_4",) -class User(BaseModel): - id: int - name: str - email: str +@pytest.mark.asyncio +async def test_quickstart_4() -> None: + """Demonstrate async SQLSpec usage.""" + # start-example + from pydantic import BaseModel + from sqlspec import SQLSpec + from sqlspec.adapters.aiosqlite import AiosqliteConfig + + class User(BaseModel): + id: int + name: str + email: str -async def test_quickstart_4() -> None: db_manager = SQLSpec() db = db_manager.add_config(AiosqliteConfig(pool_config={"database": ":memory:"})) async with db_manager.provide_session(db) as session: - # Create table - _ = await session.execute(""" + await session.execute( + """ CREATE TABLE users (id INTEGER, name TEXT, email TEXT) - """) - - # Insert data - _ = await session.execute("INSERT INTO users VALUES (?, ?, ?)", 1, "Alice", "alice@example.com") - - # Type-safe async query + """ + ) + await session.execute("INSERT INTO users VALUES (?, ?, ?)", 1, "Alice", "alice@example.com") user = await session.select_one("SELECT * FROM users WHERE id = ?", 1, schema_type=User) - print(f"User: {user.name}") + # end-example assert user == User(id=1, name="Alice", email="alice@example.com") - assert isinstance(user, User) - assert user.name == "Alice" - assert user.email == "alice@example.com" diff --git a/docs/examples/quickstart/quickstart_5.py b/docs/examples/quickstart/quickstart_5.py index 2629df54..d63227e5 100644 --- a/docs/examples/quickstart/quickstart_5.py +++ b/docs/examples/quickstart/quickstart_5.py @@ -1,53 +1,56 @@ -import os -from typing import Any +"""Async PostgreSQL quickstart example.""" -from pydantic import BaseModel +import pytest -from sqlspec import SQLSpec -from sqlspec.adapters.asyncpg import AsyncpgConfig +__all__ = ("test_quickstart_5",) -__all__ = ("User", "test_quickstart_5") - -class User(BaseModel): - id: int - name: str - email: str - - -def _pool_config() -> "dict[str, Any]": - return { - "host": os.getenv("SQLSPEC_QUICKSTART_PG_HOST", "localhost"), - "port": int(os.getenv("SQLSPEC_QUICKSTART_PG_PORT", "5432")), - "user": os.getenv("SQLSPEC_QUICKSTART_PG_USER", "postgres"), - "password": os.getenv("SQLSPEC_QUICKSTART_PG_PASSWORD", "postgres"), - "database": os.getenv("SQLSPEC_QUICKSTART_PG_DATABASE", "mydb"), - } - - -async def _seed_users(session: Any) -> None: - await session.execute( - """ - CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - email TEXT NOT NULL +@pytest.mark.asyncio +async def test_quickstart_5() -> None: + """Demonstrate async PostgreSQL usage with SQLSpec.""" + # start-example + import os + from typing import Any + + from pydantic import BaseModel + + from sqlspec import SQLSpec + from sqlspec.adapters.asyncpg import AsyncpgConfig + + class User(BaseModel): + id: int + name: str + email: str + + def pool_config() -> "dict[str, Any]": + return { + "host": os.getenv("SQLSPEC_QUICKSTART_PG_HOST", "localhost"), + "port": int(os.getenv("SQLSPEC_QUICKSTART_PG_PORT", "5432")), + "user": os.getenv("SQLSPEC_QUICKSTART_PG_USER", "postgres"), + "password": os.getenv("SQLSPEC_QUICKSTART_PG_PASSWORD", "postgres"), + "database": os.getenv("SQLSPEC_QUICKSTART_PG_DATABASE", "mydb"), + } + + async def seed_users(session: Any) -> None: + await session.execute( + """ + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + email TEXT NOT NULL + ) + """ ) - """ - ) - await session.execute("TRUNCATE TABLE users") - await session.execute("INSERT INTO users (id, name, email) VALUES ($1, $2, $3)", 1, "Alice", "alice@example.com") - + await session.execute("TRUNCATE TABLE users") + await session.execute("INSERT INTO users (id, name, email) VALUES ($1, $2, $3)", 1, "Alice", "alice@example.com") -async def test_quickstart_5() -> None: db_manager = SQLSpec() - db = db_manager.add_config(AsyncpgConfig(pool_config=_pool_config())) + db = db_manager.add_config(AsyncpgConfig(pool_config=pool_config())) async with db_manager.provide_session(db) as session: - await _seed_users(session) - - # PostgreSQL uses $1, $2 for parameters instead of ? + await seed_users(session) user = await session.select_one("SELECT * FROM users WHERE id = $1", 1, schema_type=User) print(f"User: {user.name}") + # end-example assert user == User(id=1, name="Alice", email="alice@example.com") diff --git a/docs/examples/quickstart/quickstart_6.py b/docs/examples/quickstart/quickstart_6.py index 4f4c6bfb..6d75a77f 100644 --- a/docs/examples/quickstart/quickstart_6.py +++ b/docs/examples/quickstart/quickstart_6.py @@ -1,23 +1,22 @@ -from sqlspec import SQLSpec -from sqlspec.adapters.duckdb import DuckDBConfig -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_quickstart_6",) def test_quickstart_6() -> None: - db_manager = SQLSpec() + # start-example + from sqlspec import SQLSpec + from sqlspec.adapters.duckdb import DuckDBConfig + from sqlspec.adapters.sqlite import SqliteConfig - # Register multiple databases + db_manager = SQLSpec() sqlite_db = db_manager.add_config(SqliteConfig(pool_config={"database": "app.db"})) duckdb_db = db_manager.add_config(DuckDBConfig(pool_config={"database": "analytics.duckdb"})) - # Use different databases with db_manager.provide_session(sqlite_db) as sqlite_session: users = sqlite_session.select("SELECT 1") with db_manager.provide_session(duckdb_db) as duckdb_session: analytics = duckdb_session.select("SELECT 1") + # end-example assert isinstance(users, list) assert isinstance(analytics, list) diff --git a/docs/examples/quickstart/quickstart_7.py b/docs/examples/quickstart/quickstart_7.py index 68d6ac94..cdd3551c 100644 --- a/docs/examples/quickstart/quickstart_7.py +++ b/docs/examples/quickstart/quickstart_7.py @@ -1,43 +1,47 @@ -from sqlspec import SQLSpec -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_quickstart_7",) def test_quickstart_7() -> None: - db_manager = SQLSpec() + # start-example + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + db_manager = SQLSpec() db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) - # Transaction committed on successful exit with db_manager.provide_session(db) as session: session.begin() - _ = session.execute(""" + session.execute( + """ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) - """) - _ = session.execute(""" + """ + ) + session.execute( + """ CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY, - user_name TEXT NOT NULL - ) - """) - _ = session.execute("DELETE FROM users") - _ = session.execute("DELETE FROM orders") - _ = session.execute("INSERT INTO users (name) VALUES (?)", "Alice") - _ = session.execute("INSERT INTO orders (user_name) VALUES (?)", "Alice") + user_name TEXT NOT NULL + ) + """ + ) + session.execute("DELETE FROM users") + session.execute("DELETE FROM orders") + session.execute("INSERT INTO users (name) VALUES (?)", "Alice") + session.execute("INSERT INTO orders (user_name) VALUES (?)", "Alice") session.commit() with db_manager.provide_session(db) as session: session.begin() - _ = session.execute("INSERT INTO users (name) VALUES (?)", "Bob") + session.execute("INSERT INTO users (name) VALUES (?)", "Bob") session.rollback() with db_manager.provide_session(db) as session: alice = session.select_one_or_none("SELECT * FROM users WHERE name = ?", "Alice") bob = session.select_one_or_none("SELECT * FROM users WHERE name = ?", "Bob") + # end-example assert alice is not None assert alice["name"] == "Alice" diff --git a/docs/examples/quickstart/quickstart_8.py b/docs/examples/quickstart/quickstart_8.py index bf1127ab..c2c7c862 100644 --- a/docs/examples/quickstart/quickstart_8.py +++ b/docs/examples/quickstart/quickstart_8.py @@ -1,26 +1,26 @@ -from sqlspec import SQLSpec, sql -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_quickstart_8",) def test_quickstart_8() -> None: - # Build a query programmatically + # start-example + from sqlspec import SQLSpec, sql + from sqlspec.adapters.sqlite import SqliteConfig + query = sql.select("id", "name", "email").from_("users").where("age > ?").order_by("name") db_manager = SQLSpec() db = db_manager.add_config(SqliteConfig(pool_config={"database": ":memory:"})) with db_manager.provide_session(db) as session: - # Setup - _ = session.execute(""" + session.execute( + """ CREATE TABLE users (id INTEGER, name TEXT, email TEXT, age INTEGER) - """) - _ = session.execute("INSERT INTO users VALUES (?, ?, ?, ?)", 1, "Alice", "alice@example.com", 30) - - # Execute built query + """ + ) + session.execute("INSERT INTO users VALUES (?, ?, ?, ?)", 1, "Alice", "alice@example.com", 30) results = session.select(query, 25) print(results) + # end-example assert len(results) == 1 assert results[0] == {"id": 1, "name": "Alice", "email": "alice@example.com"} diff --git a/docs/examples/usage/usage_configuration_1.py b/docs/examples/usage/usage_configuration_1.py index dbe871f5..b8e3dd3d 100644 --- a/docs/examples/usage/usage_configuration_1.py +++ b/docs/examples/usage/usage_configuration_1.py @@ -1,4 +1,9 @@ +__all__ = ("test_sqlite_memory_db",) + + def test_sqlite_memory_db() -> None: + + # start-example from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig @@ -11,4 +16,6 @@ def test_sqlite_memory_db() -> None: # Use the database with db_manager.provide_session(db) as session: result = session.execute("SELECT 1") + # end-example assert result[0] == {"1": 1} + diff --git a/docs/examples/usage/usage_configuration_10.py b/docs/examples/usage/usage_configuration_10.py index 0e78544a..353c648b 100644 --- a/docs/examples/usage/usage_configuration_10.py +++ b/docs/examples/usage/usage_configuration_10.py @@ -1,12 +1,19 @@ -POOL_INSTANCE = 20 +__all__ = ("test_manual_pool",) def test_manual_pool() -> None: + + # start-example import os + import asyncpg + from sqlspec.adapters.asyncpg import AsyncpgConfig - dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") - pool = {"dsn": dsn, "min_size": 10, "max_size": POOL_INSTANCE} + pool = asyncpg.create_pool( + dsn=os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db"), min_size=10, max_size=20 + ) db = AsyncpgConfig(pool_instance=pool) - assert db.pool_instance["max_size"] == POOL_INSTANCE + # end-example + assert db.pool_instance is pool + diff --git a/docs/examples/usage/usage_configuration_11.py b/docs/examples/usage/usage_configuration_11.py index e1545362..15fb4fae 100644 --- a/docs/examples/usage/usage_configuration_11.py +++ b/docs/examples/usage/usage_configuration_11.py @@ -1,5 +1,12 @@ +__all__ = ("test_thread_local_connections",) + + def test_thread_local_connections() -> None: + + # start-example from sqlspec.adapters.sqlite import SqliteConfig config = SqliteConfig(pool_config={"database": "test.db"}) + # end-example assert config.pool_config["database"] == "test.db" + diff --git a/docs/examples/usage/usage_configuration_12.py b/docs/examples/usage/usage_configuration_12.py index 42ea8a86..6fb8694a 100644 --- a/docs/examples/usage/usage_configuration_12.py +++ b/docs/examples/usage/usage_configuration_12.py @@ -1,4 +1,9 @@ +__all__ = ("test_basic_statement_config",) + + def test_basic_statement_config() -> None: + + # start-example import os from sqlspec import StatementConfig @@ -15,5 +20,7 @@ def test_basic_statement_config() -> None: # Apply to adapter dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") config = AsyncpgConfig(pool_config={"dsn": dsn}, statement_config=statement_config) + # end-example assert config.statement_config.dialect == "postgres" assert config.statement_config.enable_parsing is True + diff --git a/docs/examples/usage/usage_configuration_13.py b/docs/examples/usage/usage_configuration_13.py index 4c5e8eb9..65fcab21 100644 --- a/docs/examples/usage/usage_configuration_13.py +++ b/docs/examples/usage/usage_configuration_13.py @@ -1,4 +1,9 @@ +__all__ = ("test_parameter_style_config",) + + def test_parameter_style_config() -> None: + + # start-example from sqlspec import ParameterStyle, ParameterStyleConfig, StatementConfig param_config = ParameterStyleConfig( @@ -12,4 +17,6 @@ def test_parameter_style_config() -> None: ) statement_config = StatementConfig(dialect="postgres", parameter_config=param_config) + # end-example assert statement_config.parameter_config.default_parameter_style == ParameterStyle.NUMERIC + diff --git a/docs/examples/usage/usage_configuration_14.py b/docs/examples/usage/usage_configuration_14.py index 0bfda1f2..aa9a4478 100644 --- a/docs/examples/usage/usage_configuration_14.py +++ b/docs/examples/usage/usage_configuration_14.py @@ -1,4 +1,9 @@ +__all__ = ("test_parameter_styles",) + + def test_parameter_styles() -> None: + + # start-example from sqlspec import ParameterStyle # Question mark (SQLite, DuckDB) @@ -14,6 +19,8 @@ def test_parameter_styles() -> None: # Format/pyformat (psycopg, MySQL) + # end-example assert qmark == ParameterStyle.QMARK assert numeric == ParameterStyle.NUMERIC assert named_colon == ParameterStyle.NAMED_COLON + diff --git a/docs/examples/usage/usage_configuration_15.py b/docs/examples/usage/usage_configuration_15.py index 359a4968..49e9f0d6 100644 --- a/docs/examples/usage/usage_configuration_15.py +++ b/docs/examples/usage/usage_configuration_15.py @@ -1,10 +1,11 @@ """Test configuration example: Global cache configuration.""" -SQL_CACHE_SIZE = 1000 +__all__ = ("test_global_cache_config",) def test_global_cache_config() -> None: """Test global cache configuration.""" + # start-example from sqlspec.core.cache import CacheConfig, update_cache_config cache_config = CacheConfig( @@ -12,12 +13,14 @@ def test_global_cache_config() -> None: sql_cache_enabled=True, # Cache SQL strings fragment_cache_enabled=True, # Cache SQL fragments optimized_cache_enabled=True, # Cache optimized AST - sql_cache_size=SQL_CACHE_SIZE, # Maximum cached SQL items + sql_cache_size=1000, # Maximum cached SQL items ) # Update global cache configuration update_cache_config(cache_config) # Verify config applied + # end-example assert cache_config.sql_cache_enabled is True - assert cache_config.sql_cache_size == SQL_CACHE_SIZE + assert cache_config.sql_cache_size == 1000 + diff --git a/docs/examples/usage/usage_configuration_16.py b/docs/examples/usage/usage_configuration_16.py index f37009eb..fc4885ae 100644 --- a/docs/examples/usage/usage_configuration_16.py +++ b/docs/examples/usage/usage_configuration_16.py @@ -1,13 +1,15 @@ """Test configuration example: Per-instance cache configuration.""" +__all__ = ("test_per_instance_cache_config",) + def test_per_instance_cache_config() -> None: """Test per-instance cache configuration.""" + # start-example import tempfile - from sqlspec import SQLSpec + from sqlspec import CacheConfig, SQLSpec from sqlspec.adapters.sqlite import SqliteConfig - from sqlspec.core.cache import CacheConfig with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: # Configure cache for specific SQLSpec instance @@ -20,4 +22,6 @@ def test_per_instance_cache_config() -> None: # Use the configured spec with db_manager.provide_session(db) as session: result = session.execute("SELECT 1") + # end-example assert result is not None + diff --git a/docs/examples/usage/usage_configuration_17.py b/docs/examples/usage/usage_configuration_17.py index c3a988c4..7c1599da 100644 --- a/docs/examples/usage/usage_configuration_17.py +++ b/docs/examples/usage/usage_configuration_17.py @@ -1,13 +1,16 @@ """Test configuration example: Cache statistics tracking.""" +__all__ = ("test_cache_statistics",) + def test_cache_statistics() -> None: """Test cache statistics tracking.""" + # start-example import tempfile from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig - from sqlspec.core.cache import get_cache_statistics, log_cache_stats + from sqlspec.core import get_cache_statistics, log_cache_stats with tempfile.NamedTemporaryFile(suffix=".db", delete=True) as tmp: db_manager = SQLSpec() @@ -20,8 +23,10 @@ def test_cache_statistics() -> None: # Get statistics stats = get_cache_statistics() + # end-example assert isinstance(stats, dict) assert "multi_level" in stats # Log statistics (logs to configured logger) log_cache_stats() + diff --git a/docs/examples/usage/usage_configuration_18.py b/docs/examples/usage/usage_configuration_18.py index d17b693d..79175045 100644 --- a/docs/examples/usage/usage_configuration_18.py +++ b/docs/examples/usage/usage_configuration_18.py @@ -1,12 +1,16 @@ """Test configuration example: Cache clearing operations.""" +__all__ = ("test_clear_cache",) + def test_clear_cache() -> None: """Test cache clearing operations.""" - from sqlspec.core.cache import clear_all_caches, get_cache_statistics + # start-example + from sqlspec.core import clear_all_caches, get_cache_statistics # Get initial statistics stats_before = get_cache_statistics() + # end-example assert isinstance(stats_before, dict) # Clear all caches and reset statistics @@ -16,3 +20,4 @@ def test_clear_cache() -> None: stats_after = get_cache_statistics() assert isinstance(stats_after, dict) assert "multi_level" in stats_after + diff --git a/docs/examples/usage/usage_configuration_19.py b/docs/examples/usage/usage_configuration_19.py index 6020d20c..332ff45a 100644 --- a/docs/examples/usage/usage_configuration_19.py +++ b/docs/examples/usage/usage_configuration_19.py @@ -1,8 +1,11 @@ """Test configuration example: Binding multiple database configurations.""" +__all__ = ("test_binding_multiple_configs",) + def test_binding_multiple_configs() -> None: """Test binding multiple database configurations.""" + # start-example import os import tempfile @@ -25,5 +28,7 @@ def test_binding_multiple_configs() -> None: sqlite_config = db_manager.get_config(sqlite_key) pg_config = db_manager.get_config(asyncpg_key) + # end-example assert sqlite_config.pool_config["database"] == tmp.name assert pg_config.pool_config["dsn"] == os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") + diff --git a/docs/examples/usage/usage_configuration_2.py b/docs/examples/usage/usage_configuration_2.py index 2cc4e66e..5f0b5704 100644 --- a/docs/examples/usage/usage_configuration_2.py +++ b/docs/examples/usage/usage_configuration_2.py @@ -1,4 +1,9 @@ +__all__ = ("test_sqlite_config_setup",) + + def test_sqlite_config_setup() -> None: + + # start-example from sqlspec.adapters.sqlite import SqliteConfig config = SqliteConfig( @@ -10,4 +15,6 @@ def test_sqlite_config_setup() -> None: "uri": False, # Enable URI mode } ) + # end-example assert config.pool_config["database"] == "myapp.db" + diff --git a/docs/examples/usage/usage_configuration_20.py b/docs/examples/usage/usage_configuration_20.py index 06a26634..4c6c7f51 100644 --- a/docs/examples/usage/usage_configuration_20.py +++ b/docs/examples/usage/usage_configuration_20.py @@ -1,8 +1,11 @@ """Test configuration example: Named database bindings.""" +__all__ = ("test_named_bindings",) + def test_named_bindings() -> None: """Test named database bindings.""" + # start-example import os import tempfile @@ -14,9 +17,7 @@ def test_named_bindings() -> None: db_manager = SQLSpec() # Add with bind keys - cache_key = db_manager.add_config( - SqliteConfig(pool_config={"database": tmp.name}, bind_key="cache_db") - ) + cache_key = db_manager.add_config(SqliteConfig(pool_config={"database": tmp.name}, bind_key="cache_db")) dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") main_key = db_manager.add_config(AsyncpgConfig(pool_config={"dsn": dsn}, bind_key="main_db")) @@ -27,5 +28,7 @@ def test_named_bindings() -> None: cache_config = db_manager.get_config(cache_key) main_config = db_manager.get_config(main_key) + # end-example assert cache_config.bind_key == "cache_db" assert main_config.bind_key == "main_db" + diff --git a/docs/examples/usage/usage_configuration_21.py b/docs/examples/usage/usage_configuration_21.py index 183a9b5f..4cb87ee2 100644 --- a/docs/examples/usage/usage_configuration_21.py +++ b/docs/examples/usage/usage_configuration_21.py @@ -1,13 +1,11 @@ """Test configuration example: Basic migration configuration.""" -import pytest +__all__ = ("test_basic_migration_config",) -@pytest.mark.skipif( - not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" -) def test_basic_migration_config() -> None: """Test basic migration configuration.""" + # start-example import os from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -25,5 +23,7 @@ def test_basic_migration_config() -> None: }, ) + # end-example assert config.migration_config["script_location"] == "migrations" assert "litestar" in config.migration_config["include_extensions"] + diff --git a/docs/examples/usage/usage_configuration_22.py b/docs/examples/usage/usage_configuration_22.py index d6986916..f15b398f 100644 --- a/docs/examples/usage/usage_configuration_22.py +++ b/docs/examples/usage/usage_configuration_22.py @@ -1,4 +1,9 @@ +__all__ = ("test_basic_migration_config",) + + def test_basic_migration_config() -> None: + + # start-example import os from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -15,5 +20,7 @@ def test_basic_migration_config() -> None: "include_extensions": ["litestar"], # Simple string list only }, ) + # end-example assert config.migration_config["script_location"] == "migrations" assert "litestar" in config.migration_config["include_extensions"] + diff --git a/docs/examples/usage/usage_configuration_23.py b/docs/examples/usage/usage_configuration_23.py index a04f57c3..1ec57e02 100644 --- a/docs/examples/usage/usage_configuration_23.py +++ b/docs/examples/usage/usage_configuration_23.py @@ -1,7 +1,10 @@ """Test configuration example: Environment-based configuration.""" +__all__ = ("test_extension_config",) + def test_extension_config() -> None: + # start-example import os from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -26,6 +29,8 @@ def test_extension_config() -> None: }, ) + # end-example assert config.extension_config["litestar"]["commit_mode"] == "autocommit_include_redirect" assert config.extension_config["litestar"]["extra_commit_statuses"] == {201} assert config.extension_config["litestar"]["correlation_header"] == "x-request-id" + diff --git a/docs/examples/usage/usage_configuration_24.py b/docs/examples/usage/usage_configuration_24.py index bcad9ddf..11146043 100644 --- a/docs/examples/usage/usage_configuration_24.py +++ b/docs/examples/usage/usage_configuration_24.py @@ -1,14 +1,15 @@ """Test configuration example: Environment-based configuration.""" -import os -from unittest.mock import patch - -POSTGRES_PORT = 5433 +__all__ = ("test_environment_based_configuration",) def test_environment_based_configuration() -> None: """Test environment-based configuration pattern.""" + # start-example + import os + from unittest.mock import patch + # Mock environment variables env_vars = { "DB_HOST": "testhost", @@ -31,8 +32,10 @@ def test_environment_based_configuration() -> None: } ) + # end-example assert config.pool_config["host"] == "testhost" - assert config.pool_config["port"] == POSTGRES_PORT + assert config.pool_config["port"] == 5433 assert config.pool_config["user"] == "testuser" assert config.pool_config["password"] == "testpass" assert config.pool_config["database"] == "testdb" + diff --git a/docs/examples/usage/usage_configuration_25.py b/docs/examples/usage/usage_configuration_25.py index c4e2110a..3faee88d 100644 --- a/docs/examples/usage/usage_configuration_25.py +++ b/docs/examples/usage/usage_configuration_25.py @@ -1,23 +1,20 @@ """Test configuration example: Best practice - Use connection pooling.""" -import pytest +__all__ = ("test_connection_pooling_best_practice",) -MIN_POOL_SIZE = 10 -MAX_POOL_SIZE = 20 - -@pytest.mark.skipif( - not pytest.importorskip("asyncpg", reason="AsyncPG not installed"), reason="AsyncPG integration tests disabled" -) def test_connection_pooling_best_practice() -> None: """Test connection pooling best practice configuration.""" + # start-example import os from sqlspec.adapters.asyncpg import AsyncpgConfig dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") - config = AsyncpgConfig(pool_config={"dsn": dsn, "min_size": MIN_POOL_SIZE, "max_size": MAX_POOL_SIZE}) + config = AsyncpgConfig(pool_config={"dsn": dsn, "min_size": 10, "max_size": 20}) - assert config.pool_config["min_size"] == MIN_POOL_SIZE - assert config.pool_config["max_size"] == MAX_POOL_SIZE + # end-example + assert config.pool_config["min_size"] == 10 + assert config.pool_config["max_size"] == 20 assert config.supports_connection_pooling is True + diff --git a/docs/examples/usage/usage_configuration_26.py b/docs/examples/usage/usage_configuration_26.py index ad19e70c..fcbb8253 100644 --- a/docs/examples/usage/usage_configuration_26.py +++ b/docs/examples/usage/usage_configuration_26.py @@ -1,11 +1,16 @@ """Test configuration example: Best practice - Enable caching.""" +__all__ = ("test_enable_caching_best_practice",) + def test_enable_caching_best_practice() -> None: """Test caching best practice configuration.""" + # start-example from sqlspec import StatementConfig statement_config = StatementConfig(dialect="postgres", enable_caching=True) + # end-example assert statement_config.enable_caching is True assert statement_config.dialect == "postgres" + diff --git a/docs/examples/usage/usage_configuration_27.py b/docs/examples/usage/usage_configuration_27.py index 921c0cad..5b432006 100644 --- a/docs/examples/usage/usage_configuration_27.py +++ b/docs/examples/usage/usage_configuration_27.py @@ -1,5 +1,8 @@ """Test configuration example: Best practice - Tune pool sizes.""" +__all__ = ("test_tune_pool_sizes_best_practice",) + + MIN_POOL_SIZE_CPU = 5 MAX_POOL_SIZE_CPU = 10 MIN_IO_BOUND_POOL_SIZE = 20 @@ -9,12 +12,15 @@ def test_tune_pool_sizes_best_practice() -> None: """Test pool sizing best practices for different workloads.""" + # start-example # CPU-bound workload - smaller pool - cpu_bound_pool_config = {"min_size": MIN_POOL_SIZE_CPU, "max_size": MAX_POOL_SIZE_CPU} - assert cpu_bound_pool_config["min_size"] == MIN_POOL_SIZE_CPU - assert cpu_bound_pool_config["max_size"] == MAX_POOL_SIZE_CPU + cpu_bound_pool_config = {"min_size": 5, "max_size": 10} + # end-example + assert cpu_bound_pool_config["min_size"] == 5 + assert cpu_bound_pool_config["max_size"] == 10 # I/O-bound workload - larger pool - io_bound_pool_config = {"min_size": MIN_IO_BOUND_POOL_SIZE, "max_size": MAX_IO_BOUND_POOL_SIZE} - assert io_bound_pool_config["min_size"] == MIN_IO_BOUND_POOL_SIZE - assert io_bound_pool_config["max_size"] == MAX_IO_BOUND_POOL_SIZE + io_bound_pool_config = {"min_size": 20, "max_size": 50} + assert io_bound_pool_config["min_size"] == 20 + assert io_bound_pool_config["max_size"] == 50 + diff --git a/docs/examples/usage/usage_configuration_28.py b/docs/examples/usage/usage_configuration_28.py index cc0ece0a..cc90214e 100644 --- a/docs/examples/usage/usage_configuration_28.py +++ b/docs/examples/usage/usage_configuration_28.py @@ -1,9 +1,12 @@ """Test configuration example: Best practice - Tune pool sizes.""" +__all__ = ("test_disable_security_checks_best_practice",) + def test_disable_security_checks_best_practice() -> None: """Test disabling security checks when necessary.""" + # start-example from sqlspec import StatementConfig # Example: Disabling security checks for trusted internal queries @@ -11,4 +14,6 @@ def test_disable_security_checks_best_practice() -> None: dialect="postgres", enable_validation=False, # Skip security checks ) + # end-example assert statement_config.enable_validation is False + diff --git a/docs/examples/usage/usage_configuration_29.py b/docs/examples/usage/usage_configuration_29.py index 120c81a7..dc2689b7 100644 --- a/docs/examples/usage/usage_configuration_29.py +++ b/docs/examples/usage/usage_configuration_29.py @@ -1,11 +1,15 @@ """Test configuration example: Best practice - Clean up resources.""" +__all__ = ("test_cleanup_resources_best_practice",) + + import pytest @pytest.mark.asyncio async def test_cleanup_resources_best_practice() -> None: """Test resource cleanup best practice.""" + # start-example import tempfile from sqlspec import SQLSpec @@ -23,4 +27,6 @@ async def test_cleanup_resources_best_practice() -> None: await db_manager.close_all_pools() # Verify pools are closed + # end-example assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") + diff --git a/docs/examples/usage/usage_configuration_3.py b/docs/examples/usage/usage_configuration_3.py index e53b38e3..c39c2f99 100644 --- a/docs/examples/usage/usage_configuration_3.py +++ b/docs/examples/usage/usage_configuration_3.py @@ -1,10 +1,17 @@ +__all__ = ("test_memory_databases",) + + def test_memory_databases() -> None: + + # start-example from sqlspec.adapters.sqlite import SqliteConfig # In-memory database (isolated per connection) config = SqliteConfig(pool_config={"database": ":memory:"}) + # end-example assert ":memory_" in config.pool_config["database"] # Shared memory database shared_config = SqliteConfig(pool_config={"database": "file:memdb1?mode=memory&cache=shared", "uri": True}) assert shared_config.pool_config["database"] == "file:memdb1?mode=memory&cache=shared" + diff --git a/docs/examples/usage/usage_configuration_30.py b/docs/examples/usage/usage_configuration_30.py index 9b653400..99ab752b 100644 --- a/docs/examples/usage/usage_configuration_30.py +++ b/docs/examples/usage/usage_configuration_30.py @@ -1,8 +1,11 @@ """Telemetry snapshot example.""" +__all__ = ("test_telemetry_snapshot",) + def test_telemetry_snapshot() -> None: """Demonstrate SQLSpec.telemetry_snapshot().""" + # start-example from sqlspec import SQLSpec from sqlspec.adapters.sqlite import SqliteConfig @@ -13,5 +16,7 @@ def test_telemetry_snapshot() -> None: session.execute("SELECT 1") snapshot = db_manager.telemetry_snapshot() + # end-example assert "SqliteConfig.lifecycle.query_start" in snapshot _ = snapshot.get("storage_bridge.bytes_written", 0) + diff --git a/docs/examples/usage/usage_configuration_4.py b/docs/examples/usage/usage_configuration_4.py index a8de0ded..11acd553 100644 --- a/docs/examples/usage/usage_configuration_4.py +++ b/docs/examples/usage/usage_configuration_4.py @@ -1,4 +1,9 @@ +__all__ = ("test_asyncpg_config_setup",) + + def test_asyncpg_config_setup() -> None: + + # start-example import os from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -7,7 +12,7 @@ def test_asyncpg_config_setup() -> None: port = int(os.getenv("SQLSPEC_USAGE_PG_PORT", "5432")) user = os.getenv("SQLSPEC_USAGE_PG_USER", "user") password = os.getenv("SQLSPEC_USAGE_PG_PASSWORD", "password") - database = os.getenv("SQLSPEC_USAGE_PG_DATABASE", "db" ) + database = os.getenv("SQLSPEC_USAGE_PG_DATABASE", "db") dsn = os.getenv("SQLSPEC_USAGE_PG_DSN", f"postgresql://{user}:{password}@{host}:{port}/{database}") config = AsyncpgConfig( @@ -22,4 +27,6 @@ def test_asyncpg_config_setup() -> None: "database": database, } ) + # end-example assert config.pool_config["host"] == host + diff --git a/docs/examples/usage/usage_configuration_5.py b/docs/examples/usage/usage_configuration_5.py index 9cd3377e..a1ab8b12 100644 --- a/docs/examples/usage/usage_configuration_5.py +++ b/docs/examples/usage/usage_configuration_5.py @@ -1,4 +1,9 @@ +__all__ = ("test_psycopg_config_setup",) + + def test_psycopg_config_setup() -> None: + + # start-example import os from sqlspec.adapters.psycopg import PsycopgAsyncConfig @@ -26,4 +31,6 @@ def test_psycopg_config_setup() -> None: "timeout": 30.0, } ) + # end-example assert config.pool_config is not None + diff --git a/docs/examples/usage/usage_configuration_6.py b/docs/examples/usage/usage_configuration_6.py index 9355cb4c..c5b520a1 100644 --- a/docs/examples/usage/usage_configuration_6.py +++ b/docs/examples/usage/usage_configuration_6.py @@ -1,13 +1,14 @@ -MYSQL_PORT = 3306 +__all__ = ("test_asyncmy_config_setup",) def test_asyncmy_config_setup() -> None: + # start-example from sqlspec.adapters.asyncmy import AsyncmyConfig config = AsyncmyConfig( pool_config={ "host": "localhost", - "port": MYSQL_PORT, + "port": 3306, "user": "myuser", "password": "mypassword", "database": "mydb", @@ -17,4 +18,6 @@ def test_asyncmy_config_setup() -> None: "pool_recycle": 3600, } ) - assert config.pool_config["port"] == MYSQL_PORT + # end-example + assert config.pool_config["port"] == 3306 + diff --git a/docs/examples/usage/usage_configuration_7.py b/docs/examples/usage/usage_configuration_7.py index 3152d6ed..2dcba283 100644 --- a/docs/examples/usage/usage_configuration_7.py +++ b/docs/examples/usage/usage_configuration_7.py @@ -1,8 +1,15 @@ +__all__ = ("test_duckdb_config_setup",) + + def test_duckdb_config_setup() -> None: + + # start-example from sqlspec.adapters.duckdb import DuckDBConfig in_memory_config = DuckDBConfig() + # end-example assert in_memory_config.pool_config.get("database") == ":memory:shared_db" persistent_config = DuckDBConfig(pool_config={"database": "analytics.duckdb", "read_only": False}) assert persistent_config.pool_config["read_only"] is False + diff --git a/docs/examples/usage/usage_configuration_8.py b/docs/examples/usage/usage_configuration_8.py index acf7624a..d559c4c4 100644 --- a/docs/examples/usage/usage_configuration_8.py +++ b/docs/examples/usage/usage_configuration_8.py @@ -1,7 +1,8 @@ -MIN_POOL_SIZE = 10 +__all__ = ("test_asyncpg_pool_setup",) def test_asyncpg_pool_setup() -> None: + # start-example import os from sqlspec.adapters.asyncpg import AsyncpgConfig @@ -17,4 +18,6 @@ def test_asyncpg_pool_setup() -> None: "max_inactive_connection_lifetime": 300.0, } ) - assert config.pool_config["min_size"] == MIN_POOL_SIZE + # end-example + assert config.pool_config["min_size"] == 10 + diff --git a/docs/examples/usage/usage_configuration_9.py b/docs/examples/usage/usage_configuration_9.py index 0e254495..38d56f1a 100644 --- a/docs/examples/usage/usage_configuration_9.py +++ b/docs/examples/usage/usage_configuration_9.py @@ -1,4 +1,9 @@ +__all__ = ("test_pool_lifecycle",) + + def test_pool_lifecycle() -> None: + + # start-example import os from sqlspec import SQLSpec @@ -9,4 +14,6 @@ def test_pool_lifecycle() -> None: asyncpg_key = db_manager.add_config(AsyncpgConfig(pool_config={"dsn": dsn})) asyncpg_config = db_manager.get_config(asyncpg_key) + # end-example assert asyncpg_config.pool_config["dsn"] == dsn + diff --git a/docs/examples/usage/usage_index_1.py b/docs/examples/usage/usage_index_1.py index a8a48516..89b8e0d1 100644 --- a/docs/examples/usage/usage_index_1.py +++ b/docs/examples/usage/usage_index_1.py @@ -1,15 +1,16 @@ -from sqlspec import SQLSpec -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_index_1",) def test_index_1() -> None: + # start-example + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + db_manager = SQLSpec() db = db_manager.add_config(SqliteConfig()) with db_manager.provide_session(db) as session: - _ = session.execute( + session.execute( """ CREATE TABLE users ( id INTEGER PRIMARY KEY, @@ -17,9 +18,10 @@ def test_index_1() -> None: ) """ ) - _ = session.execute("INSERT INTO users VALUES (?, ?)", 1, "alice") + 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) + # end-example assert user == {"id": 1, "name": "alice"} diff --git a/docs/examples/usage/usage_index_2.py b/docs/examples/usage/usage_index_2.py index aac40079..551d3e71 100644 --- a/docs/examples/usage/usage_index_2.py +++ b/docs/examples/usage/usage_index_2.py @@ -1,16 +1,17 @@ -from sqlspec import SQLSpec, sql -from sqlspec.adapters.sqlite import SqliteConfig - __all__ = ("test_index_2",) def test_index_2() -> None: + # start-example + from sqlspec import SQLSpec, sql + from sqlspec.adapters.sqlite import SqliteConfig + 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( + session.execute( """ CREATE TABLE users ( id INTEGER PRIMARY KEY, @@ -20,7 +21,7 @@ def test_index_2() -> None: ) """ ) - _ = session.execute( + session.execute( """ INSERT INTO users VALUES (1, 'alice', 'alice@example.com', 1), @@ -29,6 +30,7 @@ def test_index_2() -> None: """ ) users = session.select(query, True) # noqa: FBT003 + # end-example 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 index 3e7db4bf..dbca3c07 100644 --- a/docs/examples/usage/usage_index_3.py +++ b/docs/examples/usage/usage_index_3.py @@ -1,24 +1,24 @@ -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: + # start-example + from pathlib import Path + from sqlspec import SQLSpec + from sqlspec.adapters.sqlite import SqliteConfig + from sqlspec.loader import SQLFileLoader + + 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) + 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( + session.execute( """ CREATE TABLE users ( id INTEGER PRIMARY KEY, @@ -29,7 +29,7 @@ def test_index_3() -> None: ) """ ) - _ = session.execute( + session.execute( """ INSERT INTO users(id, username, email) VALUES (1, 'alice', 'alice@example.com'), @@ -37,6 +37,7 @@ def test_index_3() -> None: """ ) user = session.select_one(get_user_by_id, user_id=2) + # end-example assert user["username"] == "bob" assert user["email"] == "bob@example.com" diff --git a/docs/getting_started/quickstart.rst b/docs/getting_started/quickstart.rst index 3c9e0823..e85c169f 100644 --- a/docs/getting_started/quickstart.rst +++ b/docs/getting_started/quickstart.rst @@ -12,7 +12,8 @@ Let's start with the simplest possible example - executing a query and getting r .. literalinclude:: /examples/quickstart/quickstart_1.py :language: python :caption: ``first sqlspec query`` - :lines: 6-13 + :start-after: # start-example + :end-before: # end-example :dedent: 4 What's happening here? @@ -30,7 +31,8 @@ Let's create a table, insert some data, and query it: .. literalinclude:: /examples/quickstart/quickstart_2.py :language: python :caption: ``working with real data`` - :lines: 6-38 + :start-after: # start-example + :end-before: # end-example :dedent: 4 Session Methods Cheat Sheet @@ -54,7 +56,8 @@ The real power of SQLSpec comes from type-safe result mapping. Define your data .. literalinclude:: /examples/quickstart/quickstart_3.py :language: python :caption: ``type-safe results`` - :lines: 15-34 + :start-after: # start-example + :end-before: # end-example :dedent: 4 .. note:: @@ -69,8 +72,9 @@ SQLSpec supports async/await for non-blocking database operations. Here's the sa .. literalinclude:: /examples/quickstart/quickstart_4.py :language: python :caption: ``async support`` - :lines: 15-30 - :dedent: 2 + :start-after: # start-example + :end-before: # end-example + :dedent: 4 The API is identical - just add ``await`` and use async config/drivers! @@ -82,7 +86,8 @@ One of SQLSpec's strengths is the consistent API across databases. Here's the sa .. literalinclude:: /examples/quickstart/quickstart_5.py :language: python :caption: ``switching databases`` - :lines: 18-58 + :start-after: # start-example + :end-before: # end-example :dedent: 4 .. note:: @@ -110,7 +115,8 @@ Need to work with multiple databases? Register multiple configs: .. literalinclude:: /examples/quickstart/quickstart_6.py :language: python :caption: ``multiple databases`` - :lines: 7-18 + :start-after: # start-example + :end-before: # end-example :dedent: 4 @@ -123,7 +129,8 @@ boundaries explicitly and keep examples deterministic: .. literalinclude:: /examples/quickstart/quickstart_7.py :language: python :caption: ``transaction support`` - :lines: 8-55 + :start-after: # start-example + :end-before: # end-example :dedent: 4 The snippet seeds data in a temporary SQLite database, intentionally triggers a failure, @@ -142,7 +149,8 @@ For those who prefer programmatic query construction, SQLSpec includes an experi .. literalinclude:: /examples/quickstart/quickstart_8.py :language: python :caption: ``query builder`` - :lines: 6-21 + :start-after: # start-example + :end-before: # end-example :dedent: 4 .. warning:: diff --git a/docs/usage/configuration.rst b/docs/usage/configuration.rst index dfb81ed0..2e4810d0 100644 --- a/docs/usage/configuration.rst +++ b/docs/usage/configuration.rst @@ -21,7 +21,8 @@ The simplest way to use SQLSpec is with default configuration: .. literalinclude:: /examples/usage/usage_configuration_1.py :language: python :caption: `basic configuration` - :lines: 2-14 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Database Configurations @@ -42,7 +43,8 @@ SQLite Configuration .. literalinclude:: /examples/usage/usage_configuration_2.py :language: python :caption: `sqlite configuration` - :lines: 2-11 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **Memory Databases** @@ -50,7 +52,8 @@ SQLite Configuration .. literalinclude:: /examples/usage/usage_configuration_3.py :language: python :caption: `memory sqlite configuration` - :lines: 2-13 + :start-after: # start-example + :end-before: # end-example :dedent: 2 PostgreSQL Configuration (asyncpg) @@ -59,7 +62,8 @@ PostgreSQL Configuration (asyncpg) .. literalinclude:: /examples/usage/usage_configuration_4.py :language: python :caption: `postgres asyncpg configuration` - :lines: 2-16 + :start-after: # start-example + :end-before: # end-example :dedent: 2 PostgreSQL Configuration (psycopg) @@ -68,7 +72,8 @@ PostgreSQL Configuration (psycopg) .. literalinclude:: /examples/usage/usage_configuration_5.py :language: python :caption: `psycopg async configuration` - :lines: 2-19 + :start-after: # start-example + :end-before: # end-example :dedent: 2 MySQL Configuration (asyncmy) @@ -77,7 +82,8 @@ MySQL Configuration (asyncmy) .. literalinclude:: /examples/usage/usage_configuration_6.py :language: python :caption: `mysql asyncmy configuration` - :lines: 2-15 + :start-after: # start-example + :end-before: # end-example :dedent: 2 DuckDB Configuration @@ -86,7 +92,8 @@ DuckDB Configuration .. literalinclude:: /examples/usage/usage_configuration_7.py :language: python :caption: `duckdb configuration` - :lines: 2-11 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Connection Pooling @@ -100,7 +107,8 @@ Pool Configuration .. literalinclude:: /examples/usage/usage_configuration_8.py :language: python :caption: `pool configuration` - :lines: 2-11 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **Pool Lifecycle Management** @@ -108,7 +116,8 @@ Pool Configuration .. literalinclude:: /examples/usage/usage_configuration_9.py :language: python :caption: `pool lifecycle management` - :lines: 2-7 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Using Pre-Created Pools @@ -117,7 +126,8 @@ Using Pre-Created Pools .. literalinclude:: /examples/usage/usage_configuration_10.py :language: python :caption: `using pre-created pools` - :lines: 2-9 + :start-after: # start-example + :end-before: # end-example :dedent: 2 No-Pooling Configuration @@ -126,7 +136,8 @@ No-Pooling Configuration .. literalinclude:: /examples/usage/usage_configuration_11.py :language: python :caption: `no-pooling configuration` - :lines: 2-4 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Statement Configuration @@ -140,7 +151,8 @@ Basic Statement Config .. literalinclude:: /examples/usage/usage_configuration_12.py :language: python :caption: `basic statement config` - :lines: 2-14 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Parameter Style Configuration @@ -149,7 +161,8 @@ Parameter Style Configuration .. literalinclude:: /examples/usage/usage_configuration_13.py :language: python :caption: `parameter style configuration` - :lines: 2-17 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **Parameter Styles** @@ -159,7 +172,8 @@ SQLSpec supports multiple parameter placeholder styles: .. literalinclude:: /examples/usage/usage_configuration_14.py :language: python :caption: `parameter styles` - :lines: 2-21 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Validation Configuration @@ -172,7 +186,8 @@ Disable validation for performance-critical paths where input is trusted: .. literalinclude:: /examples/usage/usage_configuration_15.py :language: python :caption: `validation configuration` - :lines: 5-17 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Cache Configuration @@ -183,19 +198,21 @@ SQLSpec uses multi-tier caching to avoid recompiling SQL statements. Global Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/usage_configuration_16.py +.. literalinclude:: /examples/usage/usage_configuration_15.py :language: python :caption: `global cache configuration` - :lines: 6-28 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Per-Instance Cache Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. literalinclude:: /examples/usage/usage_configuration_17.py +.. literalinclude:: /examples/usage/usage_configuration_16.py :language: python :caption: `per-instance cache configuration` - :lines: 6-27 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Cache Statistics @@ -203,19 +220,21 @@ Cache Statistics Monitor cache statistics: -.. literalinclude:: /examples/usage/usage_configuration_18.py +.. literalinclude:: /examples/usage/usage_configuration_17.py :language: python :caption: `cache statistics` - :lines: 6-18 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Clear Cache ^^^^^^^^^^^ -.. literalinclude:: /examples/usage/usage_configuration_19.py +.. literalinclude:: /examples/usage/usage_configuration_18.py :language: python :caption: `clear cache` - :lines: 6-24 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Multiple Database Configurations @@ -229,7 +248,8 @@ Binding Multiple Configs .. literalinclude:: /examples/usage/usage_configuration_19.py :language: python :caption: `binding multiple configurations` - :lines: 4-27 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Named Bindings @@ -240,7 +260,8 @@ Use bind keys for clearer configuration management: .. literalinclude:: /examples/usage/usage_configuration_20.py :language: python :caption: `named bindings` - :lines: 6-25 + :start-after: # start-example + :end-before: # end-example :dedent: 2 ``SQLSpec.add_config()`` returns a key for the registered configuration. @@ -261,7 +282,8 @@ Basic Migration Config .. literalinclude:: /examples/usage/usage_configuration_22.py :language: python :caption: `basic migration config` - :lines: 2-15 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **Migration CLI** @@ -307,7 +329,8 @@ Litestar Plugin Configuration .. literalinclude:: /examples/usage/usage_configuration_23.py :language: python :caption: `litestar plugin configuration` - :lines: 4-28 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Telemetry Snapshot @@ -318,8 +341,9 @@ Call ``SQLSpec.telemetry_snapshot()`` to inspect lifecycle counters, serializer .. literalinclude:: /examples/usage/usage_configuration_30.py :language: python :caption: `telemetry snapshot` - :lines: 1-15 - :dedent: 0 + :start-after: # start-example + :end-before: # end-example + :dedent: 2 Environment-Based Configuration ------------------------------- @@ -329,8 +353,9 @@ Use environment variables for configuration: .. literalinclude:: /examples/usage/usage_configuration_24.py :language: python :caption: `environment-based configuration` - :lines: 25-41 - :dedent: 4 + :start-after: # start-example + :end-before: # end-example + :dedent: 2 Configuration Best Practices ----------------------------- @@ -342,7 +367,8 @@ Always use pooling in production: .. literalinclude:: /examples/usage/usage_configuration_25.py :language: python :caption: `connection pooling` - :lines: 11-13 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **2. Enable Caching** @@ -352,7 +378,8 @@ Enable caching to avoid recompiling SQL statements: .. literalinclude:: /examples/usage/usage_configuration_26.py :language: python :caption: `enable caching` - :lines: 6-8 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **3. Tune Pool Sizes** @@ -362,7 +389,8 @@ Size pools based on your workload: .. literalinclude:: /examples/usage/usage_configuration_27.py :language: python :caption: `tune pool sizes` - :lines: 3-20 + :start-after: # start-example + :end-before: # end-example :dedent: 2 **4. Disable Validation in Production** @@ -372,7 +400,8 @@ For trusted, performance-critical queries: .. literalinclude:: /examples/usage/usage_configuration_28.py :language: python :caption: `no validation` - :lines: 6-12 + :start-after: # start-example + :end-before: # end-example :dedent: 2 @@ -383,7 +412,8 @@ Always close pools on shutdown: .. literalinclude:: /examples/usage/usage_configuration_29.py :language: python :caption: `cleanup resources` - :lines: 10-27 + :start-after: # start-example + :end-before: # end-example :dedent: 2 Next Steps diff --git a/docs/usage/index.rst b/docs/usage/index.rst index 2f31063f..06f5f4f2 100644 --- a/docs/usage/index.rst +++ b/docs/usage/index.rst @@ -54,7 +54,8 @@ Quick Reference .. literalinclude:: /examples/usage/usage_index_1.py :language: python :caption: ``basic query execution`` - :lines: 1-23 + :start-after: # start-example + :end-before: # end-example :dedent: 4 **Using the Query Builder** @@ -62,7 +63,8 @@ Quick Reference .. literalinclude:: /examples/usage/usage_index_2.py :language: python :caption: ``using the query builder`` - :lines: 1-32 + :start-after: # start-example + :end-before: # end-example :dedent: 4 **Loading from SQL Files** @@ -70,7 +72,8 @@ Quick Reference .. literalinclude:: /examples/usage/usage_index_3.py :language: python :caption: ``loading from sql files`` - :lines: 1-39 + :start-after: # start-example + :end-before: # end-example :dedent: 4 Next Steps diff --git a/pyproject.toml b/pyproject.toml index b6e7631f..a568b67c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -287,7 +287,6 @@ exclude_lines = [ addopts = ["-q", "-ra"] asyncio_default_fixture_loop_scope = "function" asyncio_mode = "auto" -python_files = ["test_*.py", "quickstart_*.py", "usage_*.py"] filterwarnings = [ "ignore::DeprecationWarning:pkg_resources.*", "ignore:pkg_resources is deprecated as an API:DeprecationWarning", @@ -331,6 +330,7 @@ markers = [ "pymysql: marks tests using pymysql", "psqlpy: marks tests using psqlpy", ] +python_files = ["test_*.py", "quickstart_*.py", "usage_*.py"] testpaths = ["tests", "docs/examples/quickstart", "docs/examples/usage"] [tool.mypy] @@ -499,7 +499,7 @@ known-first-party = ["sqlspec", "tests"] split-on-trailing-comma = false [tool.ruff.lint.per-file-ignores] -"docs/**/*.*" = ["S", "B", "DTZ", "A", "TC", "ERA", "D", "RET", "PLW0127"] +"docs/**/*.*" = ["S", "B", "DTZ", "A", "TC", "ERA", "D", "RET", "PLW0127", "PLR2004"] "docs/examples/**" = ["T201"] "sqlspec/builder/mixins/**/*.*" = ["SLF001"] "sqlspec/extensions/adk/converters.py" = ["S403"] From 177419753e777869ae9d96d214d79d52fd5c461b Mon Sep 17 00:00:00 2001 From: Cody Fincher Date: Mon, 10 Nov 2025 04:50:58 +0000 Subject: [PATCH 18/18] docs: improve example clarity by adjusting indentation and adding no-upgrade directive --- docs/examples/quickstart/quickstart_5.py | 4 +++- docs/examples/usage/usage_configuration_1.py | 3 +-- docs/examples/usage/usage_configuration_10.py | 1 - docs/examples/usage/usage_configuration_11.py | 1 - docs/examples/usage/usage_configuration_12.py | 1 - docs/examples/usage/usage_configuration_13.py | 1 - docs/examples/usage/usage_configuration_14.py | 1 - docs/examples/usage/usage_configuration_15.py | 1 - docs/examples/usage/usage_configuration_16.py | 3 +-- docs/examples/usage/usage_configuration_17.py | 3 +-- docs/examples/usage/usage_configuration_18.py | 1 - docs/examples/usage/usage_configuration_19.py | 3 +-- docs/examples/usage/usage_configuration_2.py | 1 - docs/examples/usage/usage_configuration_20.py | 3 +-- docs/examples/usage/usage_configuration_21.py | 1 - docs/examples/usage/usage_configuration_22.py | 1 - docs/examples/usage/usage_configuration_23.py | 1 - docs/examples/usage/usage_configuration_24.py | 3 +-- docs/examples/usage/usage_configuration_25.py | 1 - docs/examples/usage/usage_configuration_26.py | 1 - docs/examples/usage/usage_configuration_27.py | 1 - docs/examples/usage/usage_configuration_28.py | 1 - docs/examples/usage/usage_configuration_29.py | 3 +-- docs/examples/usage/usage_configuration_3.py | 1 - docs/examples/usage/usage_configuration_30.py | 1 - docs/examples/usage/usage_configuration_4.py | 1 - docs/examples/usage/usage_configuration_5.py | 1 - docs/examples/usage/usage_configuration_6.py | 1 - docs/examples/usage/usage_configuration_7.py | 1 - docs/examples/usage/usage_configuration_8.py | 1 - docs/examples/usage/usage_configuration_9.py | 1 - docs/getting_started/quickstart.rst | 8 ++++++++ docs/usage/index.rst | 3 +++ 33 files changed, 21 insertions(+), 38 deletions(-) diff --git a/docs/examples/quickstart/quickstart_5.py b/docs/examples/quickstart/quickstart_5.py index d63227e5..4572e55e 100644 --- a/docs/examples/quickstart/quickstart_5.py +++ b/docs/examples/quickstart/quickstart_5.py @@ -42,7 +42,9 @@ async def seed_users(session: Any) -> None: """ ) await session.execute("TRUNCATE TABLE users") - await session.execute("INSERT INTO users (id, name, email) VALUES ($1, $2, $3)", 1, "Alice", "alice@example.com") + await session.execute( + "INSERT INTO users (id, name, email) VALUES ($1, $2, $3)", 1, "Alice", "alice@example.com" + ) db_manager = SQLSpec() db = db_manager.add_config(AsyncpgConfig(pool_config=pool_config())) diff --git a/docs/examples/usage/usage_configuration_1.py b/docs/examples/usage/usage_configuration_1.py index b8e3dd3d..71e154d0 100644 --- a/docs/examples/usage/usage_configuration_1.py +++ b/docs/examples/usage/usage_configuration_1.py @@ -16,6 +16,5 @@ def test_sqlite_memory_db() -> None: # Use the database with db_manager.provide_session(db) as session: result = session.execute("SELECT 1") - # end-example + # end-example assert result[0] == {"1": 1} - diff --git a/docs/examples/usage/usage_configuration_10.py b/docs/examples/usage/usage_configuration_10.py index 353c648b..59f934c7 100644 --- a/docs/examples/usage/usage_configuration_10.py +++ b/docs/examples/usage/usage_configuration_10.py @@ -16,4 +16,3 @@ def test_manual_pool() -> None: db = AsyncpgConfig(pool_instance=pool) # end-example assert db.pool_instance is pool - diff --git a/docs/examples/usage/usage_configuration_11.py b/docs/examples/usage/usage_configuration_11.py index 15fb4fae..6d6415ee 100644 --- a/docs/examples/usage/usage_configuration_11.py +++ b/docs/examples/usage/usage_configuration_11.py @@ -9,4 +9,3 @@ def test_thread_local_connections() -> None: config = SqliteConfig(pool_config={"database": "test.db"}) # end-example assert config.pool_config["database"] == "test.db" - diff --git a/docs/examples/usage/usage_configuration_12.py b/docs/examples/usage/usage_configuration_12.py index 6fb8694a..00f019ac 100644 --- a/docs/examples/usage/usage_configuration_12.py +++ b/docs/examples/usage/usage_configuration_12.py @@ -23,4 +23,3 @@ def test_basic_statement_config() -> None: # end-example assert config.statement_config.dialect == "postgres" assert config.statement_config.enable_parsing is True - diff --git a/docs/examples/usage/usage_configuration_13.py b/docs/examples/usage/usage_configuration_13.py index 65fcab21..b93a6e62 100644 --- a/docs/examples/usage/usage_configuration_13.py +++ b/docs/examples/usage/usage_configuration_13.py @@ -19,4 +19,3 @@ def test_parameter_style_config() -> None: statement_config = StatementConfig(dialect="postgres", parameter_config=param_config) # end-example assert statement_config.parameter_config.default_parameter_style == ParameterStyle.NUMERIC - diff --git a/docs/examples/usage/usage_configuration_14.py b/docs/examples/usage/usage_configuration_14.py index aa9a4478..2d59a620 100644 --- a/docs/examples/usage/usage_configuration_14.py +++ b/docs/examples/usage/usage_configuration_14.py @@ -23,4 +23,3 @@ def test_parameter_styles() -> None: assert qmark == ParameterStyle.QMARK assert numeric == ParameterStyle.NUMERIC assert named_colon == ParameterStyle.NAMED_COLON - diff --git a/docs/examples/usage/usage_configuration_15.py b/docs/examples/usage/usage_configuration_15.py index 49e9f0d6..c8c2cdab 100644 --- a/docs/examples/usage/usage_configuration_15.py +++ b/docs/examples/usage/usage_configuration_15.py @@ -23,4 +23,3 @@ def test_global_cache_config() -> None: # end-example assert cache_config.sql_cache_enabled is True assert cache_config.sql_cache_size == 1000 - diff --git a/docs/examples/usage/usage_configuration_16.py b/docs/examples/usage/usage_configuration_16.py index fc4885ae..3f257a91 100644 --- a/docs/examples/usage/usage_configuration_16.py +++ b/docs/examples/usage/usage_configuration_16.py @@ -22,6 +22,5 @@ def test_per_instance_cache_config() -> None: # Use the configured spec with db_manager.provide_session(db) as session: result = session.execute("SELECT 1") - # end-example + # end-example assert result is not None - diff --git a/docs/examples/usage/usage_configuration_17.py b/docs/examples/usage/usage_configuration_17.py index 7c1599da..21b6e178 100644 --- a/docs/examples/usage/usage_configuration_17.py +++ b/docs/examples/usage/usage_configuration_17.py @@ -23,10 +23,9 @@ def test_cache_statistics() -> None: # Get statistics stats = get_cache_statistics() - # end-example + # end-example assert isinstance(stats, dict) assert "multi_level" in stats # Log statistics (logs to configured logger) log_cache_stats() - diff --git a/docs/examples/usage/usage_configuration_18.py b/docs/examples/usage/usage_configuration_18.py index 79175045..ecd519d6 100644 --- a/docs/examples/usage/usage_configuration_18.py +++ b/docs/examples/usage/usage_configuration_18.py @@ -20,4 +20,3 @@ def test_clear_cache() -> None: stats_after = get_cache_statistics() assert isinstance(stats_after, dict) assert "multi_level" in stats_after - diff --git a/docs/examples/usage/usage_configuration_19.py b/docs/examples/usage/usage_configuration_19.py index 332ff45a..64698980 100644 --- a/docs/examples/usage/usage_configuration_19.py +++ b/docs/examples/usage/usage_configuration_19.py @@ -28,7 +28,6 @@ def test_binding_multiple_configs() -> None: sqlite_config = db_manager.get_config(sqlite_key) pg_config = db_manager.get_config(asyncpg_key) - # end-example + # end-example assert sqlite_config.pool_config["database"] == tmp.name assert pg_config.pool_config["dsn"] == os.getenv("SQLSPEC_USAGE_PG_DSN", "postgresql://localhost/db") - diff --git a/docs/examples/usage/usage_configuration_2.py b/docs/examples/usage/usage_configuration_2.py index 5f0b5704..c438621a 100644 --- a/docs/examples/usage/usage_configuration_2.py +++ b/docs/examples/usage/usage_configuration_2.py @@ -17,4 +17,3 @@ def test_sqlite_config_setup() -> None: ) # end-example assert config.pool_config["database"] == "myapp.db" - diff --git a/docs/examples/usage/usage_configuration_20.py b/docs/examples/usage/usage_configuration_20.py index 4c6c7f51..5dcf01c1 100644 --- a/docs/examples/usage/usage_configuration_20.py +++ b/docs/examples/usage/usage_configuration_20.py @@ -28,7 +28,6 @@ def test_named_bindings() -> None: cache_config = db_manager.get_config(cache_key) main_config = db_manager.get_config(main_key) - # end-example + # end-example assert cache_config.bind_key == "cache_db" assert main_config.bind_key == "main_db" - diff --git a/docs/examples/usage/usage_configuration_21.py b/docs/examples/usage/usage_configuration_21.py index 4cb87ee2..270a3952 100644 --- a/docs/examples/usage/usage_configuration_21.py +++ b/docs/examples/usage/usage_configuration_21.py @@ -26,4 +26,3 @@ def test_basic_migration_config() -> None: # end-example assert config.migration_config["script_location"] == "migrations" assert "litestar" in config.migration_config["include_extensions"] - diff --git a/docs/examples/usage/usage_configuration_22.py b/docs/examples/usage/usage_configuration_22.py index f15b398f..c5ea078e 100644 --- a/docs/examples/usage/usage_configuration_22.py +++ b/docs/examples/usage/usage_configuration_22.py @@ -23,4 +23,3 @@ def test_basic_migration_config() -> None: # end-example assert config.migration_config["script_location"] == "migrations" assert "litestar" in config.migration_config["include_extensions"] - diff --git a/docs/examples/usage/usage_configuration_23.py b/docs/examples/usage/usage_configuration_23.py index 1ec57e02..533dbde6 100644 --- a/docs/examples/usage/usage_configuration_23.py +++ b/docs/examples/usage/usage_configuration_23.py @@ -33,4 +33,3 @@ def test_extension_config() -> None: assert config.extension_config["litestar"]["commit_mode"] == "autocommit_include_redirect" assert config.extension_config["litestar"]["extra_commit_statuses"] == {201} assert config.extension_config["litestar"]["correlation_header"] == "x-request-id" - diff --git a/docs/examples/usage/usage_configuration_24.py b/docs/examples/usage/usage_configuration_24.py index 11146043..bfcbd7b0 100644 --- a/docs/examples/usage/usage_configuration_24.py +++ b/docs/examples/usage/usage_configuration_24.py @@ -32,10 +32,9 @@ def test_environment_based_configuration() -> None: } ) - # end-example + # end-example assert config.pool_config["host"] == "testhost" assert config.pool_config["port"] == 5433 assert config.pool_config["user"] == "testuser" assert config.pool_config["password"] == "testpass" assert config.pool_config["database"] == "testdb" - diff --git a/docs/examples/usage/usage_configuration_25.py b/docs/examples/usage/usage_configuration_25.py index 3faee88d..3286c59e 100644 --- a/docs/examples/usage/usage_configuration_25.py +++ b/docs/examples/usage/usage_configuration_25.py @@ -17,4 +17,3 @@ def test_connection_pooling_best_practice() -> None: assert config.pool_config["min_size"] == 10 assert config.pool_config["max_size"] == 20 assert config.supports_connection_pooling is True - diff --git a/docs/examples/usage/usage_configuration_26.py b/docs/examples/usage/usage_configuration_26.py index fcbb8253..ccb4a27b 100644 --- a/docs/examples/usage/usage_configuration_26.py +++ b/docs/examples/usage/usage_configuration_26.py @@ -13,4 +13,3 @@ def test_enable_caching_best_practice() -> None: # end-example assert statement_config.enable_caching is True assert statement_config.dialect == "postgres" - diff --git a/docs/examples/usage/usage_configuration_27.py b/docs/examples/usage/usage_configuration_27.py index 5b432006..2101d90c 100644 --- a/docs/examples/usage/usage_configuration_27.py +++ b/docs/examples/usage/usage_configuration_27.py @@ -23,4 +23,3 @@ def test_tune_pool_sizes_best_practice() -> None: io_bound_pool_config = {"min_size": 20, "max_size": 50} assert io_bound_pool_config["min_size"] == 20 assert io_bound_pool_config["max_size"] == 50 - diff --git a/docs/examples/usage/usage_configuration_28.py b/docs/examples/usage/usage_configuration_28.py index cc90214e..f862b184 100644 --- a/docs/examples/usage/usage_configuration_28.py +++ b/docs/examples/usage/usage_configuration_28.py @@ -16,4 +16,3 @@ def test_disable_security_checks_best_practice() -> None: ) # end-example assert statement_config.enable_validation is False - diff --git a/docs/examples/usage/usage_configuration_29.py b/docs/examples/usage/usage_configuration_29.py index dc2689b7..002a0e45 100644 --- a/docs/examples/usage/usage_configuration_29.py +++ b/docs/examples/usage/usage_configuration_29.py @@ -27,6 +27,5 @@ async def test_cleanup_resources_best_practice() -> None: await db_manager.close_all_pools() # Verify pools are closed - # end-example + # end-example assert db.pool_instance is None or not hasattr(db.pool_instance, "_pool") - diff --git a/docs/examples/usage/usage_configuration_3.py b/docs/examples/usage/usage_configuration_3.py index c39c2f99..77f9f165 100644 --- a/docs/examples/usage/usage_configuration_3.py +++ b/docs/examples/usage/usage_configuration_3.py @@ -14,4 +14,3 @@ def test_memory_databases() -> None: # Shared memory database shared_config = SqliteConfig(pool_config={"database": "file:memdb1?mode=memory&cache=shared", "uri": True}) assert shared_config.pool_config["database"] == "file:memdb1?mode=memory&cache=shared" - diff --git a/docs/examples/usage/usage_configuration_30.py b/docs/examples/usage/usage_configuration_30.py index 99ab752b..47e74a35 100644 --- a/docs/examples/usage/usage_configuration_30.py +++ b/docs/examples/usage/usage_configuration_30.py @@ -19,4 +19,3 @@ def test_telemetry_snapshot() -> None: # end-example assert "SqliteConfig.lifecycle.query_start" in snapshot _ = snapshot.get("storage_bridge.bytes_written", 0) - diff --git a/docs/examples/usage/usage_configuration_4.py b/docs/examples/usage/usage_configuration_4.py index 11acd553..909ff3cd 100644 --- a/docs/examples/usage/usage_configuration_4.py +++ b/docs/examples/usage/usage_configuration_4.py @@ -29,4 +29,3 @@ def test_asyncpg_config_setup() -> None: ) # end-example assert config.pool_config["host"] == host - diff --git a/docs/examples/usage/usage_configuration_5.py b/docs/examples/usage/usage_configuration_5.py index a1ab8b12..c617ace2 100644 --- a/docs/examples/usage/usage_configuration_5.py +++ b/docs/examples/usage/usage_configuration_5.py @@ -33,4 +33,3 @@ def test_psycopg_config_setup() -> None: ) # end-example assert config.pool_config is not None - diff --git a/docs/examples/usage/usage_configuration_6.py b/docs/examples/usage/usage_configuration_6.py index c5b520a1..4fd65624 100644 --- a/docs/examples/usage/usage_configuration_6.py +++ b/docs/examples/usage/usage_configuration_6.py @@ -20,4 +20,3 @@ def test_asyncmy_config_setup() -> None: ) # end-example assert config.pool_config["port"] == 3306 - diff --git a/docs/examples/usage/usage_configuration_7.py b/docs/examples/usage/usage_configuration_7.py index 2dcba283..13aed2c0 100644 --- a/docs/examples/usage/usage_configuration_7.py +++ b/docs/examples/usage/usage_configuration_7.py @@ -12,4 +12,3 @@ def test_duckdb_config_setup() -> None: persistent_config = DuckDBConfig(pool_config={"database": "analytics.duckdb", "read_only": False}) assert persistent_config.pool_config["read_only"] is False - diff --git a/docs/examples/usage/usage_configuration_8.py b/docs/examples/usage/usage_configuration_8.py index d559c4c4..39fdfcd9 100644 --- a/docs/examples/usage/usage_configuration_8.py +++ b/docs/examples/usage/usage_configuration_8.py @@ -20,4 +20,3 @@ def test_asyncpg_pool_setup() -> None: ) # end-example assert config.pool_config["min_size"] == 10 - diff --git a/docs/examples/usage/usage_configuration_9.py b/docs/examples/usage/usage_configuration_9.py index 38d56f1a..40407297 100644 --- a/docs/examples/usage/usage_configuration_9.py +++ b/docs/examples/usage/usage_configuration_9.py @@ -16,4 +16,3 @@ def test_pool_lifecycle() -> None: asyncpg_config = db_manager.get_config(asyncpg_key) # end-example assert asyncpg_config.pool_config["dsn"] == dsn - diff --git a/docs/getting_started/quickstart.rst b/docs/getting_started/quickstart.rst index e85c169f..87f64ec5 100644 --- a/docs/getting_started/quickstart.rst +++ b/docs/getting_started/quickstart.rst @@ -15,6 +15,7 @@ Let's start with the simplest possible example - executing a query and getting r :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: What's happening here? @@ -34,6 +35,7 @@ Let's create a table, insert some data, and query it: :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: Session Methods Cheat Sheet ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,6 +61,7 @@ The real power of SQLSpec comes from type-safe result mapping. Define your data :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: .. note:: @@ -75,6 +78,7 @@ SQLSpec supports async/await for non-blocking database operations. Here's the sa :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: The API is identical - just add ``await`` and use async config/drivers! @@ -89,6 +93,7 @@ One of SQLSpec's strengths is the consistent API across databases. Here's the sa :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: .. note:: @@ -118,6 +123,7 @@ Need to work with multiple databases? Register multiple configs: :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: Transaction Support @@ -132,6 +138,7 @@ boundaries explicitly and keep examples deterministic: :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: The snippet seeds data in a temporary SQLite database, intentionally triggers a failure, and uses ``contextlib.suppress`` so the docs stay readable while the companion test verifies @@ -152,6 +159,7 @@ For those who prefer programmatic query construction, SQLSpec includes an experi :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: .. warning:: diff --git a/docs/usage/index.rst b/docs/usage/index.rst index 06f5f4f2..3ad03838 100644 --- a/docs/usage/index.rst +++ b/docs/usage/index.rst @@ -57,6 +57,7 @@ Quick Reference :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: **Using the Query Builder** @@ -66,6 +67,7 @@ Quick Reference :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: **Loading from SQL Files** @@ -75,6 +77,7 @@ Quick Reference :start-after: # start-example :end-before: # end-example :dedent: 4 + :no-upgrade: Next Steps ----------