sqlspec 0.7.1__py3-none-any.whl → 0.9.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 (54) hide show
  1. sqlspec/__init__.py +15 -0
  2. sqlspec/_serialization.py +16 -2
  3. sqlspec/_typing.py +40 -7
  4. sqlspec/adapters/adbc/__init__.py +7 -0
  5. sqlspec/adapters/adbc/config.py +183 -17
  6. sqlspec/adapters/adbc/driver.py +392 -0
  7. sqlspec/adapters/aiosqlite/__init__.py +5 -1
  8. sqlspec/adapters/aiosqlite/config.py +24 -6
  9. sqlspec/adapters/aiosqlite/driver.py +264 -0
  10. sqlspec/adapters/asyncmy/__init__.py +7 -2
  11. sqlspec/adapters/asyncmy/config.py +71 -11
  12. sqlspec/adapters/asyncmy/driver.py +246 -0
  13. sqlspec/adapters/asyncpg/__init__.py +9 -0
  14. sqlspec/adapters/asyncpg/config.py +102 -25
  15. sqlspec/adapters/asyncpg/driver.py +444 -0
  16. sqlspec/adapters/duckdb/__init__.py +5 -1
  17. sqlspec/adapters/duckdb/config.py +194 -12
  18. sqlspec/adapters/duckdb/driver.py +225 -0
  19. sqlspec/adapters/oracledb/__init__.py +7 -4
  20. sqlspec/adapters/oracledb/config/__init__.py +4 -4
  21. sqlspec/adapters/oracledb/config/_asyncio.py +96 -12
  22. sqlspec/adapters/oracledb/config/_common.py +1 -1
  23. sqlspec/adapters/oracledb/config/_sync.py +96 -12
  24. sqlspec/adapters/oracledb/driver.py +571 -0
  25. sqlspec/adapters/psqlpy/__init__.py +0 -0
  26. sqlspec/adapters/psqlpy/config.py +258 -0
  27. sqlspec/adapters/psqlpy/driver.py +335 -0
  28. sqlspec/adapters/psycopg/__init__.py +16 -0
  29. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  30. sqlspec/adapters/psycopg/config/_async.py +107 -15
  31. sqlspec/adapters/psycopg/config/_common.py +2 -2
  32. sqlspec/adapters/psycopg/config/_sync.py +107 -15
  33. sqlspec/adapters/psycopg/driver.py +578 -0
  34. sqlspec/adapters/sqlite/__init__.py +7 -0
  35. sqlspec/adapters/sqlite/config.py +24 -6
  36. sqlspec/adapters/sqlite/driver.py +305 -0
  37. sqlspec/base.py +565 -63
  38. sqlspec/exceptions.py +30 -0
  39. sqlspec/extensions/litestar/__init__.py +19 -0
  40. sqlspec/extensions/litestar/_utils.py +56 -0
  41. sqlspec/extensions/litestar/config.py +87 -0
  42. sqlspec/extensions/litestar/handlers.py +213 -0
  43. sqlspec/extensions/litestar/plugin.py +105 -11
  44. sqlspec/statement.py +373 -0
  45. sqlspec/typing.py +81 -17
  46. sqlspec/utils/__init__.py +3 -0
  47. sqlspec/utils/fixtures.py +4 -5
  48. sqlspec/utils/sync_tools.py +335 -0
  49. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/METADATA +4 -1
  50. sqlspec-0.9.0.dist-info/RECORD +61 -0
  51. sqlspec-0.7.1.dist-info/RECORD +0 -46
  52. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/WHEEL +0 -0
  53. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/licenses/LICENSE +0 -0
  54. {sqlspec-0.7.1.dist-info → sqlspec-0.9.0.dist-info}/licenses/NOTICE +0 -0
@@ -1,33 +1,33 @@
1
1
  from contextlib import asynccontextmanager
2
- from dataclasses import dataclass
2
+ from dataclasses import dataclass, field
3
3
  from typing import TYPE_CHECKING, Any, Optional
4
4
 
5
5
  from psycopg import AsyncConnection
6
6
  from psycopg_pool import AsyncConnectionPool
7
7
 
8
- from sqlspec.adapters.psycopg.config._common import PsycoPgGenericPoolConfig
8
+ from sqlspec.adapters.psycopg.config._common import PsycopgGenericPoolConfig
9
+ from sqlspec.adapters.psycopg.driver import PsycopgAsyncDriver
9
10
  from sqlspec.base import AsyncDatabaseConfig
