sqlspec 0.16.1__cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.
- 51ff5a9eadfdefd49f98__mypyc.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +92 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +77 -0
- sqlspec/_sql.py +1780 -0
- sqlspec/_typing.py +680 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +361 -0
- sqlspec/adapters/adbc/driver.py +512 -0
- sqlspec/adapters/aiosqlite/__init__.py +19 -0
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +253 -0
- sqlspec/adapters/aiosqlite/driver.py +248 -0
- sqlspec/adapters/asyncmy/__init__.py +19 -0
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +180 -0
- sqlspec/adapters/asyncmy/driver.py +274 -0
- sqlspec/adapters/asyncpg/__init__.py +21 -0
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +229 -0
- sqlspec/adapters/asyncpg/driver.py +344 -0
- sqlspec/adapters/bigquery/__init__.py +18 -0
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +298 -0
- sqlspec/adapters/bigquery/driver.py +558 -0
- sqlspec/adapters/duckdb/__init__.py +22 -0
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +504 -0
- sqlspec/adapters/duckdb/driver.py +368 -0
- sqlspec/adapters/oracledb/__init__.py +32 -0
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +317 -0
- sqlspec/adapters/oracledb/driver.py +538 -0
- sqlspec/adapters/psqlpy/__init__.py +16 -0
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +214 -0
- sqlspec/adapters/psqlpy/driver.py +530 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +426 -0
- sqlspec/adapters/psycopg/driver.py +796 -0
- sqlspec/adapters/sqlite/__init__.py +15 -0
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +240 -0
- sqlspec/adapters/sqlite/driver.py +294 -0
- sqlspec/base.py +571 -0
- sqlspec/builder/__init__.py +62 -0
- sqlspec/builder/_base.py +473 -0
- sqlspec/builder/_column.py +320 -0
- sqlspec/builder/_ddl.py +1346 -0
- sqlspec/builder/_ddl_utils.py +103 -0
- sqlspec/builder/_delete.py +76 -0
- sqlspec/builder/_insert.py +256 -0
- sqlspec/builder/_merge.py +71 -0
- sqlspec/builder/_parsing_utils.py +140 -0
- sqlspec/builder/_select.py +170 -0
- sqlspec/builder/_update.py +188 -0
- sqlspec/builder/mixins/__init__.py +55 -0
- sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
- sqlspec/builder/mixins/_delete_operations.py +41 -0
- sqlspec/builder/mixins/_insert_operations.py +244 -0
- sqlspec/builder/mixins/_join_operations.py +122 -0
- sqlspec/builder/mixins/_merge_operations.py +476 -0
- sqlspec/builder/mixins/_order_limit_operations.py +135 -0
- sqlspec/builder/mixins/_pivot_operations.py +153 -0
- sqlspec/builder/mixins/_select_operations.py +603 -0
- sqlspec/builder/mixins/_update_operations.py +187 -0
- sqlspec/builder/mixins/_where_clause.py +621 -0
- sqlspec/cli.py +247 -0
- sqlspec/config.py +395 -0
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +871 -0
- sqlspec/core/compiler.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +417 -0
- sqlspec/core/filters.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters.py +1237 -0
- sqlspec/core/result.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result.py +677 -0
- sqlspec/core/splitter.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +819 -0
- sqlspec/core/statement.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +676 -0
- sqlspec/driver/__init__.py +19 -0
- sqlspec/driver/_async.py +502 -0
- sqlspec/driver/_common.py +631 -0
- sqlspec/driver/_sync.py +503 -0
- sqlspec/driver/mixins/__init__.py +6 -0
- sqlspec/driver/mixins/_result_tools.py +193 -0
- sqlspec/driver/mixins/_sql_translator.py +86 -0
- sqlspec/exceptions.py +193 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/aiosql/__init__.py +10 -0
- sqlspec/extensions/aiosql/adapter.py +461 -0
- sqlspec/extensions/litestar/__init__.py +6 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/config.py +92 -0
- sqlspec/extensions/litestar/handlers.py +260 -0
- sqlspec/extensions/litestar/plugin.py +145 -0
- sqlspec/extensions/litestar/providers.py +454 -0
- sqlspec/loader.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +760 -0
- sqlspec/migrations/__init__.py +35 -0
- sqlspec/migrations/base.py +414 -0
- sqlspec/migrations/commands.py +443 -0
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +213 -0
- sqlspec/migrations/tracker.py +140 -0
- sqlspec/migrations/utils.py +129 -0
- sqlspec/protocols.py +407 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +23 -0
- sqlspec/storage/backends/__init__.py +0 -0
- sqlspec/storage/backends/base.py +163 -0
- sqlspec/storage/backends/fsspec.py +386 -0
- sqlspec/storage/backends/obstore.py +459 -0
- sqlspec/storage/capabilities.py +102 -0
- sqlspec/storage/registry.py +239 -0
- sqlspec/typing.py +299 -0
- sqlspec/utils/__init__.py +3 -0
- sqlspec/utils/correlation.py +150 -0
- sqlspec/utils/deprecation.py +106 -0
- sqlspec/utils/fixtures.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +58 -0
- sqlspec/utils/logging.py +127 -0
- sqlspec/utils/module_loader.py +89 -0
- sqlspec/utils/serializers.py +4 -0
- sqlspec/utils/singleton.py +32 -0
- sqlspec/utils/sync_tools.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +237 -0
- sqlspec/utils/text.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +96 -0
- sqlspec/utils/type_guards.cpython-39-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1139 -0
- sqlspec-0.16.1.dist-info/METADATA +365 -0
- sqlspec-0.16.1.dist-info/RECORD +148 -0
- sqlspec-0.16.1.dist-info/WHEEL +7 -0
- sqlspec-0.16.1.dist-info/entry_points.txt +2 -0
- sqlspec-0.16.1.dist-info/licenses/LICENSE +21 -0
- sqlspec-0.16.1.dist-info/licenses/NOTICE +29 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# pyright: reportCallIssue=false, reportAttributeAccessIssue=false, reportArgumentType=false
|
|
2
|
+
import datetime
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from functools import partial
|
|
7
|
+
from pathlib import Path, PurePath
|
|
8
|
+
from typing import Any, Callable, Final, Optional, overload
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from mypy_extensions import trait
|
|
12
|
+
|
|
13
|
+
from sqlspec.exceptions import SQLSpecError
|
|
14
|
+
from sqlspec.typing import (
|
|
15
|
+
CATTRS_INSTALLED,
|
|
16
|
+
ModelDTOT,
|
|
17
|
+
ModelT,
|
|
18
|
+
attrs_asdict,
|
|
19
|
+
cattrs_structure,
|
|
20
|
+
cattrs_unstructure,
|
|
21
|
+
convert,
|
|
22
|
+
get_type_adapter,
|
|
23
|
+
)
|
|
24
|
+
from sqlspec.utils.type_guards import is_attrs_schema, is_dataclass, is_msgspec_struct, is_pydantic_model
|
|
25
|
+
|
|
26
|
+
__all__ = ("_DEFAULT_TYPE_DECODERS", "_default_msgspec_deserializer")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
# Constants for performance optimization
|
|
32
|
+
_DATETIME_TYPES: Final[set[type]] = {datetime.datetime, datetime.date, datetime.time}
|
|
33
|
+
_PATH_TYPES: Final[tuple[type, ...]] = (Path, PurePath, UUID)
|
|
34
|
+
|
|
35
|
+
_DEFAULT_TYPE_DECODERS: Final[list[tuple[Callable[[Any], bool], Callable[[Any, Any], Any]]]] = [
|
|
36
|
+
(lambda x: x is UUID, lambda t, v: t(v.hex)),
|
|
37
|
+
(lambda x: x is datetime.datetime, lambda t, v: t(v.isoformat())),
|
|
38
|
+
(lambda x: x is datetime.date, lambda t, v: t(v.isoformat())),
|
|
39
|
+
(lambda x: x is datetime.time, lambda t, v: t(v.isoformat())),
|
|
40
|
+
(lambda x: x is Enum, lambda t, v: t(v.value)),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _default_msgspec_deserializer(
|
|
45
|
+
target_type: Any, value: Any, type_decoders: "Optional[Sequence[tuple[Any, Any]]]" = None
|
|
46
|
+
) -> Any:
|
|
47
|
+
"""Default msgspec deserializer with type conversion support.
|
|
48
|
+
|
|
49
|
+
Converts values to appropriate types for msgspec deserialization, including
|
|
50
|
+
UUID, datetime, date, time, Enum, Path, and PurePath types.
|
|
51
|
+
"""
|
|
52
|
+
if type_decoders:
|
|
53
|
+
for predicate, decoder in type_decoders:
|
|
54
|
+
if predicate(target_type):
|
|
55
|
+
return decoder(target_type, value)
|
|
56
|
+
|
|
57
|
+
# Fast path checks using type identity and isinstance
|
|
58
|
+
if target_type is UUID and isinstance(value, UUID):
|
|
59
|
+
return value.hex
|
|
60
|
+
|
|
61
|
+
# Use pre-computed set for faster lookup
|
|
62
|
+
if target_type in _DATETIME_TYPES:
|
|
63
|
+
try:
|
|
64
|
+
return value.isoformat()
|
|
65
|
+
except AttributeError:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
if isinstance(target_type, type) and issubclass(target_type, Enum) and isinstance(value, Enum):
|
|
69
|
+
return value.value
|
|
70
|
+
|
|
71
|
+
if isinstance(value, target_type):
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
# Check for path types using pre-computed tuple
|
|
75
|
+
if isinstance(target_type, type):
|
|
76
|
+
try:
|
|
77
|
+
if issubclass(target_type, (Path, PurePath)) or issubclass(target_type, UUID):
|
|
78
|
+
return target_type(str(value))
|
|
79
|
+
except (TypeError, ValueError):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
return value
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@trait
|
|
86
|
+
class ToSchemaMixin:
|
|
87
|
+
__slots__ = ()
|
|
88
|
+
|
|
89
|
+
# Schema conversion overloads - handle common cases first
|
|
90
|
+
@overload
|
|
91
|
+
@staticmethod
|
|
92
|
+
def to_schema(data: "list[dict[str, Any]]") -> "list[dict[str, Any]]": ...
|
|
93
|
+
@overload
|
|
94
|
+
@staticmethod
|
|
95
|
+
def to_schema(data: "list[dict[str, Any]]", *, schema_type: "type[ModelDTOT]") -> "list[ModelDTOT]": ...
|
|
96
|
+
@overload
|
|
97
|
+
@staticmethod
|
|
98
|
+
def to_schema(data: "list[dict[str, Any]]", *, schema_type: None = None) -> "list[dict[str, Any]]": ...
|
|
99
|
+
@overload
|
|
100
|
+
@staticmethod
|
|
101
|
+
def to_schema(data: "dict[str, Any]") -> "dict[str, Any]": ...
|
|
102
|
+
@overload
|
|
103
|
+
@staticmethod
|
|
104
|
+
def to_schema(data: "dict[str, Any]", *, schema_type: "type[ModelDTOT]") -> "ModelDTOT": ...
|
|
105
|
+
@overload
|
|
106
|
+
@staticmethod
|
|
107
|
+
def to_schema(data: "dict[str, Any]", *, schema_type: None = None) -> "dict[str, Any]": ...
|
|
108
|
+
@overload
|
|
109
|
+
@staticmethod
|
|
110
|
+
def to_schema(data: "list[ModelT]") -> "list[ModelT]": ...
|
|
111
|
+
@overload
|
|
112
|
+
@staticmethod
|
|
113
|
+
def to_schema(data: "list[ModelT]", *, schema_type: "type[ModelDTOT]") -> "list[ModelDTOT]": ...
|
|
114
|
+
@overload
|
|
115
|
+
@staticmethod
|
|
116
|
+
def to_schema(data: "list[ModelT]", *, schema_type: None = None) -> "list[ModelT]": ...
|
|
117
|
+
@overload
|
|
118
|
+
@staticmethod
|
|
119
|
+
def to_schema(data: "ModelT") -> "ModelT": ...
|
|
120
|
+
@overload
|
|
121
|
+
@staticmethod
|
|
122
|
+
def to_schema(data: Any, *, schema_type: None = None) -> Any: ...
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def to_schema(data: Any, *, schema_type: "Optional[type[ModelDTOT]]" = None) -> Any:
|
|
126
|
+
"""Convert data to a specified schema type.
|
|
127
|
+
|
|
128
|
+
Supports conversion to dataclasses, msgspec structs, Pydantic models, and attrs classes.
|
|
129
|
+
Handles both single objects and sequences.
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
SQLSpecError if `schema_type` is not a valid type.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Converted data in the specified schema type.
|
|
136
|
+
|
|
137
|
+
"""
|
|
138
|
+
if schema_type is None:
|
|
139
|
+
return data
|
|
140
|
+
if is_dataclass(schema_type):
|
|
141
|
+
if isinstance(data, list):
|
|
142
|
+
result: list[Any] = []
|
|
143
|
+
for item in data:
|
|
144
|
+
if hasattr(item, "keys"):
|
|
145
|
+
result.append(schema_type(**dict(item))) # type: ignore[operator]
|
|
146
|
+
else:
|
|
147
|
+
result.append(item)
|
|
148
|
+
return result
|
|
149
|
+
if hasattr(data, "keys"):
|
|
150
|
+
return schema_type(**dict(data)) # type: ignore[operator]
|
|
151
|
+
if isinstance(data, dict):
|
|
152
|
+
return schema_type(**data) # type: ignore[operator]
|
|
153
|
+
return data
|
|
154
|
+
if is_msgspec_struct(schema_type):
|
|
155
|
+
# Cache the deserializer to avoid repeated partial() calls
|
|
156
|
+
deserializer = partial(_default_msgspec_deserializer, type_decoders=_DEFAULT_TYPE_DECODERS)
|
|
157
|
+
if not isinstance(data, Sequence):
|
|
158
|
+
return convert(obj=data, type=schema_type, from_attributes=True, dec_hook=deserializer)
|
|
159
|
+
return convert(
|
|
160
|
+
obj=data,
|
|
161
|
+
type=list[schema_type], # type: ignore[valid-type] # pyright: ignore
|
|
162
|
+
from_attributes=True,
|
|
163
|
+
dec_hook=deserializer,
|
|
164
|
+
)
|
|
165
|
+
if is_pydantic_model(schema_type):
|
|
166
|
+
if not isinstance(data, Sequence):
|
|
167
|
+
adapter = get_type_adapter(schema_type)
|
|
168
|
+
return adapter.validate_python(data, from_attributes=True) # pyright: ignore
|
|
169
|
+
list_adapter = get_type_adapter(list[schema_type]) # type: ignore[valid-type] # pyright: ignore
|
|
170
|
+
return list_adapter.validate_python(data, from_attributes=True)
|
|
171
|
+
if is_attrs_schema(schema_type):
|
|
172
|
+
if CATTRS_INSTALLED:
|
|
173
|
+
if isinstance(data, Sequence):
|
|
174
|
+
return cattrs_structure(data, list[schema_type]) # type: ignore[valid-type] # pyright: ignore
|
|
175
|
+
if hasattr(data, "__attrs_attrs__"):
|
|
176
|
+
unstructured_data = cattrs_unstructure(data)
|
|
177
|
+
return cattrs_structure(unstructured_data, schema_type) # pyright: ignore
|
|
178
|
+
return cattrs_structure(data, schema_type) # pyright: ignore
|
|
179
|
+
if isinstance(data, list):
|
|
180
|
+
attrs_result: list[Any] = []
|
|
181
|
+
for item in data:
|
|
182
|
+
if hasattr(item, "keys"):
|
|
183
|
+
attrs_result.append(schema_type(**dict(item)))
|
|
184
|
+
else:
|
|
185
|
+
attrs_result.append(schema_type(**attrs_asdict(item)))
|
|
186
|
+
return attrs_result
|
|
187
|
+
if hasattr(data, "keys"):
|
|
188
|
+
return schema_type(**dict(data))
|
|
189
|
+
if isinstance(data, dict):
|
|
190
|
+
return schema_type(**data)
|
|
191
|
+
return data
|
|
192
|
+
msg = "`schema_type` should be a valid Dataclass, Pydantic model, Msgspec struct, or Attrs class"
|
|
193
|
+
raise SQLSpecError(msg)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Final, NoReturn, Optional
|
|
2
|
+
|
|
3
|
+
from mypy_extensions import trait
|
|
4
|
+
from sqlglot import exp, parse_one
|
|
5
|
+
from sqlglot.dialects.dialect import DialectType
|
|
6
|
+
|
|
7
|
+
from sqlspec.core.statement import SQL, Statement
|
|
8
|
+
from sqlspec.exceptions import SQLConversionError
|
|
9
|
+
|
|
10
|
+
__all__ = ("SQLTranslatorMixin",)
|
|
11
|
+
|
|
12
|
+
# Constants for better performance
|
|
13
|
+
_DEFAULT_PRETTY: Final[bool] = True
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@trait
|
|
17
|
+
class SQLTranslatorMixin:
|
|
18
|
+
"""Mixin for drivers supporting SQL translation."""
|
|
19
|
+
|
|
20
|
+
__slots__ = ()
|
|
21
|
+
|
|
22
|
+
def convert_to_dialect(
|
|
23
|
+
self, statement: "Statement", to_dialect: "Optional[DialectType]" = None, pretty: bool = _DEFAULT_PRETTY
|
|
24
|
+
) -> str:
|
|
25
|
+
"""Convert a statement to a target SQL dialect.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
statement: SQL statement to convert
|
|
29
|
+
to_dialect: Target dialect (defaults to current dialect)
|
|
30
|
+
pretty: Whether to format the output SQL
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
SQL string in target dialect
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
SQLConversionError: If parsing or conversion fails
|
|
37
|
+
"""
|
|
38
|
+
# Fast path: get the parsed expression with minimal allocations
|
|
39
|
+
parsed_expression: Optional[exp.Expression] = None
|
|
40
|
+
|
|
41
|
+
if statement is not None and isinstance(statement, SQL):
|
|
42
|
+
if statement.expression is None:
|
|
43
|
+
self._raise_statement_parse_error()
|
|
44
|
+
parsed_expression = statement.expression
|
|
45
|
+
elif isinstance(statement, exp.Expression):
|
|
46
|
+
parsed_expression = statement
|
|
47
|
+
else:
|
|
48
|
+
parsed_expression = self._parse_statement_safely(statement)
|
|
49
|
+
|
|
50
|
+
# Get target dialect with fallback
|
|
51
|
+
target_dialect = to_dialect or self.dialect # type: ignore[attr-defined]
|
|
52
|
+
|
|
53
|
+
# Generate SQL with error handling
|
|
54
|
+
return self._generate_sql_safely(parsed_expression, target_dialect, pretty)
|
|
55
|
+
|
|
56
|
+
def _parse_statement_safely(self, statement: "Statement") -> "exp.Expression":
|
|
57
|
+
"""Parse statement with copy=False optimization and proper error handling."""
|
|
58
|
+
try:
|
|
59
|
+
# Convert statement to string if needed
|
|
60
|
+
sql_string = str(statement)
|
|
61
|
+
# Use copy=False for better performance
|
|
62
|
+
return parse_one(sql_string, dialect=self.dialect, copy=False) # type: ignore[attr-defined]
|
|
63
|
+
except Exception as e:
|
|
64
|
+
self._raise_parse_error(e)
|
|
65
|
+
|
|
66
|
+
def _generate_sql_safely(self, expression: "exp.Expression", dialect: DialectType, pretty: bool) -> str:
|
|
67
|
+
"""Generate SQL with proper error handling."""
|
|
68
|
+
try:
|
|
69
|
+
return expression.sql(dialect=dialect, pretty=pretty)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
self._raise_conversion_error(dialect, e)
|
|
72
|
+
|
|
73
|
+
def _raise_statement_parse_error(self) -> NoReturn:
|
|
74
|
+
"""Raise error for unparsable statements."""
|
|
75
|
+
msg = "Statement could not be parsed"
|
|
76
|
+
raise SQLConversionError(msg)
|
|
77
|
+
|
|
78
|
+
def _raise_parse_error(self, e: Exception) -> NoReturn:
|
|
79
|
+
"""Raise error for parsing failures."""
|
|
80
|
+
error_msg = f"Failed to parse SQL statement: {e!s}"
|
|
81
|
+
raise SQLConversionError(error_msg) from e
|
|
82
|
+
|
|
83
|
+
def _raise_conversion_error(self, dialect: DialectType, e: Exception) -> NoReturn:
|
|
84
|
+
"""Raise error for conversion failures."""
|
|
85
|
+
error_msg = f"Failed to convert SQL expression to {dialect}: {e!s}"
|
|
86
|
+
raise SQLConversionError(error_msg) from e
|
sqlspec/exceptions.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
|
+
"FileNotFoundInStorageError",
|
|
7
|
+
"ImproperConfigurationError",
|
|
8
|
+
"IntegrityError",
|
|
9
|
+
"MissingDependencyError",
|
|
10
|
+
"MultipleResultsFoundError",
|
|
11
|
+
"NotFoundError",
|
|
12
|
+
"RepositoryError",
|
|
13
|
+
"SQLBuilderError",
|
|
14
|
+
"SQLConversionError",
|
|
15
|
+
"SQLFileNotFoundError",
|
|
16
|
+
"SQLFileParseError",
|
|
17
|
+
"SQLParsingError",
|
|
18
|
+
"SQLSpecError",
|
|
19
|
+
"SerializationError",
|
|
20
|
+
"StorageOperationFailedError",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SQLSpecError(Exception):
|
|
25
|
+
"""Base exception class for SQLSpec exceptions."""
|
|
26
|
+
|
|
27
|
+
detail: str
|
|
28
|
+
|
|
29
|
+
def __init__(self, *args: Any, detail: str = "") -> None:
|
|
30
|
+
"""Initialize SQLSpecError.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
*args: args are converted to :class:`str` before passing to :class:`Exception`
|
|
34
|
+
detail: detail of the exception.
|
|
35
|
+
"""
|
|
36
|
+
str_args = [str(arg) for arg in args if arg]
|
|
37
|
+
if not detail:
|
|
38
|
+
if str_args:
|
|
39
|
+
detail, *str_args = str_args
|
|
40
|
+
elif hasattr(self, "detail"):
|
|
41
|
+
detail = self.detail
|
|
42
|
+
self.detail = detail
|
|
43
|
+
super().__init__(*str_args)
|
|
44
|
+
|
|
45
|
+
def __repr__(self) -> str:
|
|
46
|
+
if self.detail:
|
|
47
|
+
return f"{self.__class__.__name__} - {self.detail}"
|
|
48
|
+
return self.__class__.__name__
|
|
49
|
+
|
|
50
|
+
def __str__(self) -> str:
|
|
51
|
+
return " ".join((*self.args, self.detail)).strip()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MissingDependencyError(SQLSpecError, ImportError):
|
|
55
|
+
"""Raised when a required dependency is not installed."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, package: str, install_package: Optional[str] = None) -> None:
|
|
58
|
+
super().__init__(
|
|
59
|
+
f"Package {package!r} is not installed but required. You can install it by running "
|
|
60
|
+
f"'pip install sqlspec[{install_package or package}]' to install sqlspec with the required extra "
|
|
61
|
+
f"or 'pip install {install_package or package}' to install the package separately"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BackendNotRegisteredError(SQLSpecError):
|
|
66
|
+
"""Raised when a requested storage backend key is not registered."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, backend_key: str) -> None:
|
|
69
|
+
super().__init__(f"Storage backend '{backend_key}' is not registered. Please register it before use.")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SQLParsingError(SQLSpecError):
|
|
73
|
+
"""Issues parsing SQL statements."""
|
|
74
|
+
|
|
75
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
76
|
+
if message is None:
|
|
77
|
+
message = "Issues parsing SQL statement."
|
|
78
|
+
super().__init__(message)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class SQLBuilderError(SQLSpecError):
|
|
82
|
+
"""Issues Building or Generating SQL statements."""
|
|
83
|
+
|
|
84
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
85
|
+
if message is None:
|
|
86
|
+
message = "Issues building SQL statement."
|
|
87
|
+
super().__init__(message)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SQLConversionError(SQLSpecError):
|
|
91
|
+
"""Issues converting SQL statements."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
94
|
+
if message is None:
|
|
95
|
+
message = "Issues converting SQL statement."
|
|
96
|
+
super().__init__(message)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ImproperConfigurationError(SQLSpecError):
|
|
100
|
+
"""Raised when configuration is invalid or incomplete."""
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class SerializationError(SQLSpecError):
|
|
104
|
+
"""Encoding or decoding of an object failed."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class RepositoryError(SQLSpecError):
|
|
108
|
+
"""Base repository exception type."""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class IntegrityError(RepositoryError):
|
|
112
|
+
"""Data integrity error."""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class NotFoundError(RepositoryError):
|
|
116
|
+
"""An identity does not exist."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class MultipleResultsFoundError(RepositoryError):
|
|
120
|
+
"""A single database result was required but more than one were found."""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class StorageOperationFailedError(SQLSpecError):
|
|
124
|
+
"""Raised when a storage backend operation fails (e.g., network, permission, API error)."""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class FileNotFoundInStorageError(StorageOperationFailedError):
|
|
128
|
+
"""Raised when a file or object is not found in the storage backend."""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SQLFileNotFoundError(SQLSpecError):
|
|
132
|
+
"""Raised when a SQL file cannot be found."""
|
|
133
|
+
|
|
134
|
+
def __init__(self, name: str, path: "Optional[str]" = None) -> None:
|
|
135
|
+
"""Initialize the error.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
name: Name of the SQL file.
|
|
139
|
+
path: Optional path where the file was expected.
|
|
140
|
+
"""
|
|
141
|
+
message = f"SQL file '{name}' not found at path: {path}" if path else f"SQL file '{name}' not found"
|
|
142
|
+
super().__init__(message)
|
|
143
|
+
self.name = name
|
|
144
|
+
self.path = path
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SQLFileParseError(SQLSpecError):
|
|
148
|
+
"""Raised when a SQL file cannot be parsed."""
|
|
149
|
+
|
|
150
|
+
def __init__(self, name: str, path: str, original_error: "Exception") -> None:
|
|
151
|
+
"""Initialize the error.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
name: Name of the SQL file.
|
|
155
|
+
path: Path to the SQL file.
|
|
156
|
+
original_error: The underlying parsing error.
|
|
157
|
+
"""
|
|
158
|
+
message = f"Failed to parse SQL file '{name}' at {path}: {original_error}"
|
|
159
|
+
super().__init__(message)
|
|
160
|
+
self.name = name
|
|
161
|
+
self.path = path
|
|
162
|
+
self.original_error = original_error
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@contextmanager
|
|
166
|
+
def wrap_exceptions(
|
|
167
|
+
wrap_exceptions: bool = True, suppress: "Optional[Union[type[Exception], tuple[type[Exception], ...]]]" = None
|
|
168
|
+
) -> Generator[None, None, None]:
|
|
169
|
+
"""Context manager for exception handling with optional suppression.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
wrap_exceptions: If True, wrap exceptions in RepositoryError. If False, let them pass through.
|
|
173
|
+
suppress: Exception type(s) to suppress completely (like contextlib.suppress).
|
|
174
|
+
If provided, these exceptions are caught and ignored.
|
|
175
|
+
"""
|
|
176
|
+
try:
|
|
177
|
+
yield
|
|
178
|
+
|
|
179
|
+
except Exception as exc:
|
|
180
|
+
if suppress is not None and (
|
|
181
|
+
(isinstance(suppress, type) and isinstance(exc, suppress))
|
|
182
|
+
or (isinstance(suppress, tuple) and isinstance(exc, suppress))
|
|
183
|
+
):
|
|
184
|
+
return # Suppress this exception
|
|
185
|
+
|
|
186
|
+
# If it's already a SQLSpec exception, don't wrap it
|
|
187
|
+
if isinstance(exc, SQLSpecError):
|
|
188
|
+
raise
|
|
189
|
+
|
|
190
|
+
if wrap_exceptions is False:
|
|
191
|
+
raise
|
|
192
|
+
msg = "An error occurred during the operation."
|
|
193
|
+
raise RepositoryError(detail=msg) from exc
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""SQLSpec aiosql integration for loading SQL files.
|
|
2
|
+
|
|
3
|
+
This module provides a simple way to load aiosql-style SQL files and use them
|
|
4
|
+
with SQLSpec drivers. It focuses on just the file parsing functionality,
|
|
5
|
+
returning SQL objects that work with existing SQLSpec execution.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sqlspec.extensions.aiosql.adapter import AiosqlAsyncAdapter, AiosqlSyncAdapter
|
|
9
|
+
|
|
10
|
+
__all__ = ("AiosqlAsyncAdapter", "AiosqlSyncAdapter")
|