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/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
- "NoPoolConfig",
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, *args: Any, **kwargs: Any
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, *args: Any, **kwargs: Any
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 NoPoolConfig(DatabaseConfigProtocol[ConnectionT, None]):
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 GenericDatabaseConfig:
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 __future__ import annotations
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 | None = None) -> None:
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 | None = None) -> None:
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 | None = None) -> None:
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 __future__ import annotations
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
- """Aiosql plugin."""
12
+ """SQLSpec plugin."""
16
13
 
17
14
  __slots__ = ("_config",)
18
15
 
19
- def __init__(self, config: Any) -> None:
20
- """Initialize ``AiosqlPlugin``.
16
+ def __init__(self, config: "ConfigManager") -> None:
17
+ """Initialize ``SQLSpecPlugin``.
21
18
 
22
19
  Args:
23
- config: configure and start Aiosql.
20
+ config: configure SQLSpec plugin for use with Litestar.
24
21
  """
25
22
  self._config = config
26
23
 
27
- def on_app_init(self, app_config: AppConfig) -> AppConfig:
28
- """Configure application for use with Aiosql.
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
- app_config.signature_namespace.update(self._config.signature_namespace)
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 # noqa: TC003
4
+ from collections import abc
7
5
  from dataclasses import dataclass
8
- from datetime import datetime # noqa: TC003
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 | None
43
+ before: Optional[datetime] = None
46
44
  """Filter results where field earlier than this."""
47
- after: datetime | None
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 | None
55
+ on_or_before: Optional[datetime] = None
58
56
  """Filter results where field is on or earlier than this."""
59
- on_or_after: datetime | None
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] | None
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] | None
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 | set[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 | None = False
121
+ ignore_case: Optional[bool] = False
124
122
  """Should the search be case insensitive."""
125
123
 
126
124