sqlspec 0.26.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 +55 -25
- sqlspec/_typing.py +62 -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 +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +52 -2
- sqlspec/adapters/adbc/driver.py +144 -45
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +44 -50
- 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 +86 -16
- sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
- sqlspec/adapters/aiosqlite/driver.py +127 -38
- 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 +1 -1
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +59 -17
- sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
- sqlspec/adapters/asyncmy/driver.py +293 -62
- 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 +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +41 -2
- sqlspec/adapters/asyncpg/driver.py +153 -23
- 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 +25 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +352 -144
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +55 -23
- sqlspec/adapters/duckdb/_types.py +2 -2
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +138 -43
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +5 -5
- sqlspec/adapters/duckdb/type_converter.py +51 -21
- 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 +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +292 -84
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +316 -25
- sqlspec/adapters/oracledb/type_converter.py +91 -16
- 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 +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +41 -2
- sqlspec/adapters/psqlpy/driver.py +101 -31
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +40 -11
- 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 +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +77 -3
- sqlspec/adapters/psycopg/driver.py +200 -78
- 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 +85 -16
- sqlspec/adapters/sqlite/data_dictionary.py +34 -2
- sqlspec/adapters/sqlite/driver.py +120 -52
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +5 -5
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +91 -58
- sqlspec/builder/_column.py +5 -5
- sqlspec/builder/_ddl.py +98 -89
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +41 -44
- sqlspec/builder/_insert.py +5 -82
- sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +9 -11
- sqlspec/builder/_select.py +1313 -25
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +76 -69
- sqlspec/config.py +231 -60
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +32 -31
- sqlspec/core/type_conversion.py +3 -2
- sqlspec/driver/__init__.py +1 -3
- sqlspec/driver/_async.py +95 -161
- sqlspec/driver/_common.py +133 -80
- sqlspec/driver/_sync.py +95 -162
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +70 -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/base.py +200 -76
- sqlspec/migrations/commands.py +591 -62
- sqlspec/migrations/context.py +6 -9
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +47 -19
- sqlspec/migrations/runner.py +241 -75
- 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 +14 -84
- sqlspec/utils/config_resolver.py +6 -6
- 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 +3 -3
- 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.26.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 -253
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_merge_operations.py +0 -698
- sqlspec/builder/mixins/_order_limit_operations.py +0 -145
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -930
- sqlspec/builder/mixins/_update_operations.py +0 -199
- sqlspec/builder/mixins/_where_clause.py +0 -1298
- sqlspec-0.26.0.dist-info/RECORD +0 -157
- sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.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,
|
|
@@ -23,7 +23,6 @@ from sqlspec._typing import (
|
|
|
23
23
|
UNSET,
|
|
24
24
|
AiosqlAsyncProtocol,
|
|
25
25
|
AiosqlParamType,
|
|
26
|
-
AiosqlProtocol,
|
|
27
26
|
AiosqlSQLOperationType,
|
|
28
27
|
AiosqlSyncProtocol,
|
|
29
28
|
ArrowRecordBatch,
|
|
@@ -41,6 +40,7 @@ from sqlspec._typing import (
|
|
|
41
40
|
FailFast,
|
|
42
41
|
Gauge,
|
|
43
42
|
Histogram,
|
|
43
|
+
NumpyArray,
|
|
44
44
|
Span,
|
|
45
45
|
Status,
|
|
46
46
|
StatusCode,
|
|
@@ -61,9 +61,6 @@ from sqlspec._typing import (
|
|
|
61
61
|
trace,
|
|
62
62
|
)
|
|
63
63
|
|
|
64
|
-
if TYPE_CHECKING:
|
|
65
|
-
from collections.abc import Sequence
|
|
66
|
-
|
|
67
64
|
|
|
68
65
|
class DictLike(Protocol):
|
|
69
66
|
"""A protocol for objects that behave like a dictionary for reading."""
|
|
@@ -87,30 +84,22 @@ PoolT = TypeVar("PoolT")
|
|
|
87
84
|
|
|
88
85
|
:class:`~sqlspec.typing.PoolT`
|
|
89
86
|
"""
|
|
90
|
-
|
|
91
|
-
"""Type variable for
|
|
92
|
-
|
|
93
|
-
:class:`~sqlspec.typing.PoolT_co`
|
|
94
|
-
"""
|
|
95
|
-
ModelT = TypeVar("ModelT", bound="Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub]")
|
|
96
|
-
"""Type variable for model types.
|
|
87
|
+
SchemaT = TypeVar("SchemaT", default=dict[str, Any])
|
|
88
|
+
"""Type variable for schema types (models, TypedDict, dataclasses, etc.).
|
|
97
89
|
|
|
98
|
-
|
|
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.
|
|
99
92
|
"""
|
|
100
|
-
RowT = TypeVar("RowT", bound="dict[str, Any]")
|
|
101
|
-
|
|
102
93
|
|
|
103
|
-
DictRow: TypeAlias = "dict[str, Any]"
|
|
104
|
-
"""Type variable for DictRow types."""
|
|
105
|
-
TupleRow: TypeAlias = "tuple[Any, ...]"
|
|
106
|
-
"""Type variable for TupleRow types."""
|
|
107
94
|
|
|
108
|
-
SupportedSchemaModel: TypeAlias =
|
|
95
|
+
SupportedSchemaModel: TypeAlias = (
|
|
96
|
+
DictLike | StructStub | BaseModelStub | DataclassProtocol | AttrsInstanceStub | _TypedDict
|
|
97
|
+
)
|
|
109
98
|
"""Type alias for pydantic or msgspec models.
|
|
110
99
|
|
|
111
100
|
:class:`msgspec.Struct` | :class:`pydantic.BaseModel` | :class:`DataclassProtocol` | :class:`AttrsInstance`
|
|
112
101
|
"""
|
|
113
|
-
StatementParameters: TypeAlias = "
|
|
102
|
+
StatementParameters: TypeAlias = "Any | dict[str, Any] | list[Any] | tuple[Any, ...] | None"
|
|
114
103
|
"""Type alias for statement parameters.
|
|
115
104
|
|
|
116
105
|
Represents:
|
|
@@ -119,40 +108,6 @@ Represents:
|
|
|
119
108
|
- :type:`tuple[Any, ...]`
|
|
120
109
|
- :type:`None`
|
|
121
110
|
"""
|
|
122
|
-
ModelDTOT = TypeVar("ModelDTOT", bound="SupportedSchemaModel")
|
|
123
|
-
"""Type variable for model DTOs.
|
|
124
|
-
|
|
125
|
-
:class:`msgspec.Struct`|:class:`pydantic.BaseModel`
|
|
126
|
-
"""
|
|
127
|
-
PydanticOrMsgspecT = SupportedSchemaModel
|
|
128
|
-
"""Type alias for pydantic or msgspec models.
|
|
129
|
-
|
|
130
|
-
:class:`msgspec.Struct` or :class:`pydantic.BaseModel`
|
|
131
|
-
"""
|
|
132
|
-
ModelDict: TypeAlias = (
|
|
133
|
-
"Union[dict[str, Any], Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub], Any]"
|
|
134
|
-
)
|
|
135
|
-
"""Type alias for model dictionaries.
|
|
136
|
-
|
|
137
|
-
Represents:
|
|
138
|
-
- :type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`
|
|
139
|
-
"""
|
|
140
|
-
ModelDictList: TypeAlias = (
|
|
141
|
-
"Sequence[Union[dict[str, Any], Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub]]]"
|
|
142
|
-
)
|
|
143
|
-
"""Type alias for model dictionary lists.
|
|
144
|
-
|
|
145
|
-
A list or sequence of any of the following:
|
|
146
|
-
- :type:`Sequence`[:type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`]
|
|
147
|
-
|
|
148
|
-
"""
|
|
149
|
-
BulkModelDict: TypeAlias = "Union[Sequence[Union[dict[str, Any], Union[DictLike, StructStub, BaseModelStub, DataclassProtocol, AttrsInstanceStub]]], Any]"
|
|
150
|
-
"""Type alias for bulk model dictionaries.
|
|
151
|
-
|
|
152
|
-
Represents:
|
|
153
|
-
- :type:`Sequence`[:type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`]
|
|
154
|
-
- :class:`DTOData`[:type:`list[ModelT]`]
|
|
155
|
-
"""
|
|
156
111
|
|
|
157
112
|
|
|
158
113
|
@lru_cache(typed=True)
|
|
@@ -170,18 +125,6 @@ def get_type_adapter(f: "type[T]") -> Any:
|
|
|
170
125
|
return TypeAdapter(f)
|
|
171
126
|
|
|
172
127
|
|
|
173
|
-
def MixinOf(base: type[T]) -> type[T]: # noqa: N802
|
|
174
|
-
"""Useful function to make mixins with baseclass type hint
|
|
175
|
-
|
|
176
|
-
```
|
|
177
|
-
class StorageMixin(MixinOf(DriverProtocol)): ...
|
|
178
|
-
```
|
|
179
|
-
"""
|
|
180
|
-
if TYPE_CHECKING:
|
|
181
|
-
return base
|
|
182
|
-
return type("<MixinOf>", (base,), {})
|
|
183
|
-
|
|
184
|
-
|
|
185
128
|
__all__ = (
|
|
186
129
|
"AIOSQL_INSTALLED",
|
|
187
130
|
"ATTRS_INSTALLED",
|
|
@@ -201,38 +144,26 @@ __all__ = (
|
|
|
201
144
|
"UNSET",
|
|
202
145
|
"AiosqlAsyncProtocol",
|
|
203
146
|
"AiosqlParamType",
|
|
204
|
-
"AiosqlProtocol",
|
|
205
147
|
"AiosqlSQLOperationType",
|
|
206
148
|
"AiosqlSyncProtocol",
|
|
207
149
|
"ArrowRecordBatch",
|
|
208
150
|
"ArrowTable",
|
|
209
151
|
"AttrsInstance",
|
|
210
152
|
"BaseModel",
|
|
211
|
-
"BulkModelDict",
|
|
212
153
|
"ConnectionT",
|
|
213
154
|
"Counter",
|
|
214
155
|
"DTOData",
|
|
215
156
|
"DataclassProtocol",
|
|
216
157
|
"DictLike",
|
|
217
|
-
"DictRow",
|
|
218
158
|
"Empty",
|
|
219
159
|
"EmptyEnum",
|
|
220
160
|
"EmptyType",
|
|
221
161
|
"FailFast",
|
|
222
162
|
"Gauge",
|
|
223
163
|
"Histogram",
|
|
224
|
-
"
|
|
225
|
-
"MixinOf",
|
|
226
|
-
"ModelDTOT",
|
|
227
|
-
"ModelDict",
|
|
228
|
-
"ModelDict",
|
|
229
|
-
"ModelDictList",
|
|
230
|
-
"ModelDictList",
|
|
231
|
-
"ModelT",
|
|
164
|
+
"NumpyArray",
|
|
232
165
|
"PoolT",
|
|
233
|
-
"
|
|
234
|
-
"PydanticOrMsgspecT",
|
|
235
|
-
"RowT",
|
|
166
|
+
"SchemaT",
|
|
236
167
|
"Span",
|
|
237
168
|
"StatementParameters",
|
|
238
169
|
"Status",
|
|
@@ -240,7 +171,6 @@ __all__ = (
|
|
|
240
171
|
"Struct",
|
|
241
172
|
"SupportedSchemaModel",
|
|
242
173
|
"Tracer",
|
|
243
|
-
"TupleRow",
|
|
244
174
|
"TypeAdapter",
|
|
245
175
|
"UnsetType",
|
|
246
176
|
"aiosql",
|
sqlspec/utils/config_resolver.py
CHANGED
|
@@ -7,7 +7,7 @@ Supports both synchronous and asynchronous callable functions.
|
|
|
7
7
|
|
|
8
8
|
import inspect
|
|
9
9
|
from collections.abc import Sequence
|
|
10
|
-
from typing import TYPE_CHECKING, Any,
|
|
10
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
11
11
|
|
|
12
12
|
from sqlspec.exceptions import ConfigResolverError
|
|
13
13
|
from sqlspec.utils.module_loader import import_string
|
|
@@ -21,7 +21,7 @@ __all__ = ("resolve_config_async", "resolve_config_sync")
|
|
|
21
21
|
|
|
22
22
|
async def resolve_config_async(
|
|
23
23
|
config_path: str,
|
|
24
|
-
) -> "
|
|
24
|
+
) -> "list[AsyncDatabaseConfig | SyncDatabaseConfig] | AsyncDatabaseConfig | SyncDatabaseConfig":
|
|
25
25
|
"""Resolve config from dotted path, handling callables and direct instances.
|
|
26
26
|
|
|
27
27
|
This is the async-first version that handles both sync and async callables efficiently.
|
|
@@ -58,7 +58,7 @@ async def resolve_config_async(
|
|
|
58
58
|
|
|
59
59
|
def resolve_config_sync(
|
|
60
60
|
config_path: str,
|
|
61
|
-
) -> "
|
|
61
|
+
) -> "list[AsyncDatabaseConfig | SyncDatabaseConfig] | AsyncDatabaseConfig | SyncDatabaseConfig":
|
|
62
62
|
"""Synchronous wrapper for resolve_config.
|
|
63
63
|
|
|
64
64
|
Args:
|
|
@@ -90,7 +90,7 @@ def resolve_config_sync(
|
|
|
90
90
|
|
|
91
91
|
def _validate_config_result(
|
|
92
92
|
config_result: Any, config_path: str
|
|
93
|
-
) -> "
|
|
93
|
+
) -> "list[AsyncDatabaseConfig | SyncDatabaseConfig] | AsyncDatabaseConfig | SyncDatabaseConfig":
|
|
94
94
|
"""Validate that the config result is a valid config or list of configs.
|
|
95
95
|
|
|
96
96
|
Args:
|
|
@@ -117,13 +117,13 @@ def _validate_config_result(
|
|
|
117
117
|
msg = f"Config '{config_path}' returned invalid config at index {i}. Expected database config instance."
|
|
118
118
|
raise ConfigResolverError(msg)
|
|
119
119
|
|
|
120
|
-
return cast("list[
|
|
120
|
+
return cast("list[AsyncDatabaseConfig | SyncDatabaseConfig]", list(config_result))
|
|
121
121
|
|
|
122
122
|
if not _is_valid_config(config_result):
|
|
123
123
|
msg = f"Config '{config_path}' returned invalid type '{type(config_result).__name__}'. Expected database config instance or list."
|
|
124
124
|
raise ConfigResolverError(msg)
|
|
125
125
|
|
|
126
|
-
return cast("
|
|
126
|
+
return cast("AsyncDatabaseConfig | SyncDatabaseConfig", config_result)
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
def _is_valid_config(config: Any) -> bool:
|
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
|
|
sqlspec/utils/fixtures.py
CHANGED
|
@@ -7,7 +7,7 @@ used in testing and development. Supports both sync and async operations.
|
|
|
7
7
|
import gzip
|
|
8
8
|
import zipfile
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import TYPE_CHECKING, Any
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
11
|
|
|
12
12
|
from sqlspec.storage import storage_registry
|
|
13
13
|
from sqlspec.utils.serializers import from_json as decode_json
|
|
@@ -16,7 +16,7 @@ from sqlspec.utils.sync_tools import async_
|
|
|
16
16
|
from sqlspec.utils.type_guards import schema_dump
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
from sqlspec.typing import
|
|
19
|
+
from sqlspec.typing import SupportedSchemaModel
|
|
20
20
|
|
|
21
21
|
__all__ = ("open_fixture", "open_fixture_async", "write_fixture", "write_fixture_async")
|
|
22
22
|
|
|
@@ -171,7 +171,7 @@ def _serialize_data(data: Any) -> str:
|
|
|
171
171
|
def write_fixture(
|
|
172
172
|
fixtures_path: str,
|
|
173
173
|
table_name: str,
|
|
174
|
-
data: "
|
|
174
|
+
data: "list[SupportedSchemaModel] | list[dict[str, Any]] | SupportedSchemaModel",
|
|
175
175
|
storage_backend: str = "local",
|
|
176
176
|
compress: bool = False,
|
|
177
177
|
**storage_kwargs: Any,
|
|
@@ -219,7 +219,7 @@ def write_fixture(
|
|
|
219
219
|
async def write_fixture_async(
|
|
220
220
|
fixtures_path: str,
|
|
221
221
|
table_name: str,
|
|
222
|
-
data: "
|
|
222
|
+
data: "list[SupportedSchemaModel] | list[dict[str, Any]] | SupportedSchemaModel",
|
|
223
223
|
storage_backend: str = "local",
|
|
224
224
|
compress: bool = False,
|
|
225
225
|
**storage_kwargs: Any,
|
sqlspec/utils/logging.py
CHANGED
|
@@ -8,16 +8,24 @@ SQLSpec provides StructuredFormatter for JSON-formatted logs if desired.
|
|
|
8
8
|
import logging
|
|
9
9
|
from contextvars import ContextVar
|
|
10
10
|
from logging import LogRecord
|
|
11
|
-
from typing import Any
|
|
11
|
+
from typing import Any
|
|
12
12
|
|
|
13
13
|
from sqlspec._serialization import encode_json
|
|
14
14
|
|
|
15
|
-
__all__ = (
|
|
15
|
+
__all__ = (
|
|
16
|
+
"SqlglotCommandFallbackFilter",
|
|
17
|
+
"StructuredFormatter",
|
|
18
|
+
"correlation_id_var",
|
|
19
|
+
"get_correlation_id",
|
|
20
|
+
"get_logger",
|
|
21
|
+
"set_correlation_id",
|
|
22
|
+
"suppress_erroneous_sqlglot_log_messages",
|
|
23
|
+
)
|
|
16
24
|
|
|
17
|
-
correlation_id_var: "ContextVar[
|
|
25
|
+
correlation_id_var: "ContextVar[str | None]" = ContextVar("correlation_id", default=None)
|
|
18
26
|
|
|
19
27
|
|
|
20
|
-
def set_correlation_id(correlation_id: "
|
|
28
|
+
def set_correlation_id(correlation_id: "str | None") -> None:
|
|
21
29
|
"""Set the correlation ID for the current context.
|
|
22
30
|
|
|
23
31
|
Args:
|
|
@@ -26,7 +34,7 @@ def set_correlation_id(correlation_id: "Optional[str]") -> None:
|
|
|
26
34
|
correlation_id_var.set(correlation_id)
|
|
27
35
|
|
|
28
36
|
|
|
29
|
-
def get_correlation_id() -> "
|
|
37
|
+
def get_correlation_id() -> "str | None":
|
|
30
38
|
"""Get the current correlation ID.
|
|
31
39
|
|
|
32
40
|
Returns:
|
|
@@ -86,7 +94,27 @@ class CorrelationIDFilter(logging.Filter):
|
|
|
86
94
|
return True
|
|
87
95
|
|
|
88
96
|
|
|
89
|
-
|
|
97
|
+
class SqlglotCommandFallbackFilter(logging.Filter):
|
|
98
|
+
"""Filter to suppress sqlglot's confusing 'Falling back to Command' warning.
|
|
99
|
+
|
|
100
|
+
This filter suppresses the warning message that sqlglot emits when it
|
|
101
|
+
encounters unsupported syntax and falls back to parsing as a Command.
|
|
102
|
+
This is expected behavior in SQLSpec and the warning is confusing to users.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def filter(self, record: LogRecord) -> bool:
|
|
106
|
+
"""Suppress the 'Falling back to Command' warning message.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
record: The log record to evaluate
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
False if the record contains the fallback warning, True otherwise
|
|
113
|
+
"""
|
|
114
|
+
return "Falling back to parsing as a 'Command'" not in record.getMessage()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_logger(name: "str | None" = None) -> logging.Logger:
|
|
90
118
|
"""Get a logger instance with standardized configuration.
|
|
91
119
|
|
|
92
120
|
Args:
|
|
@@ -121,3 +149,15 @@ def log_with_context(logger: logging.Logger, level: int, message: str, **extra_f
|
|
|
121
149
|
record = logger.makeRecord(logger.name, level, "(unknown file)", 0, message, (), None)
|
|
122
150
|
record.extra_fields = extra_fields
|
|
123
151
|
logger.handle(record)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def suppress_erroneous_sqlglot_log_messages() -> None:
|
|
155
|
+
"""Suppress confusing sqlglot warning messages.
|
|
156
|
+
|
|
157
|
+
Adds a filter to the sqlglot logger to suppress the warning message
|
|
158
|
+
about falling back to parsing as a Command. This is expected behavior
|
|
159
|
+
in SQLSpec and the warning is confusing to users.
|
|
160
|
+
"""
|
|
161
|
+
sqlglot_logger = logging.getLogger("sqlglot")
|
|
162
|
+
if not any(isinstance(f, SqlglotCommandFallbackFilter) for f in sqlglot_logger.filters):
|
|
163
|
+
sqlglot_logger.addFilter(SqlglotCommandFallbackFilter())
|