10
11
  from sqlspec.exceptions import ImproperConfigurationError
11
12
  from sqlspec.typing import dataclass_to_dict
12
13
 
13
14
  if TYPE_CHECKING:
14
15
  from collections.abc import AsyncGenerator, Awaitable
15
- from typing import Any
16
16
 
17
17
 
18
18
  __all__ = (
19
- "PsycoPgAsyncDatabaseConfig",
20
- "PsycoPgAsyncPoolConfig",
19
+ "PsycopgAsyncConfig",
20
+ "PsycopgAsyncPoolConfig",
21
21
  )
22
22
 
23
23
 
24
24
  @dataclass
25
- class PsycoPgAsyncPoolConfig(PsycoPgGenericPoolConfig[AsyncConnection, AsyncConnectionPool]):
25
+ class PsycopgAsyncPoolConfig(PsycopgGenericPoolConfig[AsyncConnection, AsyncConnectionPool]):
26
26
  """Async Psycopg Pool Config"""
27
27
 
28
28
 
29
29
  @dataclass
30
- class PsycoPgAsyncDatabaseConfig(AsyncDatabaseConfig[AsyncConnection, AsyncConnectionPool]):
30
+ class PsycopgAsyncConfig(AsyncDatabaseConfig[AsyncConnection, AsyncConnectionPool, PsycopgAsyncDriver]):
31
31
  """Async Psycopg database Configuration.
32
32
 
33
33
  This class provides the base configuration for Psycopg database connections, extending
@@ -37,21 +37,88 @@ class PsycoPgAsyncDatabaseConfig(AsyncDatabaseConfig[AsyncConnection, AsyncConne
37
37
  with both synchronous and asynchronous connections.([2](https://www.psycopg.org/psycopg3/docs/api/connections.html))
38
38
  """
39
39
 
40
- pool_config: "Optional[PsycoPgAsyncPoolConfig]" = None
40
+ pool_config: "Optional[PsycopgAsyncPoolConfig]" = None
41
41
  """Psycopg Pool configuration"""
42
42
  pool_instance: "Optional[AsyncConnectionPool]" = None
43
43
  """Optional pool to use"""
44
+ connection_type: "type[AsyncConnection]" = field(init=False, default_factory=lambda: AsyncConnection) # type: ignore[assignment]
45
+ """Type of the connection object"""
46
+ driver_type: "type[PsycopgAsyncDriver]" = field(init=False, default_factory=lambda: PsycopgAsyncDriver) # type: ignore[type-abstract,unused-ignore]
47
+ """Type of the driver object"""
48
+
49
+ @property
50
+ def connection_config_dict(self) -> "dict[str, Any]":
51
+ """Return the connection configuration as a dict.
52
+
53
+ Returns:
54
+ A string keyed dict of config kwargs for the psycopg.connect function.
55
+
56
+ Raises:
57
+ ImproperConfigurationError: If the connection configuration is not provided.
58
+ """
59
+ if self.pool_config:
60
+ # Filter out pool-specific parameters
61
+ pool_only_params = {
62
+ "min_size",
63
+ "max_size",
64
+ "name",
65
+ "timeout",
66
+ "reconnect_timeout",
67
+ "max_idle",
68
+ "max_lifetime",
69
+ }
70
+ return dataclass_to_dict(
71
+ self.pool_config,
72
+ exclude_empty=True,
73
+ convert_nested=False,
74
+ exclude=pool_only_params.union({"pool_instance", "connection_type", "driver_type", "open"}),
75
+ )
76
+ msg = "You must provide a 'pool_config' for this adapter."
77
+ raise ImproperConfigurationError(msg)
44
78
 
45
79
  @property
46
80
  def pool_config_dict(self) -> "dict[str, Any]":
47
- """Return the pool configuration as a dict."""
81
+ """Return the pool configuration as a dict.
82
+
83
+ Raises:
84
+ ImproperConfigurationError: If pool_config is not set but pool_instance is provided.
85
+ """
48
86
  if self.pool_config:
49
- return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
87
+ return dataclass_to_dict(
88
+ self.pool_config,
89
+ exclude_empty=True,
90
+ convert_nested=False,
91
+ exclude={"pool_instance", "connection_type", "driver_type"},
92
+ )
50
93
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
51
94
  raise ImproperConfigurationError(msg)
