sqlspec 0.8.0__py3-none-any.whl → 0.9.1__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 (45) hide show
  1. sqlspec/_typing.py +39 -6
  2. sqlspec/adapters/adbc/__init__.py +2 -2
  3. sqlspec/adapters/adbc/config.py +34 -11
  4. sqlspec/adapters/adbc/driver.py +302 -111
  5. sqlspec/adapters/aiosqlite/__init__.py +2 -2
  6. sqlspec/adapters/aiosqlite/config.py +2 -2
  7. sqlspec/adapters/aiosqlite/driver.py +164 -42
  8. sqlspec/adapters/asyncmy/__init__.py +3 -3
  9. sqlspec/adapters/asyncmy/config.py +11 -12
  10. sqlspec/adapters/asyncmy/driver.py +161 -37
  11. sqlspec/adapters/asyncpg/__init__.py +5 -5
  12. sqlspec/adapters/asyncpg/config.py +17 -19
  13. sqlspec/adapters/asyncpg/driver.py +386 -96
  14. sqlspec/adapters/duckdb/__init__.py +2 -2
  15. sqlspec/adapters/duckdb/config.py +2 -2
  16. sqlspec/adapters/duckdb/driver.py +190 -60
  17. sqlspec/adapters/oracledb/__init__.py +8 -8
  18. sqlspec/adapters/oracledb/config/__init__.py +6 -6
  19. sqlspec/adapters/oracledb/config/_asyncio.py +9 -10
  20. sqlspec/adapters/oracledb/config/_sync.py +8 -9
  21. sqlspec/adapters/oracledb/driver.py +384 -45
  22. sqlspec/adapters/psqlpy/__init__.py +0 -0
  23. sqlspec/adapters/psqlpy/config.py +250 -0
  24. sqlspec/adapters/psqlpy/driver.py +481 -0
  25. sqlspec/adapters/psycopg/__init__.py +10 -5
  26. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  27. sqlspec/adapters/psycopg/config/_async.py +12 -12
  28. sqlspec/adapters/psycopg/config/_sync.py +13 -13
  29. sqlspec/adapters/psycopg/driver.py +432 -222
  30. sqlspec/adapters/sqlite/__init__.py +2 -2
  31. sqlspec/adapters/sqlite/config.py +2 -2
  32. sqlspec/adapters/sqlite/driver.py +176 -72
  33. sqlspec/base.py +687 -161
  34. sqlspec/exceptions.py +30 -0
  35. sqlspec/extensions/litestar/config.py +6 -0
  36. sqlspec/extensions/litestar/handlers.py +25 -0
  37. sqlspec/extensions/litestar/plugin.py +8 -1
  38. sqlspec/statement.py +373 -0
  39. sqlspec/typing.py +10 -1
  40. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/METADATA +144 -2
  41. sqlspec-0.9.1.dist-info/RECORD +61 -0
  42. sqlspec-0.8.0.dist-info/RECORD +0 -57
  43. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/WHEEL +0 -0
  44. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/LICENSE +0 -0
  45. {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/NOTICE +0 -0
File without changes
@@ -0,0 +1,250 @@
1
+ """Configuration for the psqlpy PostgreSQL adapter."""
2
+
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass, field
5
+ from typing import TYPE_CHECKING, Any, Optional, Union
6
+
7
+ from psqlpy import Connection, ConnectionPool
8
+
9
+ from sqlspec.adapters.psqlpy.driver import PsqlpyDriver
10
+ from sqlspec.base import AsyncDatabaseConfig, GenericPoolConfig
11
+ from sqlspec.exceptions import ImproperConfigurationError
12
+ from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import AsyncGenerator, Awaitable
16
+
17
+
18
+ __all__ = (
19
+ "PsqlpyConfig",
20
+ "PsqlpyPoolConfig",
21
+ )
22
+
23
+
24
+ @dataclass
25
+ class PsqlpyPoolConfig(GenericPoolConfig):
26
+ """Configuration for psqlpy connection pool.
27
+
28
+ Ref: https://psqlpy-python.github.io/components/connection_pool.html#all-available-connectionpool-parameters
29
+ """
30
+
31
+ dsn: Optional[Union[str, EmptyType]] = Empty
32
+ """DSN of the PostgreSQL."""
33
+ # Required connection parameters
34
+ username: Optional[Union[str, EmptyType]] = Empty
35
+ """Username of the user in the PostgreSQL."""
36
+ password: Optional[Union[str, EmptyType]] = Empty
37
+ """Password of the user in the PostgreSQL."""
38
+ db_name: Optional[Union[str, EmptyType]] = Empty
39
+ """Name of the database in PostgreSQL."""
40
+
41
+ # Single or Multi-host parameters (mutually exclusive)
42
+ host: Optional[Union[str, EmptyType]] = Empty
43
+ """Host of the PostgreSQL (use for single host)."""
44
+ port: Optional[Union[int, EmptyType]] = Empty
45
+ """Port of the PostgreSQL (use for single host)."""
46
+ hosts: Optional[Union[list[str], EmptyType]] = Empty
47
+ """List of hosts of the PostgreSQL (use for multiple hosts)."""
48
+ ports: Optional[Union[list[int], EmptyType]] = Empty
49
+ """List of ports of the PostgreSQL (use for multiple hosts)."""
50
+
51
+ # Pool size
52
+ max_db_pool_size: int = 10
53
+ """Maximum size of the connection pool. Defaults to 10."""
54
+
55
+ # Optional timeouts
56
+ connect_timeout_sec: Optional[Union[int, EmptyType]] = Empty
57
+ """The time limit in seconds applied to each socket-level connection attempt."""
58
+ connect_timeout_nanosec: Optional[Union[int, EmptyType]] = Empty
59
+ """Nanoseconds for connection timeout, can be used only with `connect_timeout_sec`."""
60
+ tcp_user_timeout_sec: Optional[Union[int, EmptyType]] = Empty
61
+ """The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed."""
62
+ tcp_user_timeout_nanosec: Optional[Union[int, EmptyType]] = Empty
63
+ """Nanoseconds for tcp_user_timeout, can be used only with `tcp_user_timeout_sec`."""
64
+
65
+ # Optional keepalives
66
+ keepalives: bool = True
67
+ """Controls the use of TCP keepalive. Defaults to True (on)."""
68
+ keepalives_idle_sec: Optional[Union[int, EmptyType]] = Empty
69
+ """The number of seconds of inactivity after which a keepalive message is sent to the server."""
70
+ keepalives_idle_nanosec: Optional[Union[int, EmptyType]] = Empty
71
+ """Nanoseconds for keepalives_idle_sec."""
72
+ keepalives_interval_sec: Optional[Union[int, EmptyType]] = Empty
73
+ """The time interval between TCP keepalive probes."""
74
+ keepalives_interval_nanosec: Optional[Union[int, EmptyType]] = Empty
75
+ """Nanoseconds for keepalives_interval_sec."""
76
+ keepalives_retries: Optional[Union[int, EmptyType]] = Empty
77
+ """The maximum number of TCP keepalive probes that will be sent before dropping a connection."""
78
+
79
+ # Other optional parameters
80
+ load_balance_hosts: Optional[Union[str, EmptyType]] = Empty
81
+ """Controls the order in which the client tries to connect to the available hosts and addresses ('disable' or 'random')."""
82
+ conn_recycling_method: Optional[Union[str, EmptyType]] = Empty
83
+ """How a connection is recycled."""
84
+ ssl_mode: Optional[Union[str, EmptyType]] = Empty
85
+ """SSL mode."""
86
+ ca_file: Optional[Union[str, EmptyType]] = Empty
87
+ """Path to ca_file for SSL."""
88
+ target_session_attrs: Optional[Union[str, EmptyType]] = Empty
89
+ """Specifies requirements of the session (e.g., 'read-write')."""
90
+ options: Optional[Union[str, EmptyType]] = Empty
91
+ """Command line options used to configure the server."""
92
+ application_name: Optional[Union[str, EmptyType]] = Empty
93
+ """Sets the application_name parameter on the server."""
94
+
95
+
96
+ @dataclass
97
+ class PsqlpyConfig(AsyncDatabaseConfig[Connection, ConnectionPool, PsqlpyDriver]):
98
+ """Configuration for psqlpy database connections, managing a connection pool.
99
+
100
+ This configuration class wraps `PsqlpyPoolConfig` and manages the lifecycle
101
+ of a `psqlpy.ConnectionPool`.
102
+ """
103
+
104
+ pool_config: Optional[PsqlpyPoolConfig] = field(default=None)
105
+ """Psqlpy Pool configuration"""
106
+ driver_type: type[PsqlpyDriver] = field(default=PsqlpyDriver, init=False, hash=False)
107
+ """Type of the driver object"""
108
+ connection_type: type[Connection] = field(default=Connection, init=False, hash=False)
109
+ """Type of the connection object"""
110
+ pool_instance: Optional[ConnectionPool] = field(default=None, hash=False)
111
+ """The connection pool instance. If set, this will be used instead of creating a new pool."""
112
+
113
+ @property
114
+ def connection_config_dict(self) -> "dict[str, Any]":
115
+ """Return the minimal connection configuration as a dict for standalone use.
116
+
117
+ Returns:
118
+ A string keyed dict of config kwargs for a psqlpy.Connection.
119
+
120
+ Raises:
121
+ ImproperConfigurationError: If essential connection parameters are missing.
122
+ """
123
+ if self.pool_config:
124
+ # Exclude pool-specific keys and internal metadata
125
+ pool_specific_keys = {
126
+ "max_db_pool_size",
127
+ "load_balance_hosts",
128
+ "conn_recycling_method",
129
+ "pool_instance",
130
+ "connection_type",
131
+ "driver_type",
132
+ }
133
+ return dataclass_to_dict(
134
+ self.pool_config,
135
+ exclude_empty=True,
136
+ convert_nested=False,
137
+ exclude_none=True,
138
+ exclude=pool_specific_keys,
139
+ )
140
+ msg = "You must provide a 'pool_config' for this adapter."
141
+ raise ImproperConfigurationError(msg)
142
+
143
+ @property
144
+ def pool_config_dict(self) -> "dict[str, Any]":
145
+ """Return the pool configuration as a dict.
146
+
147
+ Raises:
148
+ ImproperConfigurationError: If no pool_config is provided but a pool_instance
149
+
150
+ Returns:
151
+ A string keyed dict of config kwargs for creating a psqlpy pool.
152
+ """
153
+ if self.pool_config:
154
+ # Extract the config from the pool_config
155
+ return dataclass_to_dict(
156
+ self.pool_config,
157
+ exclude_empty=True,
158
+ convert_nested=False,
159
+ exclude_none=True,
160
+ exclude={"pool_instance", "connection_type", "driver_type"},
161
+ )
162
+
163
+ msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
164
+ raise ImproperConfigurationError(msg)
165
+
166
+ async def create_pool(self) -> "ConnectionPool":
167
+ """Return a pool. If none exists yet, create one.
168
+
169
+ Ensures that the pool is initialized and returns the instance.
170
+
171
+ Returns:
172
+ The pool instance used by the plugin.
173
+
174
+ Raises:
175
+ ImproperConfigurationError: If the pool could not be configured.
176
+ """
177
+ if self.pool_instance is not None:
178
+ return self.pool_instance
179
+
180
+ if self.pool_config is None:
181
+ msg = "One of 'pool_config' or 'pool_instance' must be provided."
182
+ raise ImproperConfigurationError(msg)
183
+
184
+ # pool_config is guaranteed to exist due to __post_init__
185
+ try:
186
+ # psqlpy ConnectionPool doesn't have an explicit async connect/startup method
187
+ # It creates connections on demand.
188
+ self.pool_instance = ConnectionPool(**self.pool_config_dict)
189
+ except Exception as e:
190
+ msg = f"Could not configure the 'pool_instance'. Error: {e!s}. Please check your configuration."
191
+ raise ImproperConfigurationError(msg) from e
192
+
193
+ return self.pool_instance
194
+
195
+ def provide_pool(self, *args: "Any", **kwargs: "Any") -> "Awaitable[ConnectionPool]":
196
+ """Create or return the pool instance.
197
+
198
+ Returns:
199
+ An awaitable resolving to the Pool instance.
200
+ """
201
+
202
+ async def _create() -> "ConnectionPool":
203
+ return await self.create_pool()
204
+
205
+ return _create()
206
+
207
+ def create_connection(self) -> "Awaitable[Connection]":
208
+ """Create and return a new, standalone psqlpy connection using the configured parameters.
209
+
210
+ Returns:
211
+ An awaitable that resolves to a new Connection instance.
212
+ """
213
+
214
+ async def _create() -> "Connection":
215
+ try:
216
+ async with self.provide_connection() as conn:
217
+ return conn
218
+ except Exception as e:
219
+ msg = f"Could not configure the psqlpy connection. Error: {e!s}"
220
+ raise ImproperConfigurationError(msg) from e
221
+
222
+ return _create()
223
+
224
+ @asynccontextmanager
225
+ async def provide_connection(self, *args: "Any", **kwargs: "Any") -> "AsyncGenerator[Connection, None]":
226
+ """Acquire a connection from the pool.
227
+
228
+ Yields:
229
+ A connection instance managed by the pool.
230
+ """
231
+ db_pool = await self.provide_pool(*args, **kwargs)
232
+ async with db_pool.acquire() as conn:
233
+ yield conn
234
+
235
+ def close_pool(self) -> None:
236
+ """Close the connection pool."""
237
+ if self.pool_instance is not None:
238
+ # psqlpy pool close is synchronous
239
+ self.pool_instance.close()
240
+ self.pool_instance = None
241
+
242
+ @asynccontextmanager
243
+ async def provide_session(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[PsqlpyDriver, None]":
244
+ """Create and provide a database session using a pooled connection.
245
+
246
+ Yields:
247
+ A Psqlpy driver instance wrapping a pooled connection.
248
+ """
249
+ async with self.provide_connection(*args, **kwargs) as connection:
250
+ yield self.driver_type(connection)