sqlspec 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (42) hide show
  1. sqlspec/__metadata__.py +1 -1
  2. sqlspec/_serialization.py +1 -1
  3. sqlspec/_typing.py +112 -0
  4. sqlspec/adapters/adbc/__init__.py +0 -0
  5. sqlspec/adapters/adbc/config.py +54 -0
  6. sqlspec/adapters/aiosqlite/__init__.py +3 -0
  7. sqlspec/adapters/aiosqlite/config.py +112 -0
  8. sqlspec/adapters/asyncmy/__init__.py +3 -0
  9. sqlspec/adapters/asyncmy/config.py +194 -0
  10. sqlspec/adapters/asyncpg/__init__.py +0 -0
  11. sqlspec/adapters/asyncpg/config.py +155 -0
  12. sqlspec/adapters/duckdb/__init__.py +0 -0
  13. sqlspec/adapters/duckdb/config.py +101 -0
  14. sqlspec/adapters/oracledb/__init__.py +13 -0
  15. sqlspec/adapters/oracledb/config/__init__.py +9 -0
  16. sqlspec/adapters/oracledb/config/_asyncio.py +98 -0
  17. sqlspec/adapters/oracledb/config/_common.py +151 -0
  18. sqlspec/adapters/oracledb/config/_sync.py +102 -0
  19. sqlspec/adapters/psycopg/__init__.py +0 -0
  20. sqlspec/adapters/psycopg/config/__init__.py +9 -0
  21. sqlspec/adapters/psycopg/config/_async.py +84 -0
  22. sqlspec/adapters/psycopg/config/_common.py +72 -0
  23. sqlspec/adapters/psycopg/config/_sync.py +84 -0
  24. sqlspec/adapters/sqlite/__init__.py +0 -0
  25. sqlspec/adapters/sqlite/config.py +109 -0
  26. sqlspec/config.py +16 -0
  27. sqlspec/exceptions.py +29 -0
  28. sqlspec/extensions/__init__.py +0 -0
  29. sqlspec/extensions/litestar/__init__.py +0 -0
  30. sqlspec/extensions/litestar/plugin.py +34 -0
  31. sqlspec/filters.py +33 -28
  32. sqlspec/typing.py +287 -0
  33. sqlspec/utils/dataclass.py +11 -3
  34. sqlspec/{types → utils}/empty.py +1 -1
  35. sqlspec-0.3.0.dist-info/METADATA +84 -0
  36. sqlspec-0.3.0.dist-info/RECORD +42 -0
  37. {sqlspec-0.1.0.dist-info → sqlspec-0.3.0.dist-info}/WHEEL +1 -1
  38. sqlspec-0.3.0.dist-info/licenses/NOTICE +29 -0
  39. sqlspec/types/protocols.py +0 -117
  40. sqlspec-0.1.0.dist-info/METADATA +0 -25
  41. sqlspec-0.1.0.dist-info/RECORD +0 -14
  42. /sqlspec/{types → adapters}/__init__.py +0 -0
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from sqlspec.config import GenericDatabaseConfig
8
+ from sqlspec.exceptions import ImproperConfigurationError
9
+ from sqlspec.utils.dataclass import simple_asdict
10
+ from sqlspec.utils.empty import Empty, EmptyType
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Generator
14
+
15
+ from duckdb import DuckDBPyConnection
16
+
17
+ __all__ = ("DuckDBConfig",)
18
+
19
+
20
+ @dataclass
21
+ class DuckDBConfig(GenericDatabaseConfig):
22
+ """Configuration for DuckDB database connections.
23
+
24
+ This class provides configuration options for DuckDB database connections, wrapping all parameters
25
+ available to duckdb.connect().
26
+
27
+ For details see: https://duckdb.org/docs/api/python/overview#connection-options
28
+ """
29
+
30
+ database: str | EmptyType = Empty
31
+ """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. If not specified, an in-memory database will be created."""
32
+
33
+ read_only: bool | EmptyType = Empty
34
+ """If True, the database will be opened in read-only mode. This is required if multiple processes want to access the same database file at the same time."""
35
+
36
+ config: dict[str, Any] | EmptyType = Empty
37
+ """A dictionary of configuration options to be passed to DuckDB. These can include settings like 'access_mode', 'max_memory', 'threads', etc.
38
+
39
+ For details see: https://duckdb.org/docs/api/python/overview#connection-options
40
+ """
41
+
42
+ @property
43
+ def connection_config_dict(self) -> dict[str, Any]:
44
+ """Return the connection configuration as a dict.
45
+
46
+ Returns:
47
+ A string keyed dict of config kwargs for the duckdb.connect() function.
48
+ """
49
+ config = simple_asdict(self, exclude_empty=True, convert_nested=False)
50
+ if not config.get("database"):
51
+ config["database"] = ":memory:"
52
+ return config
53
+
54
+ def create_connection(self) -> DuckDBPyConnection:
55
+ """Create and return a new database connection.
56
+
57
+ Returns:
58
+ A new DuckDB connection instance.
59
+
60
+ Raises:
61
+ ImproperConfigurationError: If the connection could not be established.
62
+ """
63
+ import duckdb
64
+
65
+ try:
66
+ return duckdb.connect(**self.connection_config_dict)
67
+ except Exception as e:
68
+ msg = f"Could not configure the DuckDB connection. Error: {e!s}"
69
+ raise ImproperConfigurationError(msg) from e
70
+
71
+ @contextmanager
72
+ def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
73
+ """Manage the lifecycle of a database connection.
74
+
75
+ Yields:
76
+ None
77
+
78
+ Raises:
79
+ ImproperConfigurationError: If the connection could not be established.
80
+ """
81
+ connection = self.create_connection()
82
+ try:
83
+ yield
84
+ finally:
85
+ connection.close()
86
+
87
+ @contextmanager
88
+ def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[DuckDBPyConnection, None, None]:
89
+ """Create and provide a database connection.
90
+
91
+ Yields:
92
+ A DuckDB connection instance.
93
+
94
+ Raises:
95
+ ImproperConfigurationError: If the connection could not be established.
96
+ """
97
+ connection = self.create_connection()
98
+ try:
99
+ yield connection
100
+ finally:
101
+ connection.close()
@@ -0,0 +1,13 @@
1
+ from .config import (
2
+ OracleAsyncDatabaseConfig,
3
+ OracleAsyncPoolConfig,
4
+ OracleSyncDatabaseConfig,
5
+ OracleSyncPoolConfig,
6
+ )
7
+
8
+ __all__ = (
9
+ "OracleAsyncDatabaseConfig",
10
+ "OracleAsyncPoolConfig",
11
+ "OracleSyncDatabaseConfig",
12
+ "OracleSyncPoolConfig",
13
+ )
@@ -0,0 +1,9 @@
1
+ from sqlspec.adapters.oracledb.config._asyncio import OracleAsyncDatabaseConfig, OracleAsyncPoolConfig
2
+ from sqlspec.adapters.oracledb.config._sync import OracleSyncDatabaseConfig, OracleSyncPoolConfig
3
+
4
+ __all__ = (
5
+ "OracleAsyncDatabaseConfig",
6
+ "OracleAsyncPoolConfig",
7
+ "OracleSyncDatabaseConfig",
8
+ "OracleSyncPoolConfig",
9
+ )
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING
6
+
7
+ from oracledb import create_pool_async as oracledb_create_pool
8
+ from oracledb.connection import AsyncConnection
9
+ from oracledb.pool import AsyncConnectionPool
10
+
11
+ from sqlspec.adapters.oracledb.config._common import (
12
+ OracleGenericDatabaseConfig,
13
+ OracleGenericPoolConfig,
14
+ )
15
+ from sqlspec.exceptions import ImproperConfigurationError
16
+ from sqlspec.utils.dataclass import simple_asdict
17
+
18
+ if TYPE_CHECKING:
19
+ from collections.abc import AsyncGenerator, Awaitable
20
+ from typing import Any
21
+
22
+ __all__ = (
23
+ "OracleAsyncDatabaseConfig",
24
+ "OracleAsyncPoolConfig",
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class OracleAsyncPoolConfig(OracleGenericPoolConfig[AsyncConnectionPool, AsyncConnection]):
30
+ """Async Oracle Pool Config"""
31
+
32
+
33
+ @dataclass
34
+ class OracleAsyncDatabaseConfig(OracleGenericDatabaseConfig[AsyncConnectionPool, AsyncConnection]):
35
+ """Async Oracle database Configuration."""
36
+
37
+ pool_config: OracleAsyncPoolConfig | None = None
38
+ """Oracle Pool configuration"""
39
+
40
+ @property
41
+ def pool_config_dict(self) -> dict[str, Any]:
42
+ """Return the pool configuration as a dict.
43
+
44
+ Returns:
45
+ A string keyed dict of config kwargs for the Asyncpg :func:`create_pool <oracledb.pool.create_pool>`
46
+ function.
47
+ """
48
+ if self.pool_config is not None:
49
+ return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
50
+ msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
51
+ raise ImproperConfigurationError(msg)
52
+
53
+ async def create_pool(self) -> AsyncConnectionPool:
54
+ """Return a pool. If none exists yet, create one.
55
+
56
+ Returns:
57
+ Getter that returns the pool instance used by the plugin.
58
+ """
59
+ if self.pool_instance is not None:
60
+ return self.pool_instance
61
+
62
+ if self.pool_config is None:
63
+ msg = "One of 'pool_config' or 'pool_instance' must be provided."
64
+ raise ImproperConfigurationError(msg)
65
+
66
+ pool_config = self.pool_config_dict
67
+ self.pool_instance = oracledb_create_pool(**pool_config)
68
+ if self.pool_instance is None:
69
+ msg = "Could not configure the 'pool_instance'. Please check your configuration."
70
+ raise ImproperConfigurationError(msg)
71
+ return self.pool_instance
72
+
73
+ @asynccontextmanager
74
+ async def lifespan(self, *args: Any, **kwargs) -> AsyncGenerator[None, None]:
75
+ db_pool = await self.create_pool()
76
+ try:
77
+ yield
78
+ finally:
79
+ await db_pool.close(force=True)
80
+
81
+ def provide_pool(self, *args: Any, **kwargs) -> Awaitable[AsyncConnectionPool]:
82
+ """Create a pool instance.
83
+
84
+ Returns:
85
+ A Pool instance.
86
+ """
87
+ return self.create_pool()
88
+
89
+ @asynccontextmanager
90
+ async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncConnection, None]:
91
+ """Create a connection instance.
92
+
93
+ Returns:
94
+ A connection instance.
95
+ """
96
+ db_pool = await self.provide_pool(*args, **kwargs)
97
+ async with db_pool.acquire() as connection:
98
+ yield connection
@@ -0,0 +1,151 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Generic, TypeVar
5
+
6
+ from oracledb import ConnectionPool
7
+
8
+ from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
9
+ from sqlspec.utils.empty import Empty
10
+
11
+ if TYPE_CHECKING:
12
+ import ssl
13
+ from collections.abc import Callable
14
+ from typing import Any
15
+
16
+ from oracledb import AuthMode, ConnectParams, Purity
17
+ from oracledb.connection import AsyncConnection, Connection
18
+ from oracledb.pool import AsyncConnectionPool, ConnectionPool
19
+
20
+ from sqlspec.utils.empty import EmptyType
21
+
22
+ __all__ = (
23
+ "OracleGenericDatabaseConfig",
24
+ "OracleGenericPoolConfig",
25
+ )
26
+
27
+
28
+ T = TypeVar("T")
29
+
30
+ ConnectionT = TypeVar("ConnectionT", bound="Connection | AsyncConnection")
31
+ PoolT = TypeVar("PoolT", bound="ConnectionPool | AsyncConnectionPool")
32
+
33
+
34
+ @dataclass
35
+ class OracleGenericPoolConfig(Generic[PoolT, ConnectionT], GenericPoolConfig):
36
+ """Configuration for Oracle database connection pools.
37
+
38
+ This class provides configuration options for both synchronous and asynchronous Oracle
39
+ database connection pools. It supports all standard Oracle connection parameters and pool-specific
40
+ settings.([1](https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html))
41
+ """
42
+
43
+ conn_class: type[ConnectionT] | EmptyType = Empty
44
+ """The connection class to use (Connection or AsyncConnection)"""
45
+ dsn: str | EmptyType = Empty
46
+ """Connection string for the database """
47
+ pool: PoolT | EmptyType = Empty
48
+ """Existing pool instance to use"""
49
+ params: ConnectParams | EmptyType = Empty
50
+ """Connection parameters object"""
51
+ user: str | EmptyType = Empty
52
+ """Username for database authentication"""
53
+ proxy_user: str | EmptyType = Empty
54
+ """Name of the proxy user to connect through"""
55
+ password: str | EmptyType = Empty
56
+ """Password for database authentication"""
57
+ newpassword: str | EmptyType = Empty
58
+ """New password for password change operations"""
59
+ wallet_password: str | EmptyType = Empty
60
+ """Password for accessing Oracle Wallet"""
61
+ access_token: str | tuple | Callable | EmptyType = Empty
62
+ """Token for token-based authentication"""
63
+ host: str | EmptyType = Empty
64
+ """Database server hostname"""
65
+ port: int | EmptyType = Empty
66
+ """Database server port number"""
67
+ protocol: str | EmptyType = Empty
68
+ """Network protocol (TCP or TCPS)"""
69
+ https_proxy: str | EmptyType = Empty
70
+ """HTTPS proxy server address"""
71
+ https_proxy_port: int | EmptyType = Empty
72
+ """HTTPS proxy server port"""
73
+ service_name: str | EmptyType = Empty
74
+ """Oracle service name"""
75
+ sid: str | EmptyType = Empty
76
+ """Oracle System ID (SID)"""
77
+ server_type: str | EmptyType = Empty
78
+ """Server type (dedicated, shared, pooled, or drcp)"""
79
+ cclass: str | EmptyType = Empty
80
+ """Connection class for database resident connection pooling"""
81
+ purity: Purity | EmptyType = Empty
82
+ """Session purity (NEW, SELF, or DEFAULT)"""
83
+ expire_time: int | EmptyType = Empty
84
+ """Time in minutes after which idle connections are closed"""
85
+ retry_count: int | EmptyType = Empty
86
+ """Number of attempts to connect"""
87
+ retry_delay: int | EmptyType = Empty
88
+ """Time in seconds between connection attempts"""
89
+ tcp_connect_timeout: float | EmptyType = Empty
90
+ """Timeout for establishing TCP connections"""
91
+ ssl_server_dn_match: bool | EmptyType = Empty
92
+ """If True, verify server certificate DN"""
93
+ ssl_server_cert_dn: str | EmptyType = Empty
94
+ """Expected server certificate DN"""
95
+ wallet_location: str | EmptyType = Empty
96
+ """Location of Oracle Wallet"""
97
+ events: bool | EmptyType = Empty
98
+ """If True, enables Oracle events for FAN and RLB"""
99
+ externalauth: bool | EmptyType = Empty
100
+ """If True, uses external authentication"""
101
+ mode: AuthMode | EmptyType = Empty
102
+ """Session mode (SYSDBA, SYSOPER, etc.)"""
103
+ disable_oob: bool | EmptyType = Empty
104
+ """If True, disables Oracle out-of-band breaks"""
105
+ stmtcachesize: int | EmptyType = Empty
106
+ """Size of the statement cache"""
107
+ edition: str | EmptyType = Empty
108
+ """Edition name for edition-based redefinition"""
109
+ tag: str | EmptyType = Empty
110
+ """Connection pool tag"""
111
+ matchanytag: bool | EmptyType = Empty
112
+ """If True, allows connections with different tags"""
113
+ config_dir: str | EmptyType = Empty
114
+ """Directory containing Oracle configuration files"""
115
+ appcontext: list | EmptyType = Empty
116
+ """Application context list"""
117
+ shardingkey: list | EmptyType = Empty
118
+ """Sharding key list"""
119
+ supershardingkey: list | EmptyType = Empty
120
+ """Super sharding key list"""
121
+ debug_jdwp: str | EmptyType = Empty
122
+ """JDWP debugging string"""
123
+ connection_id_prefix: str | EmptyType = Empty
124
+ """Prefix for connection identifiers"""
125
+ ssl_context: Any | EmptyType = Empty
126
+ """SSL context for TCPS connections"""
127
+ sdu: int | EmptyType = Empty
128
+ """Session data unit size"""
129
+ pool_boundary: str | EmptyType = Empty
130
+ """Connection pool boundary (statement or transaction)"""
131
+ use_tcp_fast_open: bool | EmptyType = Empty
132
+ """If True, enables TCP Fast Open"""
133
+ ssl_version: ssl.TLSVersion | EmptyType = Empty
134
+ """SSL/TLS protocol version"""
135
+ handle: int | EmptyType = Empty
136
+ """Oracle service context handle"""
137
+
138
+
139
+ @dataclass
140
+ class OracleGenericDatabaseConfig(Generic[PoolT, ConnectionT], GenericDatabaseConfig):
141
+ """Oracle database Configuration.
142
+
143
+ This class provides the base configuration for Oracle database connections, extending
144
+ the generic database configuration with Oracle-specific settings. It supports both
145
+ thin and thick modes of the python-oracledb driver.([1](https://python-oracledb.readthedocs.io/en/latest/index.html))
146
+
147
+ The configuration supports all standard Oracle connection parameters and can be used
148
+ with both synchronous and asynchronous connections. It includes support for features
149
+ like Oracle Wallet, external authentication, connection pooling, and advanced security
150
+ options.([2](https://python-oracledb.readthedocs.io/en/latest/user_guide/tuning.html))
151
+ """
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING
6
+
7
+ from oracledb import create_pool as oracledb_create_pool
8
+ from oracledb.connection import Connection
9
+ from oracledb.pool import ConnectionPool
10
+
11
+ from sqlspec.adapters.oracledb.config._common import (
12
+ OracleGenericDatabaseConfig,
13
+ OracleGenericPoolConfig,
14
+ )
15
+ from sqlspec.exceptions import ImproperConfigurationError
16
+ from sqlspec.utils.dataclass import simple_asdict
17
+
18
+ if TYPE_CHECKING:
19
+ from collections.abc import Generator
20
+ from typing import Any
21
+
22
+ __all__ = (
23
+ "OracleSyncDatabaseConfig",
24
+ "OracleSyncPoolConfig",
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class OracleSyncPoolConfig(OracleGenericPoolConfig[ConnectionPool, Connection]):
30
+ """Sync Oracle Pool Config"""
31
+
32
+
33
+ @dataclass
34
+ class OracleSyncDatabaseConfig(OracleGenericDatabaseConfig[ConnectionPool, Connection]):
35
+ """Oracle database Configuration."""
36
+
37
+ pool_config: OracleSyncPoolConfig | None = None
38
+ """Oracle Pool configuration"""
39
+ pool_instance: ConnectionPool | None = None
40
+ """Optional pool to use.
41
+
42
+ If set, the plugin will use the provided pool rather than instantiate one.
43
+ """
44
+
45
+ @property
46
+ def pool_config_dict(self) -> dict[str, Any]:
47
+ """Return the pool configuration as a dict.
48
+
49
+ Returns:
50
+ A string keyed dict of config kwargs for the Asyncpg :func:`create_pool <oracledb.pool.create_pool>`
51
+ function.
52
+ """
53
+ if self.pool_config:
54
+ return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
55
+ msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
56
+ raise ImproperConfigurationError(msg)
57
+
58
+ def create_pool(self) -> ConnectionPool:
59
+ """Return a pool. If none exists yet, create one.
60
+
61
+ Returns:
62
+ Getter that returns the pool instance used by the plugin.
63
+ """
64
+ if self.pool_instance is not None:
65
+ return self.pool_instance
66
+
67
+ if self.pool_config is None:
68
+ msg = "One of 'pool_config' or 'pool_instance' must be provided."
69
+ raise ImproperConfigurationError(msg)
70
+
71
+ pool_config = self.pool_config_dict
72
+ self.pool_instance = oracledb_create_pool(**pool_config)
73
+ if self.pool_instance is None:
74
+ msg = "Could not configure the 'pool_instance'. Please check your configuration."
75
+ raise ImproperConfigurationError(msg)
76
+ return self.pool_instance
77
+
78
+ def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
79
+ db_pool = self.create_pool()
80
+ try:
81
+ yield
82
+ finally:
83
+ db_pool.close()
84
+
85
+ def provide_pool(self, *args: Any, **kwargs: Any) -> ConnectionPool:
86
+ """Create a pool instance.
87
+
88
+ Returns:
89
+ A Pool instance.
90
+ """
91
+ return self.create_pool()
92
+
93
+ @contextmanager
94
+ def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[Connection, None, None]:
95
+ """Create a connection instance.
96
+
97
+ Returns:
98
+ A connection instance.
99
+ """
100
+ db_pool = self.provide_pool(*args, **kwargs)
101
+ with db_pool.acquire() as connection:
102
+ yield connection
File without changes
@@ -0,0 +1,9 @@
1
+ from ._async import PsycoPgAsyncDatabaseConfig, PsycoPgAsyncPoolConfig
2
+ from ._sync import PsycoPgSyncDatabaseConfig, PsycoPgSyncPoolConfig
3
+
4
+ __all__ = (
5
+ "PsycoPgAsyncDatabaseConfig",
6
+ "PsycoPgAsyncPoolConfig",
7
+ "PsycoPgSyncDatabaseConfig",
8
+ "PsycoPgSyncPoolConfig",
9
+ )
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING
6
+
7
+ from psycopg import AsyncConnection
8
+ from psycopg_pool import AsyncConnectionPool
9
+
10
+ from sqlspec.adapters.psycopg.config._common import (
11
+ PsycoPgGenericDatabaseConfig,
12
+ PsycoPgGenericPoolConfig,
13
+ )
14
+ from sqlspec.exceptions import ImproperConfigurationError
15
+ from sqlspec.utils.dataclass import simple_asdict
16
+
17
+ if TYPE_CHECKING:
18
+ from collections.abc import AsyncGenerator, Awaitable
19
+ from typing import Any
20
+
21
+
22
+ __all__ = (
23
+ "PsycoPgAsyncDatabaseConfig",
24
+ "PsycoPgAsyncPoolConfig",
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class PsycoPgAsyncPoolConfig(PsycoPgGenericPoolConfig[AsyncConnectionPool, AsyncConnection]):
30
+ """Async Psycopg Pool Config"""
31
+
32
+
33
+ @dataclass
34
+ class PsycoPgAsyncDatabaseConfig(PsycoPgGenericDatabaseConfig[AsyncConnectionPool, AsyncConnection]):
35
+ """Async Psycopg database Configuration."""
36
+
37
+ pool_config: PsycoPgAsyncPoolConfig | None = None
38
+ """Psycopg Pool configuration"""
39
+ pool_instance: AsyncConnectionPool | None = None
40
+ """Optional pool to use"""
41
+
42
+ @property
43
+ def pool_config_dict(self) -> dict[str, Any]:
44
+ """Return the pool configuration as a dict."""
45
+ if self.pool_config:
46
+ return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
47
+ msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
48
+ raise ImproperConfigurationError(msg)
49
+
50
+ async def create_pool(self) -> AsyncConnectionPool:
51
+ """Create and return a connection pool."""
52
+ if self.pool_instance is not None:
53
+ return self.pool_instance
54
+
55
+ if self.pool_config is None:
56
+ msg = "One of 'pool_config' or 'pool_instance' must be provided."
57
+ raise ImproperConfigurationError(msg)
58
+
59
+ pool_config = self.pool_config_dict
60
+ self.pool_instance = AsyncConnectionPool(**pool_config)
61
+ if self.pool_instance is None:
62
+ msg = "Could not configure the 'pool_instance'. Please check your configuration." # type: ignore[unreachable]
63
+ raise ImproperConfigurationError(msg)
64
+ return self.pool_instance
65
+
66
+ @asynccontextmanager
67
+ async def lifespan(self, *args: Any, **kwargs: Any) -> AsyncGenerator[None, None]:
68
+ """Manage the lifecycle of the connection pool."""
69
+ pool = await self.create_pool()
70
+ try:
71
+ yield
72
+ finally:
73
+ await pool.close()
74
+
75
+ def provide_pool(self, *args: Any, **kwargs: Any) -> Awaitable[AsyncConnectionPool]:
76
+ """Create and return a connection pool."""
77
+ return self.create_pool()
78
+
79
+ @asynccontextmanager
80
+ async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[AsyncConnection, None]:
81
+ """Create and provide a database connection."""
82
+ pool = await self.provide_pool(*args, **kwargs)
83
+ async with pool.connection() as connection:
84
+ yield connection
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Generic, TypeVar
5
+
6
+ from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
7
+ from sqlspec.utils.empty import Empty
8
+
9
+ if TYPE_CHECKING:
10
+ from collections.abc import Callable
11
+ from typing import Any
12
+
13
+ from psycopg import AsyncConnection, Connection
14
+ from psycopg_pool import AsyncConnectionPool, ConnectionPool
15
+
16
+ from sqlspec.utils.empty import EmptyType
17
+
18
+ __all__ = (
19
+ "PsycoPgGenericDatabaseConfig",
20
+ "PsycoPgGenericPoolConfig",
21
+ )
22
+
23
+
24
+ ConnectionT = TypeVar("ConnectionT", bound="Connection | AsyncConnection")
25
+ PoolT = TypeVar("PoolT", bound="ConnectionPool | AsyncConnectionPool")
26
+
27
+
28
+ @dataclass
29
+ class PsycoPgGenericPoolConfig(Generic[PoolT, ConnectionT], GenericPoolConfig):
30
+ """Configuration for Psycopg connection pools.
31
+
32
+ This class provides configuration options for both synchronous and asynchronous Psycopg
33
+ database connection pools. It supports all standard Psycopg connection parameters and pool-specific
34
+ settings.([1](https://www.psycopg.org/psycopg3/docs/api/pool.html))
35
+ """
36
+
37
+ conninfo: str | EmptyType = Empty
38
+ """Connection string in libpq format"""
39
+ kwargs: dict[str, Any] | EmptyType = Empty
40
+ """Additional connection parameters"""
41
+ min_size: int | EmptyType = Empty
42
+ """Minimum number of connections in the pool"""
43
+ max_size: int | EmptyType = Empty
44
+ """Maximum number of connections in the pool"""
45
+ name: str | EmptyType = Empty
46
+ """Name of the connection pool"""
47
+ timeout: float | EmptyType = Empty
48
+ """Timeout for acquiring connections"""
49
+ max_waiting: int | EmptyType = Empty
50
+ """Maximum number of waiting clients"""
51
+ max_lifetime: float | EmptyType = Empty
52
+ """Maximum connection lifetime"""
53
+ max_idle: float | EmptyType = Empty
54
+ """Maximum idle time for connections"""
55
+ reconnect_timeout: float | EmptyType = Empty
56
+ """Time between reconnection attempts"""
57
+ num_workers: int | EmptyType = Empty
58
+ """Number of background workers"""
59
+ configure: Callable[[ConnectionT], None] | EmptyType = Empty
60
+ """Callback to configure new connections"""
61
+
62
+
63
+ @dataclass
64
+ class PsycoPgGenericDatabaseConfig(Generic[PoolT, ConnectionT], GenericDatabaseConfig):
65
+ """Psycopg database Configuration.
66
+
67
+ This class provides the base configuration for Psycopg database connections, extending
68
+ the generic database configuration with Psycopg-specific settings.([1](https://www.psycopg.org/psycopg3/docs/api/connections.html))
69
+
70
+ The configuration supports all standard Psycopg connection parameters and can be used
71
+ with both synchronous and asynchronous connections.([2](https://www.psycopg.org/psycopg3/docs/api/connections.html))
72
+ """