52
95
 
96
+ async def create_connection(self) -> "AsyncConnection":
97
+ """Create and return a new psycopg async connection from the pool.
98
+
99
+ Returns:
100
+ An AsyncConnection instance.
101
+
102
+ Raises:
103
+ ImproperConfigurationError: If the connection could not be created.
104
+ """
105
+ try:
106
+ pool = await self.provide_pool()
107
+ return await pool.getconn()
108
+ except Exception as e:
109
+ msg = f"Could not configure the Psycopg connection. Error: {e!s}"
110
+ raise ImproperConfigurationError(msg) from e
111
+
53
112
  async def create_pool(self) -> "AsyncConnectionPool":
54
- """Create and return a connection pool."""
113
+ """Create and return a connection pool.
114
+
115
+ Returns:
116
+ AsyncConnectionPool: The configured connection pool.
117
+
118
+ Raises:
119
+ ImproperConfigurationError: If neither pool_config nor pool_instance are provided
120
+ or if pool creation fails.
121
+ """
55
122
  if self.pool_instance is not None:
56
123
  return self.pool_instance
57
124
 
@@ -60,19 +127,44 @@ class PsycoPgAsyncDatabaseConfig(AsyncDatabaseConfig[AsyncConnection, AsyncConne
60
127
  raise ImproperConfigurationError(msg)
61
128
 
62
129
  pool_config = self.pool_config_dict
63
- self.pool_instance = AsyncConnectionPool(**pool_config)
130
+ self.pool_instance = AsyncConnectionPool(open=False, **pool_config)
64
131
  if self.pool_instance is None: # pyright: ignore[reportUnnecessaryComparison]
65
132
  msg = "Could not configure the 'pool_instance'. Please check your configuration." # type: ignore[unreachable]
66
133
  raise ImproperConfigurationError(msg)
134
+ await self.pool_instance.open()
67
135
  return self.pool_instance
68
136
 
69
137
  def provide_pool(self, *args: "Any", **kwargs: "Any") -> "Awaitable[AsyncConnectionPool]":
70
- """Create and return a connection pool."""
138
+ """Create and return a connection pool.
139
+
140
+ Returns:
141
+ Awaitable[AsyncConnectionPool]: The configured connection pool.
142
+ """
71
143
  return self.create_pool()
72
144
 
73
145
  @asynccontextmanager
74
146
  async def provide_connection(self, *args: "Any", **kwargs: "Any") -> "AsyncGenerator[AsyncConnection, None]":
75
- """Create and provide a database connection."""
147
+ """Create and provide a database connection.
148
+
149
+ Yields:
150
+ AsyncConnection: A database connection from the pool.
151
+ """
76
152
  pool = await self.provide_pool(*args, **kwargs)
77
- async with pool.connection() as connection:
153
+ async with pool, pool.connection() as connection:
78
154
  yield connection
155
+
156
+ @asynccontextmanager
157
+ async def provide_session(self, *args: "Any", **kwargs: "Any") -> "AsyncGenerator[PsycopgAsyncDriver, None]":
158
+ """Create and provide a database session.
159
+
160
+ Yields:
161
+ PsycopgAsyncDriver: A driver instance with an active connection.
162
+ """
163
+ async with self.provide_connection(*args, **kwargs) as connection:
164
+ yield self.driver_type(connection)
165
+
166
+ async def close_pool(self) -> None:
167
+ """Close the connection pool."""
168
+ if self.pool_instance is not None:
169
+ await self.pool_instance.close()
170
+ self.pool_instance = None
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
14
14
  from sqlspec.typing import EmptyType
15
15
 
16
16
 
17
- __all__ = ("PsycoPgGenericPoolConfig",)
17
+ __all__ = ("PsycopgGenericPoolConfig",)
18
18
 
19
19
 
20
20
  ConnectionT = TypeVar("ConnectionT", bound="Union[Connection, AsyncConnection]")
@@ -22,7 +22,7 @@ PoolT = TypeVar("PoolT", bound="Union[ConnectionPool, AsyncConnectionPool]")
22
22
 
23
23
 
24
24
  @dataclass
25
- class PsycoPgGenericPoolConfig(Generic[ConnectionT, PoolT], GenericPoolConfig):
25
+ class PsycopgGenericPoolConfig(GenericPoolConfig, Generic[ConnectionT, PoolT]):
26
26
  """Configuration for Psycopg connection pools.
27
27
 
28
28
  This class provides configuration options for both synchronous and asynchronous Psycopg
@@ -1,33 +1,33 @@
1
1
  from contextlib import contextmanager
2
- from dataclasses import dataclass
2
+ from dataclasses import dataclass, field
3
3
  from typing import TYPE_CHECKING, Any, Optional
4
4
 
5
5
  from psycopg import Connection
6
6
  from psycopg_pool import ConnectionPool
7
7
 
8
- from sqlspec.adapters.psycopg.config._common import PsycoPgGenericPoolConfig
8
+ from sqlspec.adapters.psycopg.config._common import PsycopgGenericPoolConfig
9
+ from sqlspec.adapters.psycopg.driver import PsycopgSyncDriver
9
10
  from sqlspec.base import SyncDatabaseConfig
10
11
  from sqlspec.exceptions import ImproperConfigurationError
11
12
  from sqlspec.typing import dataclass_to_dict
12
13
 
13
14
  if TYPE_CHECKING:
14
15
  from collections.abc import Generator
15
- from typing import Any
16
16
 
17
17
 
18
18
  __all__ = (
19
- "PsycoPgSyncDatabaseConfig",
20
- "PsycoPgSyncPoolConfig",
19
+ "PsycopgSyncConfig",
20
+ "PsycopgSyncPoolConfig",
21
21
  )
22
22
 
23
23
 
24
24
  @dataclass
25
- class PsycoPgSyncPoolConfig(PsycoPgGenericPoolConfig[Connection, ConnectionPool]):
25
+ class PsycopgSyncPoolConfig(PsycopgGenericPoolConfig[Connection, ConnectionPool]):
26
26
  """Sync Psycopg Pool Config"""
27
27
 
28
28
 
29
29
  @dataclass
30
- class PsycoPgSyncDatabaseConfig(SyncDatabaseConfig[Connection, ConnectionPool]):
30
+ class PsycopgSyncConfig(SyncDatabaseConfig[Connection, ConnectionPool, PsycopgSyncDriver]):
31
31
  """Sync Psycopg database Configuration.
32
32
  This class provides the base configuration for Psycopg database connections, extending
33
33
  the generic database configuration with Psycopg-specific settings.([1](https://www.psycopg.org/psycopg3/docs/api/connections.html))
@@ -36,21 +36,88 @@ class PsycoPgSyncDatabaseConfig(SyncDatabaseConfig[Connection, ConnectionPool]):
36
36
  with both synchronous and asynchronous connections.([2](https://www.psycopg.org/psycopg3/docs/api/connections.html))
37
37
  """
38
38
 
39
- pool_config: "Optional[PsycoPgSyncPoolConfig]" = None
39
+ pool_config: "Optional[PsycopgSyncPoolConfig]" = None
40
40
  """Psycopg Pool configuration"""
41
41
  pool_instance: "Optional[ConnectionPool]" = None
42
42
  """Optional pool to use"""
43
+ connection_type: "type[Connection]" = field(init=False, default_factory=lambda: Connection) # type: ignore[assignment]
44
+ """Type of the connection object"""
45
+ driver_type: "type[PsycopgSyncDriver]" = field(init=False, default_factory=lambda: PsycopgSyncDriver) # type: ignore[type-abstract,unused-ignore]
46
+ """Type of the driver object"""
47
+
48
+ @property
49
+ def connection_config_dict(self) -> "dict[str, Any]":
50
+ """Return the connection configuration as a dict.
51
+
52
+ Returns:
53
+ A string keyed dict of config kwargs for the psycopg.connect function.
54
+
55
+ Raises:
56
+ ImproperConfigurationError: If the connection configuration is not provided.
57
+ """
58
+ if self.pool_config:
59
+ # Filter out pool-specific parameters
60
+ pool_only_params = {
61
+ "min_size",
62
+ "max_size",
63
+ "name",
64
+ "timeout",
65
+ "reconnect_timeout",
66
+ "max_idle",
67
+ "max_lifetime",
68
+ }
69
+ return dataclass_to_dict(
70
+ self.pool_config,
71
+ exclude_empty=True,
72
+ convert_nested=False,
73
+ exclude=pool_only_params.union({"pool_instance", "connection_type", "driver_type", "open"}),
74
+ )
75
+ msg = "You must provide a 'pool_config' for this adapter."
76
+ raise ImproperConfigurationError(msg)
43
77
 
44
78
  @property
45
79
  def pool_config_dict(self) -> "dict[str, Any]":
46
- """Return the pool configuration as a dict."""
80
+ """Return the pool configuration as a dict.
81
+
82
+ Raises:
83
+ ImproperConfigurationError: If pool_config is not provided and instead pool_instance is used.
84
+ """
47
85
  if self.pool_config:
48
- return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
86
+ return dataclass_to_dict(
87
+ self.pool_config,
88
+ exclude_empty=True,
89
+ convert_nested=False,
90
+ exclude={"pool_instance", "connection_type", "driver_type", "open"},
91
+ )
49
92
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
50
93
  raise ImproperConfigurationError(msg)
51
94
 
95
+ def create_connection(self) -> "Connection":
96
+ """Create and return a new psycopg connection from the pool.
97
+
98
+ Returns:
99
+ A Connection instance.
100
+
101
+ Raises:
102
+ ImproperConfigurationError: If the connection could not be created.
103
+ """
104
+ try:
105
+ pool = self.provide_pool()
106
+ return pool.getconn()
107
+ except Exception as e:
108
+ msg = f"Could not configure the Psycopg connection. Error: {e!s}"
109
+ raise ImproperConfigurationError(msg) from e
110
+
52
111
  def create_pool(self) -> "ConnectionPool":
53
- """Create and return a connection pool."""
112
+ """Create and return a connection pool.
113
+
114
+ Returns:
115
+ ConnectionPool: The configured connection pool instance.
116
+
117
+ Raises:
118
+ ImproperConfigurationError: If neither pool_config nor pool_instance is provided,
119
+ or if the pool could not be configured.
120
+ """
54
121
  if self.pool_instance is not None:
55
122
  return self.pool_instance
56
123
 
@@ -59,19 +126,44 @@ class PsycoPgSyncDatabaseConfig(SyncDatabaseConfig[Connection, ConnectionPool]):
59
126
  raise ImproperConfigurationError(msg)
60
127
 
61
128
  pool_config = self.pool_config_dict
62
- self.pool_instance = ConnectionPool(**pool_config)
129
+ self.pool_instance = ConnectionPool(open=False, **pool_config)
63
130
  if self.pool_instance is None: # pyright: ignore[reportUnnecessaryComparison]
64
131
  msg = "Could not configure the 'pool_instance'. Please check your configuration." # type: ignore[unreachable]
65
132
  raise ImproperConfigurationError(msg)
133
+ self.pool_instance.open()
66
134
  return self.pool_instance
67
135
 
68
136
  def provide_pool(self, *args: "Any", **kwargs: "Any") -> "ConnectionPool":
69
- """Create and return a connection pool."""
137
+ """Create and return a connection pool.
138
+
139
+ Returns:
140
+ ConnectionPool: The configured connection pool instance.
141
+ """
70
142
  return self.create_pool()
71
143
 
72
144
  @contextmanager
73
145
  def provide_connection(self, *args: "Any", **kwargs: "Any") -> "Generator[Connection, None, None]":
74
- """Create and provide a database connection."""
146
+ """Create and provide a database connection.
147
+
148
+ Yields:
149
+ Connection: A database connection from the pool.
150
+ """
75
151
  pool = self.provide_pool(*args, **kwargs)
76
- with pool.connection() as connection:
152
+ with pool, pool.connection() as connection:
77
153
  yield connection
154
+
155
+ @contextmanager
156
+ def provide_session(self, *args: "Any", **kwargs: "Any") -> "Generator[PsycopgSyncDriver, None, None]":
157
+ """Create and provide a database session.
158
+
159
+ Yields:
160
+ PsycopgSyncDriver: A driver instance with an active connection.
161
+ """
162
+ with self.provide_connection(*args, **kwargs) as connection:
163
+ yield self.driver_type(connection)
164
+
165
+ def close_pool(self) -> None:
166
+ """Close the connection pool."""
167
+ if self.pool_instance is not None:
168
+ self.pool_instance.close()
169
+ self.pool_instance = None