Skip to content

Commit 512fe6d

Browse files
authored
feat: implement a database configuration registry (#16)
Provides a database configuration manager class allowing a centralized place to configure and retrieve connections and pools from any number of databases.
1 parent 27c0ca0 commit 512fe6d

File tree

33 files changed

+1913
-557
lines changed

33 files changed

+1913
-557
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ repos:
1717
- id: mixed-line-ending
1818
- id: trailing-whitespace
1919
- repo: https://github.com/charliermarsh/ruff-pre-commit
20-
rev: "v0.9.1"
20+
rev: "v0.9.4"
2121
hooks:
2222
- id: ruff
2323
args: ["--fix"]
2424
- id: ruff-format
2525
- repo: https://github.com/codespell-project/codespell
26-
rev: v2.3.0
26+
rev: v2.4.1
2727
hooks:
2828
- id: codespell
2929
additional_dependencies:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ This list is not final. If you have a driver you'd like to see added, please ope
5252
| [`spanner`](https://googleapis.dev/python/spanner/latest/index.html) | Spanner | Sync | 🗓️ |
5353
| [`sqlserver`](https://docs.microsoft.com/en-us/sql/connect/python/pyodbc/python-sql-driver-for-pyodbc?view=sql-server-ver16) | SQL Server | Sync | 🗓️ |
5454
| [`mysql`](https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysql-connector-python.html) | MySQL | Sync | 🗓️ |
55-
| [`snowflake`](https://docs.snowflake.com) | MySQL | Sync | 🗓️ |
55+
| [`snowflake`](https://docs.snowflake.com) | Snowflake | Sync | 🗓️ |
5656

5757
## Proposed Project Structure
5858

docs/PYPI_README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ This list is not final. If you have a driver you'd like to see added, please ope
5252
| [`spanner`](https://googleapis.dev/python/spanner/latest/index.html) | Spanner | Sync | 🗓️ |
5353
| [`sqlserver`](https://docs.microsoft.com/en-us/sql/connect/python/pyodbc/python-sql-driver-for-pyodbc?view=sql-server-ver16) | SQL Server | Sync | 🗓️ |
5454
| [`mysql`](https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysql-connector-python.html) | MySQL | Sync | 🗓️ |
55-
| [`snowflake`](https://docs.snowflake.com) | MySQL | Sync | 🗓️ |
55+
| [`snowflake`](https://docs.snowflake.com) | Snowflake | Sync | 🗓️ |
5656

5757
## Proposed Project Structure
5858

sqlspec/__metadata__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from importlib.metadata import PackageNotFoundError, metadata, version
66

7-
__all__ = ["__project__", "__version__"]
7+
__all__ = ("__project__", "__version__")
88

99
try:
1010
__version__ = version("sqlspec")

sqlspec/adapters/adbc/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from dataclasses import dataclass
55
from typing import TYPE_CHECKING
66

7-
from sqlspec.base import GenericDatabaseConfig, NoPoolConfig
7+
from sqlspec.base import NoPoolSyncConfig
88
from sqlspec.typing import Empty, EmptyType
99

1010
if TYPE_CHECKING:
@@ -17,7 +17,7 @@
1717

1818

1919
@dataclass
20-
class AdbcDatabaseConfig(NoPoolConfig["Connection"], GenericDatabaseConfig):
20+
class AdbcDatabaseConfig(NoPoolSyncConfig["Connection"]):
2121
"""Configuration for ADBC connections.
2222
2323
This class provides configuration options for ADBC database connections using the

sqlspec/adapters/aiosqlite/config.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from __future__ import annotations
22

33
from contextlib import asynccontextmanager
4-
from dataclasses import dataclass
4+
from dataclasses import dataclass, field
55
from typing import TYPE_CHECKING, Any
66

7-
from sqlspec.base import GenericDatabaseConfig, NoPoolConfig
7+
from sqlspec.base import NoPoolSyncConfig
88
from sqlspec.exceptions import ImproperConfigurationError
99
from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
1010

@@ -19,7 +19,7 @@
1919

2020

2121
@dataclass
22-
class AiosqliteConfig(NoPoolConfig["Connection"], GenericDatabaseConfig):
22+
class AiosqliteConfig(NoPoolSyncConfig["Connection"]):
2323
"""Configuration for Aiosqlite database connections.
2424
2525
This class provides configuration options for Aiosqlite database connections, wrapping all parameters
@@ -28,28 +28,21 @@ class AiosqliteConfig(NoPoolConfig["Connection"], GenericDatabaseConfig):
2828
For details see: https://github.com/omnilib/aiosqlite/blob/main/aiosqlite/__init__.pyi
2929
"""
3030

31-
database: str
31+
database: str = field(default=":memory:")
3232
"""The path to the database file to be opened. Pass ":memory:" to open a connection to a database that resides in RAM instead of on disk."""
33-
34-
timeout: float | EmptyType = Empty
33+
timeout: float | EmptyType = field(default=Empty)
3534
"""How many seconds the connection should wait before raising an OperationalError when a table is locked. If another thread or process has acquired a shared lock, a wait for the specified timeout occurs."""
36-
37-
detect_types: int | EmptyType = Empty
35+
detect_types: int | EmptyType = field(default=Empty)
3836
"""Control whether and how data types are detected. It can be 0 (default) or a combination of PARSE_DECLTYPES and PARSE_COLNAMES."""
39-
40-
isolation_level: Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"] | None | EmptyType = Empty
37+
isolation_level: Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"] | None | EmptyType = field(default=Empty)
4138
"""The isolation_level of the connection. This can be None for autocommit mode or one of "DEFERRED", "IMMEDIATE" or "EXCLUSIVE"."""
42-
43-
check_same_thread: bool | EmptyType = Empty
39+
check_same_thread: bool | EmptyType = field(default=Empty)
4440
"""If True (default), ProgrammingError is raised if the database connection is used by a thread other than the one that created it. If False, the connection may be shared across multiple threads."""
45-
46-
factory: type[SQLite3Connection] | EmptyType = Empty
41+
factory: type[SQLite3Connection] | EmptyType = field(default=Empty)
4742
"""A custom Connection class factory. If given, must be a callable that returns a Connection instance."""
48-
49-
cached_statements: int | EmptyType = Empty
43+
cached_statements: int | EmptyType = field(default=Empty)
5044
"""The number of statements that SQLite will cache for this connection. The default is 128."""
51-
52-
uri: bool | EmptyType = Empty
45+
uri: bool | EmptyType = field(default=Empty)
5346
"""If set to True, database is interpreted as a URI with supported options."""
5447

5548
@property

sqlspec/adapters/asyncmy/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from asyncmy.connection import Connection
88
from asyncmy.pool import Pool
99

10-
from sqlspec.base import DatabaseConfigProtocol, GenericDatabaseConfig, GenericPoolConfig
10+
from sqlspec.base import AsyncDatabaseConfig, GenericPoolConfig
1111
from sqlspec.exceptions import ImproperConfigurationError
1212
from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
1313

@@ -106,7 +106,7 @@ def pool_config_dict(self) -> dict[str, Any]:
106106

107107

108108
@dataclass
109-
class AsyncMyConfig(DatabaseConfigProtocol[Connection, Pool], GenericDatabaseConfig):
109+
class AsyncMyConfig(AsyncDatabaseConfig[Connection, Pool]):
110110
"""Asyncmy Configuration."""
111111

112112
__is_async__ = True

sqlspec/adapters/asyncpg/config.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from contextlib import asynccontextmanager
44
from dataclasses import dataclass
5-
from typing import TYPE_CHECKING, TypeVar, Union
5+
from typing import TYPE_CHECKING, Any, TypeVar, Union
66

77
from asyncpg import Record
88
from asyncpg import create_pool as asyncpg_create_pool
@@ -11,14 +11,13 @@
1111
from typing_extensions import TypeAlias
1212

1313
from sqlspec._serialization import decode_json, encode_json
14-
from sqlspec.base import DatabaseConfigProtocol, GenericDatabaseConfig, GenericPoolConfig
14+
from sqlspec.base import AsyncDatabaseConfig, GenericPoolConfig
1515
from sqlspec.exceptions import ImproperConfigurationError
1616
from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
1717

1818
if TYPE_CHECKING:
19-
from asyncio import AbstractEventLoop
19+
from asyncio import AbstractEventLoop # pyright: ignore[reportAttributeAccessIssue]
2020
from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine
21-
from typing import Any
2221

2322

2423
__all__ = (
@@ -73,12 +72,9 @@ class AsyncPgPoolConfig(GenericPoolConfig):
7372

7473

7574
@dataclass
76-
class AsyncPgConfig(DatabaseConfigProtocol[PgConnection, Pool], GenericDatabaseConfig):
75+
class AsyncPgConfig(AsyncDatabaseConfig[PgConnection, Pool]):
7776
"""Asyncpg Configuration."""
7877

79-
__is_async__ = True
80-
__supports_connection_pooling__ = True
81-
8278
pool_config: AsyncPgPoolConfig | None = None
8379
"""Asyncpg Pool configuration"""
8480
json_deserializer: Callable[[str], Any] = decode_json

sqlspec/adapters/duckdb/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from duckdb import DuckDBPyConnection
88

9-
from sqlspec.base import GenericDatabaseConfig, NoPoolConfig
9+
from sqlspec.base import NoPoolSyncConfig
1010
from sqlspec.exceptions import ImproperConfigurationError
1111
from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
1212

@@ -66,7 +66,7 @@ def from_dict(cls, name: str, config: dict[str, Any] | bool | None = None) -> Ex
6666

6767

6868
@dataclass
69-
class DuckDBConfig(NoPoolConfig[DuckDBPyConnection], GenericDatabaseConfig):
69+
class DuckDBConfig(NoPoolSyncConfig[DuckDBPyConnection]):
7070
"""Configuration for DuckDB database connections.
7171
7272
This class provides configuration options for DuckDB database connections, wrapping all parameters

sqlspec/adapters/oracledb/config/_asyncio.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
from dataclasses import dataclass
55
from typing import TYPE_CHECKING
66

7-
from oracledb import create_pool_async as oracledb_create_pool
7+
from oracledb import create_pool_async as oracledb_create_pool # pyright: ignore[reportUnknownVariableType]
88
from oracledb.connection import AsyncConnection
99
from oracledb.pool import AsyncConnectionPool
1010

1111
from sqlspec.adapters.oracledb.config._common import (
12-
OracleGenericDatabaseConfig,
1312
OracleGenericPoolConfig,
1413
)
14+
from sqlspec.base import AsyncDatabaseConfig
1515
from sqlspec.exceptions import ImproperConfigurationError
1616
from sqlspec.typing import dataclass_to_dict
1717

@@ -31,11 +31,18 @@ class OracleAsyncPoolConfig(OracleGenericPoolConfig[AsyncConnection, AsyncConnec
3131

3232

3333
@dataclass
34-
class OracleAsyncDatabaseConfig(OracleGenericDatabaseConfig[AsyncConnection, AsyncConnectionPool]):
35-
"""Async Oracle database Configuration."""
34+
class OracleAsyncDatabaseConfig(AsyncDatabaseConfig[AsyncConnection, AsyncConnectionPool]):
35+
"""Oracle Async database Configuration.
3636
37-
__is_async__ = True
38-
__supports_connection_pooling__ = True
37+
This class provides the base configuration for Oracle database connections, extending
38+
the generic database configuration with Oracle-specific settings. It supports both
39+
thin and thick modes of the python-oracledb driver.([1](https://python-oracledb.readthedocs.io/en/latest/index.html))
40+
41+
The configuration supports all standard Oracle connection parameters and can be used
42+
with both synchronous and asynchronous connections. It includes support for features
43+
like Oracle Wallet, external authentication, connection pooling, and advanced security
44+
options.([2](https://python-oracledb.readthedocs.io/en/latest/user_guide/tuning.html))
45+
"""
3946

4047
pool_config: OracleAsyncPoolConfig | None = None
4148
"""Oracle Pool configuration"""
@@ -94,5 +101,5 @@ async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[
94101
A connection instance.
95102
"""
96103
db_pool = await self.provide_pool(*args, **kwargs)
97-
async with db_pool.acquire() as connection:
104+
async with db_pool.acquire() as connection: # pyright: ignore[reportUnknownMemberType]
98105
yield connection

0 commit comments

Comments
 (0)