sqlspec 0.25.0__py3-none-any.whl → 0.27.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 +7 -15
- sqlspec/_serialization.py +256 -24
- sqlspec/_typing.py +71 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +870 -0
- sqlspec/adapters/adbc/config.py +69 -12
- sqlspec/adapters/adbc/data_dictionary.py +340 -0
- sqlspec/adapters/adbc/driver.py +266 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +153 -0
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +527 -0
- sqlspec/adapters/aiosqlite/config.py +88 -15
- sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
- sqlspec/adapters/aiosqlite/driver.py +143 -40
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +2 -2
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +68 -23
- sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
- sqlspec/adapters/asyncmy/driver.py +313 -58
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +450 -0
- sqlspec/adapters/asyncpg/config.py +59 -35
- sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
- sqlspec/adapters/asyncpg/driver.py +170 -25
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +576 -0
- sqlspec/adapters/bigquery/config.py +27 -10
- sqlspec/adapters/bigquery/data_dictionary.py +149 -0
- sqlspec/adapters/bigquery/driver.py +368 -142
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +125 -0
- sqlspec/adapters/duckdb/_types.py +1 -1
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +80 -20
- sqlspec/adapters/duckdb/data_dictionary.py +163 -0
- sqlspec/adapters/duckdb/driver.py +167 -45
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +4 -4
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1745 -0
- sqlspec/adapters/oracledb/config.py +122 -32
- sqlspec/adapters/oracledb/data_dictionary.py +509 -0
- sqlspec/adapters/oracledb/driver.py +353 -91
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +348 -73
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +482 -0
- sqlspec/adapters/psqlpy/config.py +46 -17
- sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
- sqlspec/adapters/psqlpy/driver.py +123 -209
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +102 -0
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +944 -0
- sqlspec/adapters/psycopg/config.py +69 -35
- sqlspec/adapters/psycopg/data_dictionary.py +331 -0
- sqlspec/adapters/psycopg/driver.py +238 -81
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +572 -0
- sqlspec/adapters/sqlite/config.py +87 -15
- sqlspec/adapters/sqlite/data_dictionary.py +149 -0
- sqlspec/adapters/sqlite/driver.py +137 -54
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +18 -9
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +162 -89
- sqlspec/builder/_column.py +62 -29
- sqlspec/builder/_ddl.py +180 -121
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +53 -94
- sqlspec/builder/_insert.py +32 -131
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +111 -17
- sqlspec/builder/_select.py +1457 -24
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +307 -194
- sqlspec/config.py +252 -67
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +17 -17
- sqlspec/core/compiler.py +62 -9
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +83 -48
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +36 -30
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +7 -6
- sqlspec/driver/_async.py +188 -151
- sqlspec/driver/_common.py +285 -80
- sqlspec/driver/_sync.py +188 -152
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +75 -7
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/adapter.py +73 -53
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +59 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +324 -223
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/__init__.py +4 -3
- sqlspec/migrations/base.py +302 -39
- sqlspec/migrations/commands.py +611 -144
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +68 -23
- sqlspec/migrations/runner.py +543 -107
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +66 -36
- sqlspec/storage/_utils.py +98 -0
- sqlspec/storage/backends/fsspec.py +134 -106
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +278 -162
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +16 -84
- sqlspec/utils/config_resolver.py +153 -0
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +2 -2
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +50 -2
- sqlspec/utils/sync_tools.py +21 -17
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +111 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
- sqlspec-0.27.0.dist-info/RECORD +207 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_join_operations.py +0 -389
- sqlspec/builder/mixins/_merge_operations.py +0 -592
- sqlspec/builder/mixins/_order_limit_operations.py +0 -152
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -936
- sqlspec/builder/mixins/_update_operations.py +0 -218
- sqlspec/builder/mixins/_where_clause.py +0 -1304
- sqlspec-0.25.0.dist-info/RECORD +0 -139
- sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/storage/registry.py
CHANGED
|
@@ -8,33 +8,19 @@ scheme-based routing, and named aliases for common configurations.
|
|
|
8
8
|
import logging
|
|
9
9
|
import re
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any, Final,
|
|
11
|
+
from typing import Any, Final, cast
|
|
12
12
|
|
|
13
13
|
from mypy_extensions import mypyc_attr
|
|
14
14
|
|
|
15
15
|
from sqlspec.exceptions import ImproperConfigurationError, MissingDependencyError
|
|
16
16
|
from sqlspec.protocols import ObjectStoreProtocol
|
|
17
17
|
from sqlspec.typing import FSSPEC_INSTALLED, OBSTORE_INSTALLED
|
|
18
|
+
from sqlspec.utils.type_guards import is_local_path
|
|
18
19
|
|
|
19
20
|
__all__ = ("StorageRegistry", "storage_registry")
|
|
20
21
|
|
|
21
22
|
logger = logging.getLogger(__name__)
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
def _is_local_uri(uri: str) -> bool:
|
|
25
|
-
"""Check if URI represents a local filesystem path."""
|
|
26
|
-
if "://" in uri and not uri.startswith("file://"):
|
|
27
|
-
return False
|
|
28
|
-
windows_drive_min_length = 3
|
|
29
|
-
return (
|
|
30
|
-
Path(uri).exists()
|
|
31
|
-
or Path(uri).is_absolute()
|
|
32
|
-
or uri.startswith(("~", ".", "/"))
|
|
33
|
-
or (len(uri) >= windows_drive_min_length and uri[1:3] == ":\\")
|
|
34
|
-
or "/" in uri
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
24
|
SCHEME_REGEX: Final = re.compile(r"([a-zA-Z0-9+.-]+)://")
|
|
39
25
|
|
|
40
26
|
|
|
@@ -74,7 +60,7 @@ class StorageRegistry:
|
|
|
74
60
|
def __init__(self) -> None:
|
|
75
61
|
self._alias_configs: dict[str, tuple[type[ObjectStoreProtocol], str, dict[str, Any]]] = {}
|
|
76
62
|
self._aliases: dict[str, dict[str, Any]] = {}
|
|
77
|
-
self._instances: dict[
|
|
63
|
+
self._instances: dict[str | tuple[str, tuple[tuple[str, Any], ...]], ObjectStoreProtocol] = {}
|
|
78
64
|
self._cache: dict[str, tuple[str, type[ObjectStoreProtocol]]] = {}
|
|
79
65
|
|
|
80
66
|
def _make_hashable(self, obj: Any) -> Any:
|
|
@@ -88,7 +74,7 @@ class StorageRegistry:
|
|
|
88
74
|
return obj
|
|
89
75
|
|
|
90
76
|
def register_alias(
|
|
91
|
-
self, alias: str, uri: str, *, backend:
|
|
77
|
+
self, alias: str, uri: str, *, backend: str | None = None, base_path: str = "", **kwargs: Any
|
|
92
78
|
) -> None:
|
|
93
79
|
"""Register a named alias for a storage configuration.
|
|
94
80
|
|
|
@@ -110,9 +96,7 @@ class StorageRegistry:
|
|
|
110
96
|
test_config["uri"] = uri
|
|
111
97
|
self._aliases[alias] = test_config
|
|
112
98
|
|
|
113
|
-
def get(
|
|
114
|
-
self, uri_or_alias: Union[str, Path], *, backend: Optional[str] = None, **kwargs: Any
|
|
115
|
-
) -> ObjectStoreProtocol:
|
|
99
|
+
def get(self, uri_or_alias: str | Path, *, backend: str | None = None, **kwargs: Any) -> ObjectStoreProtocol:
|
|
116
100
|
"""Get backend instance using URI-first routing with automatic backend selection.
|
|
117
101
|
|
|
118
102
|
Args:
|
|
@@ -133,11 +117,15 @@ class StorageRegistry:
|
|
|
133
117
|
if isinstance(uri_or_alias, Path):
|
|
134
118
|
uri_or_alias = f"file://{uri_or_alias.resolve()}"
|
|
135
119
|
|
|
136
|
-
|
|
120
|
+
# Include backend in cache key to ensure different backends for same URI are cached separately
|
|
121
|
+
cache_params = dict(kwargs)
|
|
122
|
+
if backend:
|
|
123
|
+
cache_params["__backend__"] = backend
|
|
124
|
+
cache_key = (uri_or_alias, self._make_hashable(cache_params)) if cache_params else uri_or_alias
|
|
137
125
|
if cache_key in self._instances:
|
|
138
126
|
return self._instances[cache_key]
|
|
139
127
|
scheme = self._get_scheme(uri_or_alias)
|
|
140
|
-
if not scheme and
|
|
128
|
+
if not scheme and is_local_path(uri_or_alias):
|
|
141
129
|
scheme = "file"
|
|
142
130
|
uri_or_alias = f"file://{uri_or_alias}"
|
|
143
131
|
|
|
@@ -154,57 +142,105 @@ class StorageRegistry:
|
|
|
154
142
|
self._instances[cache_key] = instance
|
|
155
143
|
return instance
|
|
156
144
|
|
|
157
|
-
def _resolve_from_uri(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
145
|
+
def _resolve_from_uri(self, uri: str, *, backend_override: str | None = None, **kwargs: Any) -> ObjectStoreProtocol:
|
|
146
|
+
"""Resolve backend from URI with optional backend override.
|
|
147
|
+
|
|
148
|
+
Backend selection priority for local files (file:// or bare paths):
|
|
149
|
+
1. obstore (if installed) - provides async I/O performance
|
|
150
|
+
2. fsspec (if installed) - async wrapper fallback
|
|
151
|
+
3. local (always available) - zero-dependency sync backend
|
|
152
|
+
|
|
153
|
+
For cloud storage, prefer obstore over fsspec when available.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
uri: Storage URI to resolve.
|
|
157
|
+
backend_override: Force specific backend type.
|
|
158
|
+
**kwargs: Additional backend configuration.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Configured backend instance.
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
MissingDependencyError: No backend available for URI scheme.
|
|
165
|
+
"""
|
|
161
166
|
if backend_override:
|
|
162
167
|
return self._create_backend(backend_override, uri, **kwargs)
|
|
168
|
+
|
|
163
169
|
scheme = self._get_scheme(uri)
|
|
164
170
|
|
|
165
|
-
#
|
|
171
|
+
# NEW: Prefer obstore for local files when available
|
|
166
172
|
if scheme in {None, "file"}:
|
|
173
|
+
# Try obstore first for async performance
|
|
174
|
+
if OBSTORE_INSTALLED:
|
|
175
|
+
try:
|
|
176
|
+
return self._create_backend("obstore", uri, **kwargs)
|
|
177
|
+
except (ValueError, ImportError, NotImplementedError):
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
# Fallback to fsspec if available
|
|
181
|
+
if FSSPEC_INSTALLED:
|
|
182
|
+
try:
|
|
183
|
+
return self._create_backend("fsspec", uri, **kwargs)
|
|
184
|
+
except (ValueError, ImportError, NotImplementedError):
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
# Final fallback: local zero-dependency backend
|
|
167
188
|
return self._create_backend("local", uri, **kwargs)
|
|
168
189
|
|
|
169
|
-
#
|
|
190
|
+
# For cloud schemes, prefer obstore over fsspec
|
|
170
191
|
if scheme not in FSSPEC_ONLY_SCHEMES and OBSTORE_INSTALLED:
|
|
171
192
|
try:
|
|
172
193
|
return self._create_backend("obstore", uri, **kwargs)
|
|
173
194
|
except (ValueError, ImportError, NotImplementedError):
|
|
174
195
|
pass
|
|
175
196
|
|
|
176
|
-
# Try
|
|
197
|
+
# Try fsspec if available
|
|
177
198
|
if FSSPEC_INSTALLED:
|
|
178
199
|
try:
|
|
179
200
|
return self._create_backend("fsspec", uri, **kwargs)
|
|
180
201
|
except (ValueError, ImportError, NotImplementedError):
|
|
181
202
|
pass
|
|
182
203
|
|
|
183
|
-
#
|
|
204
|
+
# No backend available
|
|
184
205
|
msg = f"No backend available for URI scheme '{scheme}'. Install obstore or fsspec for cloud storage support."
|
|
185
206
|
raise MissingDependencyError(msg)
|
|
186
207
|
|
|
187
208
|
def _determine_backend_class(self, uri: str) -> type[ObjectStoreProtocol]:
|
|
188
|
-
"""Determine the backend class for a URI based on availability.
|
|
209
|
+
"""Determine the backend class for a URI based on availability.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
uri: Storage URI to analyze.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Backend class type to use.
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
MissingDependencyError: No backend available for URI scheme.
|
|
219
|
+
"""
|
|
189
220
|
scheme = self._get_scheme(uri)
|
|
190
221
|
|
|
191
|
-
# For local files,
|
|
222
|
+
# NEW: For local files, prefer obstore > fsspec > local
|
|
192
223
|
if scheme in {None, "file"}:
|
|
224
|
+
if OBSTORE_INSTALLED:
|
|
225
|
+
return self._get_backend_class("obstore")
|
|
226
|
+
if FSSPEC_INSTALLED:
|
|
227
|
+
return self._get_backend_class("fsspec")
|
|
193
228
|
return self._get_backend_class("local")
|
|
194
229
|
|
|
195
230
|
# FSSpec-only schemes require FSSpec
|
|
196
|
-
if scheme in FSSPEC_ONLY_SCHEMES
|
|
231
|
+
if scheme in FSSPEC_ONLY_SCHEMES:
|
|
232
|
+
if not FSSPEC_INSTALLED:
|
|
233
|
+
msg = f"Scheme '{scheme}' requires fsspec. Install with: pip install fsspec"
|
|
234
|
+
raise MissingDependencyError(msg)
|
|
197
235
|
return self._get_backend_class("fsspec")
|
|
198
236
|
|
|
199
|
-
#
|
|
237
|
+
# For cloud schemes, prefer obstore
|
|
200
238
|
if OBSTORE_INSTALLED:
|
|
201
239
|
return self._get_backend_class("obstore")
|
|
202
240
|
|
|
203
|
-
# Fall back to FSSpec if available
|
|
204
241
|
if FSSPEC_INSTALLED:
|
|
205
242
|
return self._get_backend_class("fsspec")
|
|
206
243
|
|
|
207
|
-
# For cloud schemes without backends, provide helpful error
|
|
208
244
|
msg = f"No backend available for URI scheme '{scheme}'. Install obstore or fsspec for cloud storage support."
|
|
209
245
|
raise MissingDependencyError(msg)
|
|
210
246
|
|
|
@@ -229,7 +265,7 @@ class StorageRegistry:
|
|
|
229
265
|
"""Create backend instance for URI."""
|
|
230
266
|
return self._get_backend_class(backend_type)(uri, **kwargs)
|
|
231
267
|
|
|
232
|
-
def _get_scheme(self, uri: str) ->
|
|
268
|
+
def _get_scheme(self, uri: str) -> str | None:
|
|
233
269
|
"""Extract the scheme from a URI using regex."""
|
|
234
270
|
if not uri:
|
|
235
271
|
return None
|
|
@@ -244,7 +280,7 @@ class StorageRegistry:
|
|
|
244
280
|
"""List all registered aliases."""
|
|
245
281
|
return list(self._alias_configs.keys())
|
|
246
282
|
|
|
247
|
-
def clear_cache(self, uri_or_alias:
|
|
283
|
+
def clear_cache(self, uri_or_alias: str | None = None) -> None:
|
|
248
284
|
"""Clear resolved backend cache."""
|
|
249
285
|
if uri_or_alias:
|
|
250
286
|
self._instances.pop(uri_or_alias, None)
|
sqlspec/typing.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# pyright: ignore[reportAttributeAccessIssue]
|
|
2
|
-
from collections.abc import Iterator
|
|
2
|
+
from collections.abc import Iterator
|
|
3
3
|
from functools import lru_cache
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Annotated, Any, Protocol, TypeAlias, _TypedDict # pyright: ignore
|
|
5
5
|
|
|
6
|
-
from typing_extensions import
|
|
6
|
+
from typing_extensions import TypeVar
|
|
7
7
|
|
|
8
8
|
from sqlspec._typing import (
|
|
9
9
|
AIOSQL_INSTALLED,
|
|
@@ -15,6 +15,7 @@ from sqlspec._typing import (
|
|
|
15
15
|
NUMPY_INSTALLED,
|
|
16
16
|
OBSTORE_INSTALLED,
|
|
17
17
|
OPENTELEMETRY_INSTALLED,
|
|
18
|
+
ORJSON_INSTALLED,
|
|
18
19
|
PGVECTOR_INSTALLED,
|
|
19
20
|
PROMETHEUS_INSTALLED,
|
|
20
21
|
PYARROW_INSTALLED,
|
|
@@ -22,7 +23,6 @@ from sqlspec._typing import (
|
|
|
22
23
|
UNSET,
|
|
23
24
|
AiosqlAsyncProtocol,
|
|
24
25
|
AiosqlParamType,
|
|
25
|
-
AiosqlProtocol,
|
|
26
26
|
AiosqlSQLOperationType,
|
|
27
27
|
AiosqlSyncProtocol,
|
|
28
28
|
ArrowRecordBatch,
|
|
@@ -40,6 +40,7 @@ from sqlspec._typing import (
|
|
|
40
40
|
FailFast,
|
|
41
41
|
Gauge,
|
|
42
42
|
Histogram,
|
|
43
|
+
NumpyArray,
|
|
43
44
|
Span,
|
|
44
45
|
Status,
|
|
45
46
|
StatusCode,
|
|
@@ -60,9 +61,6 @@ from sqlspec._typing import (
|
|
|
60
61
|
trace,
|
|
61
62
|
)
|
|
62
63
|
|
|
63
|
-
if TYPE_CHECKING:
|
|
64
|
-
from collections.abc import Sequence
|
|
65
|
-
|
|
66
64
|
|
|
67
65
|
class DictLike(Protocol):
|
|
68
66
|
"""A protocol for objects that behave like a dictionary for reading."""
|
|
@@ -86,30 +84,22 @@ PoolT = TypeVar("PoolT")
|
|
|
86
84
|
|
|
87
85
|
:class:`~sqlspec.typing.PoolT`
|
|
88
86
|
"""
|
|
89
|
-
|
|
90
|
-
"""Type variable for
|
|
91
|
-
|
|
92
|
-
:class:`~sqlspec.typing.PoolT_co`
|
|
93
|
-
"""
|
|
94
|
-
ModelT = TypeVar("ModelT", bound="Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub]")
|
|
95
|
-
"""Type variable for model types.
|
|
87
|
+
SchemaT = TypeVar("SchemaT", default=dict[str, Any])
|
|
88
|
+
"""Type variable for schema types (models, TypedDict, dataclasses, etc.).
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
Unbounded TypeVar for use with schema_type parameter in driver methods.
|
|
91
|
+
Supports all schema types including TypedDict which cannot be bounded to a class hierarchy.
|
|
98
92
|
"""
|
|
99
|
-
RowT = TypeVar("RowT", bound="dict[str, Any]")
|
|
100
|
-
|
|
101
93
|
|
|
102
|
-
DictRow: TypeAlias = "dict[str, Any]"
|
|
103
|
-
"""Type variable for DictRow types."""
|
|
104
|
-
TupleRow: TypeAlias = "tuple[Any, ...]"
|
|
105
|
-
"""Type variable for TupleRow types."""
|
|
106
94
|
|
|
107
|
-
SupportedSchemaModel: TypeAlias =
|
|
95
|
+
SupportedSchemaModel: TypeAlias = (
|
|
96
|
+
DictLike | StructStub | BaseModelStub | DataclassProtocol | AttrsInstanceStub | _TypedDict
|
|
97
|
+
)
|
|
108
98
|
"""Type alias for pydantic or msgspec models.
|
|
109
99
|
|
|
110
100
|
:class:`msgspec.Struct` | :class:`pydantic.BaseModel` | :class:`DataclassProtocol` | :class:`AttrsInstance`
|
|
111
101
|
"""
|
|
112
|
-
StatementParameters: TypeAlias = "
|
|
102
|
+
StatementParameters: TypeAlias = "Any | dict[str, Any] | list[Any] | tuple[Any, ...] | None"
|
|
113
103
|
"""Type alias for statement parameters.
|
|
114
104
|
|
|
115
105
|
Represents:
|
|
@@ -118,40 +108,6 @@ Represents:
|
|
|
118
108
|
- :type:`tuple[Any, ...]`
|
|
119
109
|
- :type:`None`
|
|
120
110
|
"""
|
|
121
|
-
ModelDTOT = TypeVar("ModelDTOT", bound="SupportedSchemaModel")
|
|
122
|
-
"""Type variable for model DTOs.
|
|
123
|
-
|
|
124
|
-
:class:`msgspec.Struct`|:class:`pydantic.BaseModel`
|
|
125
|
-
"""
|
|
126
|
-
PydanticOrMsgspecT = SupportedSchemaModel
|
|
127
|
-
"""Type alias for pydantic or msgspec models.
|
|
128
|
-
|
|
129
|
-
:class:`msgspec.Struct` or :class:`pydantic.BaseModel`
|
|
130
|
-
"""
|
|
131
|
-
ModelDict: TypeAlias = (
|
|
132
|
-
"Union[dict[str, Any], Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub], Any]"
|
|
133
|
-
)
|
|
134
|
-
"""Type alias for model dictionaries.
|
|
135
|
-
|
|
136
|
-
Represents:
|
|
137
|
-
- :type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`
|
|
138
|
-
"""
|
|
139
|
-
ModelDictList: TypeAlias = (
|
|
140
|
-
"Sequence[Union[dict[str, Any], Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub]]]"
|
|
141
|
-
)
|
|
142
|
-
"""Type alias for model dictionary lists.
|
|
143
|
-
|
|
144
|
-
A list or sequence of any of the following:
|
|
145
|
-
- :type:`Sequence`[:type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`]
|
|
146
|
-
|
|
147
|
-
"""
|
|
148
|
-
BulkModelDict: TypeAlias = "Union[Sequence[Union[dict[str, Any], Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub]]], Any]"
|
|
149
|
-
"""Type alias for bulk model dictionaries.
|
|
150
|
-
|
|
151
|
-
Represents:
|
|
152
|
-
- :type:`Sequence`[:type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`]
|
|
153
|
-
- :class:`DTOData`[:type:`list[ModelT]`]
|
|
154
|
-
"""
|
|
155
111
|
|
|
156
112
|
|
|
157
113
|
@lru_cache(typed=True)
|
|
@@ -169,18 +125,6 @@ def get_type_adapter(f: "type[T]") -> Any:
|
|
|
169
125
|
return TypeAdapter(f)
|
|
170
126
|
|
|
171
127
|
|
|
172
|
-
def MixinOf(base: type[T]) -> type[T]: # noqa: N802
|
|
173
|
-
"""Useful function to make mixins with baseclass type hint
|
|
174
|
-
|
|
175
|
-
```
|
|
176
|
-
class StorageMixin(MixinOf(DriverProtocol)): ...
|
|
177
|
-
```
|
|
178
|
-
"""
|
|
179
|
-
if TYPE_CHECKING:
|
|
180
|
-
return base
|
|
181
|
-
return type("<MixinOf>", (base,), {})
|
|
182
|
-
|
|
183
|
-
|
|
184
128
|
__all__ = (
|
|
185
129
|
"AIOSQL_INSTALLED",
|
|
186
130
|
"ATTRS_INSTALLED",
|
|
@@ -191,6 +135,7 @@ __all__ = (
|
|
|
191
135
|
"NUMPY_INSTALLED",
|
|
192
136
|
"OBSTORE_INSTALLED",
|
|
193
137
|
"OPENTELEMETRY_INSTALLED",
|
|
138
|
+
"ORJSON_INSTALLED",
|
|
194
139
|
"PGVECTOR_INSTALLED",
|
|
195
140
|
"PROMETHEUS_INSTALLED",
|
|
196
141
|
"PYARROW_INSTALLED",
|
|
@@ -199,38 +144,26 @@ __all__ = (
|
|
|
199
144
|
"UNSET",
|
|
200
145
|
"AiosqlAsyncProtocol",
|
|
201
146
|
"AiosqlParamType",
|
|
202
|
-
"AiosqlProtocol",
|
|
203
147
|
"AiosqlSQLOperationType",
|
|
204
148
|
"AiosqlSyncProtocol",
|
|
205
149
|
"ArrowRecordBatch",
|
|
206
150
|
"ArrowTable",
|
|
207
151
|
"AttrsInstance",
|
|
208
152
|
"BaseModel",
|
|
209
|
-
"BulkModelDict",
|
|
210
153
|
"ConnectionT",
|
|
211
154
|
"Counter",
|
|
212
155
|
"DTOData",
|
|
213
156
|
"DataclassProtocol",
|
|
214
157
|
"DictLike",
|
|
215
|
-
"DictRow",
|
|
216
158
|
"Empty",
|
|
217
159
|
"EmptyEnum",
|
|
218
160
|
"EmptyType",
|
|
219
161
|
"FailFast",
|
|
220
162
|
"Gauge",
|
|
221
163
|
"Histogram",
|
|
222
|
-
"
|
|
223
|
-
"MixinOf",
|
|
224
|
-
"ModelDTOT",
|
|
225
|
-
"ModelDict",
|
|
226
|
-
"ModelDict",
|
|
227
|
-
"ModelDictList",
|
|
228
|
-
"ModelDictList",
|
|
229
|
-
"ModelT",
|
|
164
|
+
"NumpyArray",
|
|
230
165
|
"PoolT",
|
|
231
|
-
"
|
|
232
|
-
"PydanticOrMsgspecT",
|
|
233
|
-
"RowT",
|
|
166
|
+
"SchemaT",
|
|
234
167
|
"Span",
|
|
235
168
|
"StatementParameters",
|
|
236
169
|
"Status",
|
|
@@ -238,7 +171,6 @@ __all__ = (
|
|
|
238
171
|
"Struct",
|
|
239
172
|
"SupportedSchemaModel",
|
|
240
173
|
"Tracer",
|
|
241
|
-
"TupleRow",
|
|
242
174
|
"TypeAdapter",
|
|
243
175
|
"UnsetType",
|
|
244
176
|
"aiosql",
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Configuration resolver for SQLSpec CLI.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for resolving configuration objects from dotted paths,
|
|
4
|
+
with support for both direct config instances and callable functions that return configs.
|
|
5
|
+
Supports both synchronous and asynchronous callable functions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import inspect
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
11
|
+
|
|
12
|
+
from sqlspec.exceptions import ConfigResolverError
|
|
13
|
+
from sqlspec.utils.module_loader import import_string
|
|
14
|
+
from sqlspec.utils.sync_tools import async_, await_
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
|
|
18
|
+
|
|
19
|
+
__all__ = ("resolve_config_async", "resolve_config_sync")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def resolve_config_async(
|
|
23
|
+
config_path: str,
|
|
24
|
+
) -> "list[AsyncDatabaseConfig | SyncDatabaseConfig] | AsyncDatabaseConfig | SyncDatabaseConfig":
|
|
25
|
+
"""Resolve config from dotted path, handling callables and direct instances.
|
|
26
|
+
|
|
27
|
+
This is the async-first version that handles both sync and async callables efficiently.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
config_path: Dotted path to config object or callable function.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Resolved config instance or list of config instances.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ConfigResolverError: If config resolution fails.
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
config_obj = import_string(config_path)
|
|
40
|
+
except ImportError as e:
|
|
41
|
+
msg = f"Failed to import config from path '{config_path}': {e}"
|
|
42
|
+
raise ConfigResolverError(msg) from e
|
|
43
|
+
|
|
44
|
+
if not callable(config_obj):
|
|
45
|
+
return _validate_config_result(config_obj, config_path)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
if inspect.iscoroutinefunction(config_obj):
|
|
49
|
+
result = await config_obj()
|
|
50
|
+
else:
|
|
51
|
+
result = await async_(config_obj)()
|
|
52
|
+
except Exception as e:
|
|
53
|
+
msg = f"Failed to execute callable config '{config_path}': {e}"
|
|
54
|
+
raise ConfigResolverError(msg) from e
|
|
55
|
+
|
|
56
|
+
return _validate_config_result(result, config_path)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def resolve_config_sync(
|
|
60
|
+
config_path: str,
|
|
61
|
+
) -> "list[AsyncDatabaseConfig | SyncDatabaseConfig] | AsyncDatabaseConfig | SyncDatabaseConfig":
|
|
62
|
+
"""Synchronous wrapper for resolve_config.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
config_path: Dotted path to config object or callable function.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Resolved config instance or list of config instances.
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
config_obj = import_string(config_path)
|
|
72
|
+
except ImportError as e:
|
|
73
|
+
msg = f"Failed to import config from path '{config_path}': {e}"
|
|
74
|
+
raise ConfigResolverError(msg) from e
|
|
75
|
+
|
|
76
|
+
if not callable(config_obj):
|
|
77
|
+
return _validate_config_result(config_obj, config_path)
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
if inspect.iscoroutinefunction(config_obj):
|
|
81
|
+
result = await_(config_obj, raise_sync_error=False)()
|
|
82
|
+
else:
|
|
83
|
+
result = config_obj()
|
|
84
|
+
except Exception as e:
|
|
85
|
+
msg = f"Failed to execute callable config '{config_path}': {e}"
|
|
86
|
+
raise ConfigResolverError(msg) from e
|
|
87
|
+
|
|
88
|
+
return _validate_config_result(result, config_path)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _validate_config_result(
|
|
92
|
+
config_result: Any, config_path: str
|
|
93
|
+
) -> "list[AsyncDatabaseConfig | SyncDatabaseConfig] | AsyncDatabaseConfig | SyncDatabaseConfig":
|
|
94
|
+
"""Validate that the config result is a valid config or list of configs.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
config_result: The result from config resolution.
|
|
98
|
+
config_path: Original config path for error messages.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Validated config result.
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
ConfigResolverError: If config result is invalid.
|
|
105
|
+
"""
|
|
106
|
+
if config_result is None:
|
|
107
|
+
msg = f"Config '{config_path}' resolved to None. Expected config instance or list of configs."
|
|
108
|
+
raise ConfigResolverError(msg)
|
|
109
|
+
|
|
110
|
+
if isinstance(config_result, Sequence) and not isinstance(config_result, str):
|
|
111
|
+
if not config_result:
|
|
112
|
+
msg = f"Config '{config_path}' resolved to empty list. Expected at least one config."
|
|
113
|
+
raise ConfigResolverError(msg)
|
|
114
|
+
|
|
115
|
+
for i, config in enumerate(config_result):
|
|
116
|
+
if not _is_valid_config(config):
|
|
117
|
+
msg = f"Config '{config_path}' returned invalid config at index {i}. Expected database config instance."
|
|
118
|
+
raise ConfigResolverError(msg)
|
|
119
|
+
|
|
120
|
+
return cast("list[AsyncDatabaseConfig | SyncDatabaseConfig]", list(config_result))
|
|
121
|
+
|
|
122
|
+
if not _is_valid_config(config_result):
|
|
123
|
+
msg = f"Config '{config_path}' returned invalid type '{type(config_result).__name__}'. Expected database config instance or list."
|
|
124
|
+
raise ConfigResolverError(msg)
|
|
125
|
+
|
|
126
|
+
return cast("AsyncDatabaseConfig | SyncDatabaseConfig", config_result)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _is_valid_config(config: Any) -> bool:
|
|
130
|
+
"""Check if an object is a valid SQLSpec database config.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
config: Object to validate.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
True if object appears to be a valid config.
|
|
137
|
+
"""
|
|
138
|
+
# Check for litestar extension DatabaseConfig wrapper
|
|
139
|
+
nested_config = getattr(config, "config", None)
|
|
140
|
+
if nested_config is not None and hasattr(nested_config, "migration_config"):
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
# Check for direct database config with migration support
|
|
144
|
+
migration_config = getattr(config, "migration_config", None)
|
|
145
|
+
if migration_config is not None:
|
|
146
|
+
# Modern SQLSpec config with pool_config
|
|
147
|
+
if hasattr(config, "pool_config"):
|
|
148
|
+
return True
|
|
149
|
+
# Legacy config with database_url and bind_key
|
|
150
|
+
if hasattr(config, "database_url") and hasattr(config, "bind_key"):
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
return False
|
sqlspec/utils/correlation.py
CHANGED
|
@@ -4,15 +4,14 @@ This module provides utilities for tracking correlation IDs across
|
|
|
4
4
|
database operations, enabling distributed tracing and debugging.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
7
|
import uuid
|
|
8
|
+
from collections.abc import Generator
|
|
10
9
|
from contextlib import contextmanager
|
|
11
10
|
from contextvars import ContextVar
|
|
12
11
|
from typing import TYPE_CHECKING, Any
|
|
13
12
|
|
|
14
13
|
if TYPE_CHECKING:
|
|
15
|
-
from collections.abc import
|
|
14
|
+
from collections.abc import MutableMapping
|
|
16
15
|
from logging import LoggerAdapter
|
|
17
16
|
|
|
18
17
|
__all__ = ("CorrelationContext", "correlation_context", "get_correlation_adapter")
|
|
@@ -115,7 +114,7 @@ def correlation_context(correlation_id: str | None = None) -> Generator[str, Non
|
|
|
115
114
|
yield cid
|
|
116
115
|
|
|
117
116
|
|
|
118
|
-
def get_correlation_adapter(logger: Any) -> LoggerAdapter:
|
|
117
|
+
def get_correlation_adapter(logger: Any) -> "LoggerAdapter":
|
|
119
118
|
"""Get a logger adapter that automatically includes correlation ID.
|
|
120
119
|
|
|
121
120
|
Args:
|
|
@@ -129,7 +128,7 @@ def get_correlation_adapter(logger: Any) -> LoggerAdapter:
|
|
|
129
128
|
class CorrelationAdapter(LoggerAdapter):
|
|
130
129
|
"""Logger adapter that adds correlation ID to all logs."""
|
|
131
130
|
|
|
132
|
-
def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, dict[str, Any]]:
|
|
131
|
+
def process(self, msg: str, kwargs: "MutableMapping[str, Any]") -> tuple[str, dict[str, Any]]:
|
|
133
132
|
"""Add correlation ID to the log record.
|
|
134
133
|
|
|
135
134
|
Args:
|
|
@@ -5,7 +5,8 @@ field name conversion when mapping database results to schema objects.
|
|
|
5
5
|
Used primarily for msgspec field name conversion with rename configurations.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
__all__ = ("transform_dict_keys",)
|
|
11
12
|
|
|
@@ -30,7 +31,7 @@ def _safe_convert_key(key: Any, converter: Callable[[str], str]) -> Any:
|
|
|
30
31
|
return key
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
def transform_dict_keys(data:
|
|
34
|
+
def transform_dict_keys(data: dict | list | Any, converter: Callable[[str], str]) -> dict | list | Any:
|
|
34
35
|
"""Transform dictionary keys using the provided converter function.
|
|
35
36
|
|
|
36
37
|
Recursively transforms all dictionary keys in a data structure using
|
sqlspec/utils/deprecation.py
CHANGED
|
@@ -5,8 +5,9 @@ Used to communicate API changes and migration paths to users.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import inspect
|
|
8
|
+
from collections.abc import Callable
|
|
8
9
|
from functools import wraps
|
|
9
|
-
from typing import
|
|
10
|
+
from typing import Literal
|
|
10
11
|
from warnings import warn
|
|
11
12
|
|
|
12
13
|
from typing_extensions import ParamSpec, TypeVar
|
|
@@ -24,9 +25,9 @@ def warn_deprecation(
|
|
|
24
25
|
deprecated_name: str,
|
|
25
26
|
kind: DeprecatedKind,
|
|
26
27
|
*,
|
|
27
|
-
removal_in:
|
|
28
|
-
alternative:
|
|
29
|
-
info:
|
|
28
|
+
removal_in: str | None = None,
|
|
29
|
+
alternative: str | None = None,
|
|
30
|
+
info: str | None = None,
|
|
30
31
|
pending: bool = False,
|
|
31
32
|
) -> None:
|
|
32
33
|
"""Warn about a call to a deprecated function.
|
|
@@ -72,11 +73,11 @@ def warn_deprecation(
|
|
|
72
73
|
def deprecated(
|
|
73
74
|
version: str,
|
|
74
75
|
*,
|
|
75
|
-
removal_in:
|
|
76
|
-
alternative:
|
|
77
|
-
info:
|
|
76
|
+
removal_in: str | None = None,
|
|
77
|
+
alternative: str | None = None,
|
|
78
|
+
info: str | None = None,
|
|
78
79
|
pending: bool = False,
|
|
79
|
-
kind:
|
|
80
|
+
kind: Literal["function", "method", "classmethod", "property"] | None = None,
|
|
80
81
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
81
82
|
"""Create a decorator wrapping a function, method or property with a deprecation warning.
|
|
82
83
|
|