sqlspec 0.5.0__py3-none-any.whl → 0.7.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.
- sqlspec/__init__.py +0 -1
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +55 -8
- sqlspec/_typing.py +69 -20
- sqlspec/adapters/adbc/config.py +8 -13
- sqlspec/adapters/aiosqlite/__init__.py +1 -1
- sqlspec/adapters/aiosqlite/config.py +15 -24
- sqlspec/adapters/asyncmy/__init__.py +1 -1
- sqlspec/adapters/asyncmy/config.py +46 -46
- sqlspec/adapters/asyncpg/config.py +28 -33
- sqlspec/adapters/duckdb/config.py +75 -79
- sqlspec/adapters/oracledb/config/_asyncio.py +23 -18
- sqlspec/adapters/oracledb/config/_common.py +52 -72
- sqlspec/adapters/oracledb/config/_sync.py +21 -16
- sqlspec/adapters/psycopg/config/_async.py +17 -17
- sqlspec/adapters/psycopg/config/_common.py +17 -34
- sqlspec/adapters/psycopg/config/_sync.py +16 -17
- sqlspec/adapters/sqlite/config.py +13 -15
- sqlspec/base.py +148 -8
- sqlspec/exceptions.py +4 -6
- sqlspec/extensions/litestar/config.py +0 -0
- sqlspec/extensions/litestar/plugin.py +21 -12
- sqlspec/filters.py +11 -13
- sqlspec/typing.py +110 -127
- sqlspec/utils/deprecation.py +8 -10
- sqlspec/utils/fixtures.py +3 -5
- sqlspec/utils/module_loader.py +4 -6
- sqlspec/utils/text.py +2 -3
- {sqlspec-0.5.0.dist-info → sqlspec-0.7.0.dist-info}/METADATA +24 -15
- sqlspec-0.7.0.dist-info/RECORD +46 -0
- sqlspec-0.7.0.dist-info/licenses/LICENSE +21 -0
- sqlspec-0.5.0.dist-info/RECORD +0 -44
- {sqlspec-0.5.0.dist-info → sqlspec-0.7.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.5.0.dist-info → sqlspec-0.7.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/base.py
CHANGED
|
@@ -2,16 +2,21 @@ from abc import ABC, abstractmethod
|
|
|
2
2
|
from collections.abc import AsyncGenerator, Awaitable, Generator
|
|
3
3
|
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import Any, ClassVar, Generic, TypeVar, Union
|
|
5
|
+
from typing import Annotated, Any, ClassVar, Generic, TypeVar, Union, cast, overload
|
|
6
6
|
|
|
7
7
|
__all__ = (
|
|
8
|
+
"AsyncDatabaseConfig",
|
|
8
9
|
"DatabaseConfigProtocol",
|
|
9
10
|
"GenericPoolConfig",
|
|
10
|
-
"
|
|
11
|
+
"NoPoolAsyncConfig",
|
|
12
|
+
"NoPoolSyncConfig",
|
|
13
|
+
"SyncDatabaseConfig",
|
|
11
14
|
)
|
|
12
15
|
|
|
13
16
|
ConnectionT = TypeVar("ConnectionT")
|
|
14
17
|
PoolT = TypeVar("PoolT")
|
|
18
|
+
AsyncConfigT = TypeVar("AsyncConfigT", bound="Union[AsyncDatabaseConfig[Any, Any], NoPoolAsyncConfig[Any]]")
|
|
19
|
+
SyncConfigT = TypeVar("SyncConfigT", bound="Union[SyncDatabaseConfig[Any, Any], NoPoolSyncConfig[Any]]")
|
|
15
20
|
|
|
16
21
|
|
|
17
22
|
@dataclass
|
|
@@ -21,6 +26,9 @@ class DatabaseConfigProtocol(Generic[ConnectionT, PoolT], ABC):
|
|
|
21
26
|
__is_async__: ClassVar[bool] = False
|
|
22
27
|
__supports_connection_pooling__: ClassVar[bool] = False
|
|
23
28
|
|
|
29
|
+
def __hash__(self) -> int:
|
|
30
|
+
return id(self)
|
|
31
|
+
|
|
24
32
|
@abstractmethod
|
|
25
33
|
def create_connection(self) -> Union[ConnectionT, Awaitable[ConnectionT]]:
|
|
26
34
|
"""Create and return a new database connection."""
|
|
@@ -28,7 +36,9 @@ class DatabaseConfigProtocol(Generic[ConnectionT, PoolT], ABC):
|
|
|
28
36
|
|
|
29
37
|
@abstractmethod
|
|
30
38
|
def provide_connection(
|
|
31
|
-
self,
|
|
39
|
+
self,
|
|
40
|
+
*args: Any,
|
|
41
|
+
**kwargs: Any,
|
|
32
42
|
) -> Union[
|
|
33
43
|
Generator[ConnectionT, None, None],
|
|
34
44
|
AsyncGenerator[ConnectionT, None],
|
|
@@ -51,7 +61,9 @@ class DatabaseConfigProtocol(Generic[ConnectionT, PoolT], ABC):
|
|
|
51
61
|
|
|
52
62
|
@abstractmethod
|
|
53
63
|
def provide_pool(
|
|
54
|
-
self,
|
|
64
|
+
self,
|
|
65
|
+
*args: Any,
|
|
66
|
+
**kwargs: Any,
|
|
55
67
|
) -> Union[PoolT, Awaitable[PoolT], AbstractContextManager[PoolT], AbstractAsyncContextManager[PoolT]]:
|
|
56
68
|
"""Provide pool instance."""
|
|
57
69
|
raise NotImplementedError
|
|
@@ -67,14 +79,34 @@ class DatabaseConfigProtocol(Generic[ConnectionT, PoolT], ABC):
|
|
|
67
79
|
return self.__supports_connection_pooling__
|
|
68
80
|
|
|
69
81
|
|
|
70
|
-
class
|
|
71
|
-
"""Base class for database configurations that do not implement a pool."""
|
|
82
|
+
class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None]):
|
|
83
|
+
"""Base class for a sync database configurations that do not implement a pool."""
|
|
84
|
+
|
|
85
|
+
__is_async__ = False
|
|
86
|
+
__supports_connection_pooling__ = False
|
|
72
87
|
|
|
73
88
|
def create_pool(self) -> None:
|
|
74
89
|
"""This database backend has not implemented the pooling configurations."""
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
def provide_pool(self, *args: Any, **kwargs: Any) -> None:
|
|
93
|
+
"""This database backend has not implemented the pooling configurations."""
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None]):
|
|
98
|
+
"""Base class for an async database configurations that do not implement a pool."""
|
|
99
|
+
|
|
100
|
+
__is_async__ = True
|
|
101
|
+
__supports_connection_pooling__ = False
|
|
102
|
+
|
|
103
|
+
async def create_pool(self) -> None:
|
|
104
|
+
"""This database backend has not implemented the pooling configurations."""
|
|
105
|
+
return
|
|
75
106
|
|
|
76
107
|
def provide_pool(self, *args: Any, **kwargs: Any) -> None:
|
|
77
108
|
"""This database backend has not implemented the pooling configurations."""
|
|
109
|
+
return
|
|
78
110
|
|
|
79
111
|
|
|
80
112
|
@dataclass
|
|
@@ -83,5 +115,113 @@ class GenericPoolConfig:
|
|
|
83
115
|
|
|
84
116
|
|
|
85
117
|
@dataclass
|
|
86
|
-
class
|
|
87
|
-
"""Generic Database Configuration."""
|
|
118
|
+
class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT]):
|
|
119
|
+
"""Generic Sync Database Configuration."""
|
|
120
|
+
|
|
121
|
+
__is_async__ = False
|
|
122
|
+
__supports_connection_pooling__ = True
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT]):
|
|
127
|
+
"""Generic Async Database Configuration."""
|
|
128
|
+
|
|
129
|
+
__is_async__ = True
|
|
130
|
+
__supports_connection_pooling__ = True
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ConfigManager:
|
|
134
|
+
"""Type-safe configuration manager with literal inference."""
|
|
135
|
+
|
|
136
|
+
def __init__(self) -> None:
|
|
137
|
+
self._configs: dict[Any, DatabaseConfigProtocol[Any, Any]] = {}
|
|
138
|
+
|
|
139
|
+
@overload
|
|
140
|
+
def add_config(self, config: SyncConfigT) -> type[SyncConfigT]: ...
|
|
141
|
+
|
|
142
|
+
@overload
|
|
143
|
+
def add_config(self, config: AsyncConfigT) -> type[AsyncConfigT]: ...
|
|
144
|
+
|
|
145
|
+
def add_config(
|
|
146
|
+
self,
|
|
147
|
+
config: Union[
|
|
148
|
+
SyncConfigT,
|
|
149
|
+
AsyncConfigT,
|
|
150
|
+
],
|
|
151
|
+
) -> Union[Annotated[type[SyncConfigT], int], Annotated[type[AsyncConfigT], int]]: # pyright: ignore[reportInvalidTypeVarUse]
|
|
152
|
+
"""Add a new configuration to the manager."""
|
|
153
|
+
key = Annotated[type(config), id(config)] # type: ignore[valid-type]
|
|
154
|
+
self._configs[key] = config
|
|
155
|
+
return key # type: ignore[return-value] # pyright: ignore[reportReturnType]
|
|
156
|
+
|
|
157
|
+
@overload
|
|
158
|
+
def get_config(self, name: type[SyncConfigT]) -> SyncConfigT: ...
|
|
159
|
+
|
|
160
|
+
@overload
|
|
161
|
+
def get_config(self, name: type[AsyncConfigT]) -> AsyncConfigT: ...
|
|
162
|
+
|
|
163
|
+
def get_config(
|
|
164
|
+
self,
|
|
165
|
+
name: Union[type[DatabaseConfigProtocol[ConnectionT, PoolT]], Any],
|
|
166
|
+
) -> DatabaseConfigProtocol[ConnectionT, PoolT]:
|
|
167
|
+
"""Retrieve a configuration by its type."""
|
|
168
|
+
config = self._configs.get(name)
|
|
169
|
+
if not config:
|
|
170
|
+
msg = f"No configuration found for {name}"
|
|
171
|
+
raise KeyError(msg)
|
|
172
|
+
return config
|
|
173
|
+
|
|
174
|
+
@overload
|
|
175
|
+
def get_connection(
|
|
176
|
+
self,
|
|
177
|
+
name: Union[
|
|
178
|
+
type[NoPoolSyncConfig[ConnectionT]],
|
|
179
|
+
type[SyncDatabaseConfig[ConnectionT, PoolT]], # pyright: ignore[reportInvalidTypeVarUse]
|
|
180
|
+
],
|
|
181
|
+
) -> ConnectionT: ...
|
|
182
|
+
|
|
183
|
+
@overload
|
|
184
|
+
def get_connection(
|
|
185
|
+
self,
|
|
186
|
+
name: Union[
|
|
187
|
+
type[NoPoolAsyncConfig[ConnectionT]],
|
|
188
|
+
type[AsyncDatabaseConfig[ConnectionT, PoolT]], # pyright: ignore[reportInvalidTypeVarUse]
|
|
189
|
+
],
|
|
190
|
+
) -> Awaitable[ConnectionT]: ...
|
|
191
|
+
|
|
192
|
+
def get_connection(
|
|
193
|
+
self,
|
|
194
|
+
name: Union[
|
|
195
|
+
type[NoPoolSyncConfig[ConnectionT]],
|
|
196
|
+
type[NoPoolAsyncConfig[ConnectionT]],
|
|
197
|
+
type[SyncDatabaseConfig[ConnectionT, PoolT]],
|
|
198
|
+
type[AsyncDatabaseConfig[ConnectionT, PoolT]],
|
|
199
|
+
],
|
|
200
|
+
) -> Union[ConnectionT, Awaitable[ConnectionT]]:
|
|
201
|
+
"""Create and return a connection from the specified configuration."""
|
|
202
|
+
config = self.get_config(name)
|
|
203
|
+
return config.create_connection()
|
|
204
|
+
|
|
205
|
+
@overload
|
|
206
|
+
def get_pool(self, name: type[Union[NoPoolSyncConfig[ConnectionT], NoPoolAsyncConfig[ConnectionT]]]) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
207
|
+
|
|
208
|
+
@overload
|
|
209
|
+
def get_pool(self, name: type[SyncDatabaseConfig[ConnectionT, PoolT]]) -> type[PoolT]: ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
210
|
+
|
|
211
|
+
@overload
|
|
212
|
+
def get_pool(self, name: type[AsyncDatabaseConfig[ConnectionT, PoolT]]) -> Awaitable[type[PoolT]]: ... # pyright: ignore[reportInvalidTypeVarUse]
|
|
213
|
+
|
|
214
|
+
def get_pool(
|
|
215
|
+
self,
|
|
216
|
+
name: Union[
|
|
217
|
+
type[NoPoolSyncConfig[ConnectionT]],
|
|
218
|
+
type[NoPoolAsyncConfig[ConnectionT]],
|
|
219
|
+
type[SyncDatabaseConfig[ConnectionT, PoolT]],
|
|
220
|
+
type[AsyncDatabaseConfig[ConnectionT, PoolT]],
|
|
221
|
+
],
|
|
222
|
+
) -> Union[type[PoolT], Awaitable[type[PoolT]], None]:
|
|
223
|
+
"""Create and return a connection pool from the specified configuration."""
|
|
224
|
+
config = self.get_config(name)
|
|
225
|
+
if isinstance(config, (NoPoolSyncConfig, NoPoolAsyncConfig)):
|
|
226
|
+
return None
|
|
227
|
+
return cast("Union[type[PoolT], Awaitable[type[PoolT]]]", config.create_pool())
|
sqlspec/exceptions.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
1
|
+
from typing import Any, Optional
|
|
4
2
|
|
|
5
3
|
__all__ = (
|
|
6
4
|
"ImproperConfigurationError",
|
|
@@ -50,7 +48,7 @@ class MissingDependencyError(SQLSpecError, ImportError):
|
|
|
50
48
|
This exception is raised only when a module depends on a dependency that has not been installed.
|
|
51
49
|
"""
|
|
52
50
|
|
|
53
|
-
def __init__(self, package: str, install_package: str
|
|
51
|
+
def __init__(self, package: str, install_package: Optional[str] = None) -> None:
|
|
54
52
|
super().__init__(
|
|
55
53
|
f"Package {package!r} is not installed but required. You can install it by running "
|
|
56
54
|
f"'pip install sqlspec[{install_package or package}]' to install sqlspec with the required extra "
|
|
@@ -61,7 +59,7 @@ class MissingDependencyError(SQLSpecError, ImportError):
|
|
|
61
59
|
class SQLLoadingError(SQLSpecError):
|
|
62
60
|
"""Issues loading referenced SQL file."""
|
|
63
61
|
|
|
64
|
-
def __init__(self, message: str
|
|
62
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
65
63
|
if message is None:
|
|
66
64
|
message = "Issues loading referenced SQL file."
|
|
67
65
|
super().__init__(message)
|
|
@@ -70,7 +68,7 @@ class SQLLoadingError(SQLSpecError):
|
|
|
70
68
|
class SQLParsingError(SQLSpecError):
|
|
71
69
|
"""Issues parsing SQL statements."""
|
|
72
70
|
|
|
73
|
-
def __init__(self, message: str
|
|
71
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
74
72
|
if message is None:
|
|
75
73
|
message = "Issues parsing SQL statement."
|
|
76
74
|
super().__init__(message)
|
|
File without changes
|
|
@@ -1,34 +1,43 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, TypeVar
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
4
2
|
|
|
5
3
|
from litestar.plugins import InitPluginProtocol
|
|
6
4
|
|
|
7
5
|
if TYPE_CHECKING:
|
|
8
6
|
from litestar.config.app import AppConfig
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
T = TypeVar("T")
|
|
8
|
+
from sqlspec.base import ConfigManager
|
|
12
9
|
|
|
13
10
|
|
|
14
11
|
class SQLSpecPlugin(InitPluginProtocol):
|
|
15
|
-
"""
|
|
12
|
+
"""SQLSpec plugin."""
|
|
16
13
|
|
|
17
14
|
__slots__ = ("_config",)
|
|
18
15
|
|
|
19
|
-
def __init__(self, config:
|
|
20
|
-
"""Initialize ``
|
|
16
|
+
def __init__(self, config: "ConfigManager") -> None:
|
|
17
|
+
"""Initialize ``SQLSpecPlugin``.
|
|
21
18
|
|
|
22
19
|
Args:
|
|
23
|
-
config: configure
|
|
20
|
+
config: configure SQLSpec plugin for use with Litestar.
|
|
24
21
|
"""
|
|
25
22
|
self._config = config
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
@property
|
|
25
|
+
def config(self) -> "ConfigManager":
|
|
26
|
+
"""Return the plugin config.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
ConfigManager.
|
|
30
|
+
"""
|
|
31
|
+
return self._config
|
|
32
|
+
|
|
33
|
+
def on_app_init(self, app_config: "AppConfig") -> "AppConfig":
|
|
34
|
+
"""Configure application for use with SQLSpec.
|
|
29
35
|
|
|
30
36
|
Args:
|
|
31
37
|
app_config: The :class:`AppConfig <.config.app.AppConfig>` instance.
|
|
32
38
|
"""
|
|
33
|
-
|
|
39
|
+
|
|
40
|
+
from sqlspec.base import ConfigManager
|
|
41
|
+
|
|
42
|
+
app_config.signature_types.append(ConfigManager)
|
|
34
43
|
return app_config
|
sqlspec/filters.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"""Collection filter datastructures."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
from abc import ABC
|
|
6
|
-
from collections import abc
|
|
4
|
+
from collections import abc
|
|
7
5
|
from dataclasses import dataclass
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
from typing import Generic, Literal, Protocol
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Generic, Literal, Optional, Protocol, Union
|
|
10
8
|
|
|
11
9
|
from typing_extensions import TypeVar
|
|
12
10
|
|
|
@@ -42,9 +40,9 @@ class BeforeAfter(StatementFilter):
|
|
|
42
40
|
|
|
43
41
|
field_name: str
|
|
44
42
|
"""Name of the model attribute to filter on."""
|
|
45
|
-
before: datetime
|
|
43
|
+
before: Optional[datetime] = None
|
|
46
44
|
"""Filter results where field earlier than this."""
|
|
47
|
-
after: datetime
|
|
45
|
+
after: Optional[datetime] = None
|
|
48
46
|
"""Filter results where field later than this."""
|
|
49
47
|
|
|
50
48
|
|
|
@@ -54,9 +52,9 @@ class OnBeforeAfter(StatementFilter):
|
|
|
54
52
|
|
|
55
53
|
field_name: str
|
|
56
54
|
"""Name of the model attribute to filter on."""
|
|
57
|
-
on_or_before: datetime
|
|
55
|
+
on_or_before: Optional[datetime] = None
|
|
58
56
|
"""Filter results where field is on or earlier than this."""
|
|
59
|
-
on_or_after: datetime
|
|
57
|
+
on_or_after: Optional[datetime] = None
|
|
60
58
|
"""Filter results where field on or later than this."""
|
|
61
59
|
|
|
62
60
|
|
|
@@ -70,7 +68,7 @@ class CollectionFilter(InAnyFilter, Generic[T]):
|
|
|
70
68
|
|
|
71
69
|
field_name: str
|
|
72
70
|
"""Name of the model attribute to filter on."""
|
|
73
|
-
values: abc.Collection[T]
|
|
71
|
+
values: Optional[abc.Collection[T]]
|
|
74
72
|
"""Values for ``IN`` clause.
|
|
75
73
|
|
|
76
74
|
An empty list will return an empty result set, however, if ``None``, the filter is not applied to the query, and all rows are returned. """
|
|
@@ -82,7 +80,7 @@ class NotInCollectionFilter(InAnyFilter, Generic[T]):
|
|
|
82
80
|
|
|
83
81
|
field_name: str
|
|
84
82
|
"""Name of the model attribute to filter on."""
|
|
85
|
-
values: abc.Collection[T]
|
|
83
|
+
values: Optional[abc.Collection[T]]
|
|
86
84
|
"""Values for ``NOT IN`` clause.
|
|
87
85
|
|
|
88
86
|
An empty list or ``None`` will return all rows."""
|
|
@@ -116,11 +114,11 @@ class OrderBy(StatementFilter):
|
|
|
116
114
|
class SearchFilter(StatementFilter):
|
|
117
115
|
"""Data required to construct a ``WHERE field_name LIKE '%' || :value || '%'`` clause."""
|
|
118
116
|
|
|
119
|
-
field_name: str
|
|
117
|
+
field_name: Union[str, set[str]]
|
|
120
118
|
"""Name of the model attribute to search on."""
|
|
121
119
|
value: str
|
|
122
120
|
"""Search value."""
|
|
123
|
-
ignore_case: bool
|
|
121
|
+
ignore_case: Optional[bool] = False
|
|
124
122
|
"""Should the search be case insensitive."""
|
|
125
123
|
|
|
126
124
|
|