sqlspec 0.14.0__py3-none-any.whl → 0.15.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 +50 -25
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +256 -120
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +115 -248
- sqlspec/adapters/adbc/driver.py +462 -353
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +199 -129
- sqlspec/adapters/aiosqlite/driver.py +230 -269
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -168
- sqlspec/adapters/asyncmy/driver.py +260 -225
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +82 -181
- sqlspec/adapters/asyncpg/driver.py +285 -383
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -258
- sqlspec/adapters/bigquery/driver.py +474 -646
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +415 -351
- sqlspec/adapters/duckdb/driver.py +343 -413
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -379
- sqlspec/adapters/oracledb/driver.py +507 -560
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -254
- sqlspec/adapters/psqlpy/driver.py +505 -234
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -403
- sqlspec/adapters/psycopg/driver.py +706 -872
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +202 -118
- sqlspec/adapters/sqlite/driver.py +264 -303
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder → builder}/_base.py +120 -55
- sqlspec/{statement/builder → builder}/_column.py +17 -6
- sqlspec/{statement/builder → builder}/_ddl.py +46 -79
- sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
- sqlspec/{statement/builder → builder}/_delete.py +6 -25
- sqlspec/{statement/builder → builder}/_insert.py +6 -64
- sqlspec/builder/_merge.py +56 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +3 -10
- sqlspec/{statement/builder → builder}/_select.py +11 -56
- sqlspec/{statement/builder → builder}/_update.py +12 -18
- sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
- sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
- sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +22 -16
- sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
- sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +3 -5
- sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
- sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
- sqlspec/{statement/builder → builder}/mixins/_select_operations.py +21 -36
- sqlspec/{statement/builder → builder}/mixins/_update_operations.py +3 -14
- sqlspec/{statement/builder → builder}/mixins/_where_clause.py +52 -79
- sqlspec/cli.py +4 -5
- sqlspec/config.py +180 -133
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +873 -0
- sqlspec/core/compiler.py +396 -0
- sqlspec/core/filters.py +828 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1209 -0
- sqlspec/core/result.py +664 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +651 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +387 -176
- sqlspec/driver/_common.py +527 -289
- sqlspec/driver/_sync.py +390 -172
- sqlspec/driver/mixins/__init__.py +2 -19
- sqlspec/driver/mixins/_result_tools.py +168 -0
- sqlspec/driver/mixins/_sql_translator.py +6 -3
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +16 -14
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +424 -105
- sqlspec/migrations/__init__.py +12 -0
- sqlspec/migrations/base.py +92 -68
- sqlspec/migrations/commands.py +24 -106
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +49 -51
- sqlspec/migrations/tracker.py +31 -44
- sqlspec/migrations/utils.py +64 -24
- sqlspec/protocols.py +7 -183
- sqlspec/storage/__init__.py +1 -1
- sqlspec/storage/backends/base.py +37 -40
- sqlspec/storage/backends/fsspec.py +136 -112
- sqlspec/storage/backends/obstore.py +138 -160
- sqlspec/storage/capabilities.py +5 -4
- sqlspec/storage/registry.py +57 -106
- sqlspec/typing.py +136 -115
- sqlspec/utils/__init__.py +2 -3
- sqlspec/utils/correlation.py +0 -3
- sqlspec/utils/deprecation.py +6 -6
- sqlspec/utils/fixtures.py +6 -6
- sqlspec/utils/logging.py +0 -2
- sqlspec/utils/module_loader.py +7 -12
- sqlspec/utils/singleton.py +0 -1
- sqlspec/utils/sync_tools.py +16 -37
- sqlspec/utils/text.py +12 -51
- sqlspec/utils/type_guards.py +443 -232
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/METADATA +7 -2
- sqlspec-0.15.0.dist-info/RECORD +134 -0
- sqlspec-0.15.0.dist-info/entry_points.txt +2 -0
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_cache.py +0 -114
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -508
- sqlspec/driver/mixins/_query_tools.py +0 -796
- sqlspec/driver/mixins/_result_utils.py +0 -138
- sqlspec/driver/mixins/_storage.py +0 -912
- sqlspec/driver/mixins/_type_coercion.py +0 -128
- sqlspec/driver/parameters.py +0 -138
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/_merge.py +0 -95
- sqlspec/statement/cache.py +0 -50
- sqlspec/statement/filters.py +0 -625
- sqlspec/statement/parameters.py +0 -996
- sqlspec/statement/pipelines/__init__.py +0 -210
- sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
- sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
- sqlspec/statement/pipelines/context.py +0 -115
- sqlspec/statement/pipelines/transformers/__init__.py +0 -7
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
- sqlspec/statement/pipelines/validators/__init__.py +0 -23
- sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
- sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
- sqlspec/statement/pipelines/validators/_performance.py +0 -714
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1774
- sqlspec/utils/cached_property.py +0 -25
- sqlspec/utils/statement_hashing.py +0 -203
- sqlspec-0.14.0.dist-info/RECORD +0 -143
- sqlspec-0.14.0.dist-info/entry_points.txt +0 -2
- /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/utils/type_guards.py
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
This module provides type-safe runtime checks that help the type checker
|
|
4
4
|
understand type narrowing, replacing defensive hasattr() and duck typing patterns.
|
|
5
|
-
|
|
6
|
-
NOTE: Some sqlspec imports are nested inside functions to prevent circular
|
|
7
|
-
imports where necessary. This module is imported by core sqlspec modules,
|
|
8
|
-
so imports that would create cycles are deferred.
|
|
9
5
|
"""
|
|
10
6
|
|
|
11
|
-
from collections.abc import
|
|
7
|
+
from collections.abc import Sequence
|
|
12
8
|
from collections.abc import Set as AbstractSet
|
|
13
9
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
14
10
|
|
|
15
11
|
from sqlspec.typing import (
|
|
12
|
+
ATTRS_INSTALLED,
|
|
16
13
|
LITESTAR_INSTALLED,
|
|
17
14
|
MSGSPEC_INSTALLED,
|
|
18
15
|
PYDANTIC_INSTALLED,
|
|
@@ -20,6 +17,8 @@ from sqlspec.typing import (
|
|
|
20
17
|
DataclassProtocol,
|
|
21
18
|
DTOData,
|
|
22
19
|
Struct,
|
|
20
|
+
attrs_asdict,
|
|
21
|
+
attrs_has,
|
|
23
22
|
)
|
|
24
23
|
|
|
25
24
|
if TYPE_CHECKING:
|
|
@@ -28,12 +27,10 @@ if TYPE_CHECKING:
|
|
|
28
27
|
from sqlglot import exp
|
|
29
28
|
from typing_extensions import TypeGuard
|
|
30
29
|
|
|
30
|
+
from sqlspec._typing import AttrsInstanceStub, BaseModelStub, DTODataStub, StructStub
|
|
31
|
+
from sqlspec.builder import Select
|
|
32
|
+
from sqlspec.core.filters import LimitOffsetFilter, StatementFilter
|
|
31
33
|
from sqlspec.protocols import (
|
|
32
|
-
AsyncCloseableConnectionProtocol,
|
|
33
|
-
AsyncCopyCapableConnectionProtocol,
|
|
34
|
-
AsyncPipelineCapableDriverProtocol,
|
|
35
|
-
AsyncTransactionCapableConnectionProtocol,
|
|
36
|
-
AsyncTransactionStateConnectionProtocol,
|
|
37
34
|
BytesConvertibleProtocol,
|
|
38
35
|
DictProtocol,
|
|
39
36
|
FilterAppenderProtocol,
|
|
@@ -42,22 +39,14 @@ if TYPE_CHECKING:
|
|
|
42
39
|
HasLimitProtocol,
|
|
43
40
|
HasOffsetProtocol,
|
|
44
41
|
HasOrderByProtocol,
|
|
45
|
-
HasRiskLevelProtocol,
|
|
46
42
|
HasSQLMethodProtocol,
|
|
47
43
|
HasWhereProtocol,
|
|
48
44
|
IndexableRow,
|
|
49
45
|
ObjectStoreItemProtocol,
|
|
50
46
|
ParameterValueProtocol,
|
|
51
47
|
SQLBuilderProtocol,
|
|
52
|
-
SyncCloseableConnectionProtocol,
|
|
53
|
-
SyncCopyCapableConnectionProtocol,
|
|
54
|
-
SyncPipelineCapableDriverProtocol,
|
|
55
|
-
SyncTransactionCapableConnectionProtocol,
|
|
56
|
-
SyncTransactionStateConnectionProtocol,
|
|
57
48
|
WithMethodProtocol,
|
|
58
49
|
)
|
|
59
|
-
from sqlspec.statement.builder import Select
|
|
60
|
-
from sqlspec.statement.filters import LimitOffsetFilter, StatementFilter
|
|
61
50
|
from sqlspec.typing import SupportedSchemaModel
|
|
62
51
|
|
|
63
52
|
__all__ = (
|
|
@@ -65,25 +54,35 @@ __all__ = (
|
|
|
65
54
|
"can_convert_to_schema",
|
|
66
55
|
"can_extract_parameters",
|
|
67
56
|
"dataclass_to_dict",
|
|
57
|
+
"expression_has_limit",
|
|
68
58
|
"extract_dataclass_fields",
|
|
69
59
|
"extract_dataclass_items",
|
|
60
|
+
"get_initial_expression",
|
|
61
|
+
"get_literal_parent",
|
|
62
|
+
"get_node_expressions",
|
|
63
|
+
"get_node_this",
|
|
64
|
+
"get_param_style_and_name",
|
|
65
|
+
"get_value_attribute",
|
|
66
|
+
"has_attr",
|
|
70
67
|
"has_bytes_conversion",
|
|
71
68
|
"has_dict_attribute",
|
|
72
69
|
"has_expression_attr",
|
|
73
70
|
"has_expressions",
|
|
71
|
+
"has_expressions_attribute",
|
|
74
72
|
"has_parameter_builder",
|
|
75
73
|
"has_parameter_value",
|
|
74
|
+
"has_parent_attribute",
|
|
76
75
|
"has_query_builder_parameters",
|
|
77
|
-
"has_risk_level",
|
|
78
76
|
"has_sql_method",
|
|
79
77
|
"has_sqlglot_expression",
|
|
78
|
+
"has_this_attribute",
|
|
80
79
|
"has_to_statement",
|
|
81
80
|
"has_with_method",
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
81
|
+
"is_attrs_instance",
|
|
82
|
+
"is_attrs_instance_with_field",
|
|
83
|
+
"is_attrs_instance_without_field",
|
|
84
|
+
"is_attrs_schema",
|
|
85
|
+
"is_copy_statement",
|
|
87
86
|
"is_dataclass",
|
|
88
87
|
"is_dataclass_instance",
|
|
89
88
|
"is_dataclass_with_field",
|
|
@@ -100,6 +99,7 @@ __all__ = (
|
|
|
100
99
|
"is_msgspec_struct",
|
|
101
100
|
"is_msgspec_struct_with_field",
|
|
102
101
|
"is_msgspec_struct_without_field",
|
|
102
|
+
"is_number_literal",
|
|
103
103
|
"is_object_store_item",
|
|
104
104
|
"is_pydantic_model",
|
|
105
105
|
"is_pydantic_model_with_field",
|
|
@@ -112,11 +112,8 @@ __all__ = (
|
|
|
112
112
|
"is_schema_without_field",
|
|
113
113
|
"is_select_builder",
|
|
114
114
|
"is_statement_filter",
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"is_sync_pipeline_capable_driver",
|
|
118
|
-
"is_sync_transaction_capable",
|
|
119
|
-
"is_sync_transaction_state_capable",
|
|
115
|
+
"is_string_literal",
|
|
116
|
+
"is_typed_parameter",
|
|
120
117
|
"schema_dump",
|
|
121
118
|
"supports_limit",
|
|
122
119
|
"supports_offset",
|
|
@@ -134,7 +131,7 @@ def is_statement_filter(obj: Any) -> "TypeGuard[StatementFilter]":
|
|
|
134
131
|
Returns:
|
|
135
132
|
True if the object is a StatementFilter, False otherwise
|
|
136
133
|
"""
|
|
137
|
-
from sqlspec.
|
|
134
|
+
from sqlspec.core.filters import StatementFilter as FilterProtocol
|
|
138
135
|
|
|
139
136
|
return isinstance(obj, FilterProtocol)
|
|
140
137
|
|
|
@@ -148,7 +145,7 @@ def is_limit_offset_filter(obj: Any) -> "TypeGuard[LimitOffsetFilter]":
|
|
|
148
145
|
Returns:
|
|
149
146
|
True if the object is a LimitOffsetFilter, False otherwise
|
|
150
147
|
"""
|
|
151
|
-
from sqlspec.
|
|
148
|
+
from sqlspec.core.filters import LimitOffsetFilter
|
|
152
149
|
|
|
153
150
|
return isinstance(obj, LimitOffsetFilter)
|
|
154
151
|
|
|
@@ -162,7 +159,7 @@ def is_select_builder(obj: Any) -> "TypeGuard[Select]":
|
|
|
162
159
|
Returns:
|
|
163
160
|
True if the object is a Select, False otherwise
|
|
164
161
|
"""
|
|
165
|
-
from sqlspec.
|
|
162
|
+
from sqlspec.builder import Select
|
|
166
163
|
|
|
167
164
|
return isinstance(obj, Select)
|
|
168
165
|
|
|
@@ -193,16 +190,16 @@ def is_indexable_row(row: Any) -> "TypeGuard[IndexableRow]":
|
|
|
193
190
|
return isinstance(row, IndexableRow)
|
|
194
191
|
|
|
195
192
|
|
|
196
|
-
def is_iterable_parameters(
|
|
193
|
+
def is_iterable_parameters(parameters: Any) -> "TypeGuard[Sequence[Any]]":
|
|
197
194
|
"""Check if parameters are iterable (but not string or dict).
|
|
198
195
|
|
|
199
196
|
Args:
|
|
200
|
-
|
|
197
|
+
parameters: The parameters to check
|
|
201
198
|
|
|
202
199
|
Returns:
|
|
203
200
|
True if the parameters are iterable, False otherwise
|
|
204
201
|
"""
|
|
205
|
-
return isinstance(
|
|
202
|
+
return isinstance(parameters, Sequence) and not isinstance(parameters, (str, bytes, dict))
|
|
206
203
|
|
|
207
204
|
|
|
208
205
|
def has_with_method(obj: Any) -> "TypeGuard[WithMethodProtocol]":
|
|
@@ -237,9 +234,6 @@ def can_convert_to_schema(obj: Any) -> "TypeGuard[Any]":
|
|
|
237
234
|
return isinstance(obj, ToSchemaMixin)
|
|
238
235
|
|
|
239
236
|
|
|
240
|
-
# Type guards migrated from typing.py
|
|
241
|
-
|
|
242
|
-
|
|
243
237
|
def is_dataclass_instance(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
244
238
|
"""Check if an object is a dataclass instance.
|
|
245
239
|
|
|
@@ -249,8 +243,14 @@ def is_dataclass_instance(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
|
249
243
|
Returns:
|
|
250
244
|
True if the object is a dataclass instance.
|
|
251
245
|
"""
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
if isinstance(obj, type):
|
|
247
|
+
return False
|
|
248
|
+
try:
|
|
249
|
+
_ = type(obj).__dataclass_fields__
|
|
250
|
+
except AttributeError:
|
|
251
|
+
return False
|
|
252
|
+
else:
|
|
253
|
+
return True
|
|
254
254
|
|
|
255
255
|
|
|
256
256
|
def is_dataclass(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
@@ -262,8 +262,13 @@ def is_dataclass(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
|
262
262
|
Returns:
|
|
263
263
|
bool
|
|
264
264
|
"""
|
|
265
|
-
if isinstance(obj, type)
|
|
266
|
-
|
|
265
|
+
if isinstance(obj, type):
|
|
266
|
+
try:
|
|
267
|
+
_ = obj.__dataclass_fields__ # type: ignore[attr-defined]
|
|
268
|
+
except AttributeError:
|
|
269
|
+
return False
|
|
270
|
+
else:
|
|
271
|
+
return True
|
|
267
272
|
return is_dataclass_instance(obj)
|
|
268
273
|
|
|
269
274
|
|
|
@@ -277,7 +282,14 @@ def is_dataclass_with_field(obj: Any, field_name: str) -> "TypeGuard[object]":
|
|
|
277
282
|
Returns:
|
|
278
283
|
bool
|
|
279
284
|
"""
|
|
280
|
-
|
|
285
|
+
if not is_dataclass(obj):
|
|
286
|
+
return False
|
|
287
|
+
try:
|
|
288
|
+
_ = getattr(obj, field_name)
|
|
289
|
+
except AttributeError:
|
|
290
|
+
return False
|
|
291
|
+
else:
|
|
292
|
+
return True
|
|
281
293
|
|
|
282
294
|
|
|
283
295
|
def is_dataclass_without_field(obj: Any, field_name: str) -> "TypeGuard[object]":
|
|
@@ -290,11 +302,18 @@ def is_dataclass_without_field(obj: Any, field_name: str) -> "TypeGuard[object]"
|
|
|
290
302
|
Returns:
|
|
291
303
|
bool
|
|
292
304
|
"""
|
|
293
|
-
|
|
305
|
+
if not is_dataclass(obj):
|
|
306
|
+
return False
|
|
307
|
+
try:
|
|
308
|
+
_ = getattr(obj, field_name)
|
|
309
|
+
except AttributeError:
|
|
310
|
+
return True
|
|
311
|
+
else:
|
|
312
|
+
return False
|
|
294
313
|
|
|
295
314
|
|
|
296
|
-
def is_pydantic_model(obj: Any) -> "TypeGuard[
|
|
297
|
-
"""Check if a value is a pydantic model.
|
|
315
|
+
def is_pydantic_model(obj: Any) -> "TypeGuard[Any]":
|
|
316
|
+
"""Check if a value is a pydantic model class or instance.
|
|
298
317
|
|
|
299
318
|
Args:
|
|
300
319
|
obj: Value to check.
|
|
@@ -302,10 +321,17 @@ def is_pydantic_model(obj: Any) -> "TypeGuard[BaseModel]":
|
|
|
302
321
|
Returns:
|
|
303
322
|
bool
|
|
304
323
|
"""
|
|
305
|
-
|
|
324
|
+
if not PYDANTIC_INSTALLED:
|
|
325
|
+
return False
|
|
326
|
+
if isinstance(obj, type):
|
|
327
|
+
try:
|
|
328
|
+
return issubclass(obj, BaseModel)
|
|
329
|
+
except TypeError:
|
|
330
|
+
return False
|
|
331
|
+
return isinstance(obj, BaseModel)
|
|
306
332
|
|
|
307
333
|
|
|
308
|
-
def is_pydantic_model_with_field(obj: Any, field_name: str) -> "TypeGuard[
|
|
334
|
+
def is_pydantic_model_with_field(obj: Any, field_name: str) -> "TypeGuard[BaseModelStub]":
|
|
309
335
|
"""Check if a pydantic model has a specific field.
|
|
310
336
|
|
|
311
337
|
Args:
|
|
@@ -315,10 +341,17 @@ def is_pydantic_model_with_field(obj: Any, field_name: str) -> "TypeGuard[BaseMo
|
|
|
315
341
|
Returns:
|
|
316
342
|
bool
|
|
317
343
|
"""
|
|
318
|
-
|
|
344
|
+
if not is_pydantic_model(obj):
|
|
345
|
+
return False
|
|
346
|
+
try:
|
|
347
|
+
_ = getattr(obj, field_name)
|
|
348
|
+
except AttributeError:
|
|
349
|
+
return False
|
|
350
|
+
else:
|
|
351
|
+
return True
|
|
319
352
|
|
|
320
353
|
|
|
321
|
-
def is_pydantic_model_without_field(obj: Any, field_name: str) -> "TypeGuard[
|
|
354
|
+
def is_pydantic_model_without_field(obj: Any, field_name: str) -> "TypeGuard[BaseModelStub]":
|
|
322
355
|
"""Check if a pydantic model does not have a specific field.
|
|
323
356
|
|
|
324
357
|
Args:
|
|
@@ -328,11 +361,18 @@ def is_pydantic_model_without_field(obj: Any, field_name: str) -> "TypeGuard[Bas
|
|
|
328
361
|
Returns:
|
|
329
362
|
bool
|
|
330
363
|
"""
|
|
331
|
-
|
|
364
|
+
if not is_pydantic_model(obj):
|
|
365
|
+
return False
|
|
366
|
+
try:
|
|
367
|
+
_ = getattr(obj, field_name)
|
|
368
|
+
except AttributeError:
|
|
369
|
+
return True
|
|
370
|
+
else:
|
|
371
|
+
return False
|
|
332
372
|
|
|
333
373
|
|
|
334
|
-
def is_msgspec_struct(obj: Any) -> "TypeGuard[
|
|
335
|
-
"""Check if a value is a msgspec struct.
|
|
374
|
+
def is_msgspec_struct(obj: Any) -> "TypeGuard[StructStub]":
|
|
375
|
+
"""Check if a value is a msgspec struct class or instance.
|
|
336
376
|
|
|
337
377
|
Args:
|
|
338
378
|
obj: Value to check.
|
|
@@ -340,10 +380,17 @@ def is_msgspec_struct(obj: Any) -> "TypeGuard[Struct]":
|
|
|
340
380
|
Returns:
|
|
341
381
|
bool
|
|
342
382
|
"""
|
|
343
|
-
|
|
383
|
+
if not MSGSPEC_INSTALLED:
|
|
384
|
+
return False
|
|
385
|
+
if isinstance(obj, type):
|
|
386
|
+
try:
|
|
387
|
+
return issubclass(obj, Struct)
|
|
388
|
+
except TypeError:
|
|
389
|
+
return False
|
|
390
|
+
return isinstance(obj, Struct)
|
|
344
391
|
|
|
345
392
|
|
|
346
|
-
def is_msgspec_struct_with_field(obj: Any, field_name: str) -> "TypeGuard[
|
|
393
|
+
def is_msgspec_struct_with_field(obj: Any, field_name: str) -> "TypeGuard[StructStub]":
|
|
347
394
|
"""Check if a msgspec struct has a specific field.
|
|
348
395
|
|
|
349
396
|
Args:
|
|
@@ -353,10 +400,17 @@ def is_msgspec_struct_with_field(obj: Any, field_name: str) -> "TypeGuard[Struct
|
|
|
353
400
|
Returns:
|
|
354
401
|
bool
|
|
355
402
|
"""
|
|
356
|
-
|
|
403
|
+
if not is_msgspec_struct(obj):
|
|
404
|
+
return False
|
|
405
|
+
try:
|
|
406
|
+
_ = getattr(obj, field_name)
|
|
357
407
|
|
|
408
|
+
except AttributeError:
|
|
409
|
+
return False
|
|
410
|
+
return True
|
|
358
411
|
|
|
359
|
-
|
|
412
|
+
|
|
413
|
+
def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[StructStub]":
|
|
360
414
|
"""Check if a msgspec struct does not have a specific field.
|
|
361
415
|
|
|
362
416
|
Args:
|
|
@@ -366,7 +420,63 @@ def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[Str
|
|
|
366
420
|
Returns:
|
|
367
421
|
bool
|
|
368
422
|
"""
|
|
369
|
-
|
|
423
|
+
if not is_msgspec_struct(obj):
|
|
424
|
+
return False
|
|
425
|
+
try:
|
|
426
|
+
_ = getattr(obj, field_name)
|
|
427
|
+
except AttributeError:
|
|
428
|
+
return True
|
|
429
|
+
return False
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def is_attrs_instance(obj: Any) -> "TypeGuard[AttrsInstanceStub]":
|
|
433
|
+
"""Check if a value is an attrs class instance.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
obj: Value to check.
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
bool
|
|
440
|
+
"""
|
|
441
|
+
return ATTRS_INSTALLED and attrs_has(obj.__class__)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def is_attrs_schema(cls: Any) -> "TypeGuard[type[AttrsInstanceStub]]":
|
|
445
|
+
"""Check if a class type is an attrs schema.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
cls: Class to check.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
bool
|
|
452
|
+
"""
|
|
453
|
+
return ATTRS_INSTALLED and attrs_has(cls)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def is_attrs_instance_with_field(obj: Any, field_name: str) -> "TypeGuard[AttrsInstanceStub]":
|
|
457
|
+
"""Check if an attrs instance has a specific field.
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
obj: Value to check.
|
|
461
|
+
field_name: Field name to check for.
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
bool
|
|
465
|
+
"""
|
|
466
|
+
return is_attrs_instance(obj) and hasattr(obj, field_name)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def is_attrs_instance_without_field(obj: Any, field_name: str) -> "TypeGuard[AttrsInstanceStub]":
|
|
470
|
+
"""Check if an attrs instance does not have a specific field.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
obj: Value to check.
|
|
474
|
+
field_name: Field name to check for.
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
bool
|
|
478
|
+
"""
|
|
479
|
+
return is_attrs_instance(obj) and not hasattr(obj, field_name)
|
|
370
480
|
|
|
371
481
|
|
|
372
482
|
def is_dict(obj: Any) -> "TypeGuard[dict[str, Any]]":
|
|
@@ -408,7 +518,7 @@ def is_dict_without_field(obj: Any, field_name: str) -> "TypeGuard[dict[str, Any
|
|
|
408
518
|
|
|
409
519
|
|
|
410
520
|
def is_schema(obj: Any) -> "TypeGuard[SupportedSchemaModel]":
|
|
411
|
-
"""Check if a value is a msgspec Struct or
|
|
521
|
+
"""Check if a value is a msgspec Struct, Pydantic model, attrs instance, or schema class.
|
|
412
522
|
|
|
413
523
|
Args:
|
|
414
524
|
obj: Value to check.
|
|
@@ -416,7 +526,13 @@ def is_schema(obj: Any) -> "TypeGuard[SupportedSchemaModel]":
|
|
|
416
526
|
Returns:
|
|
417
527
|
bool
|
|
418
528
|
"""
|
|
419
|
-
return
|
|
529
|
+
return (
|
|
530
|
+
is_msgspec_struct(obj)
|
|
531
|
+
or is_pydantic_model(obj)
|
|
532
|
+
or is_attrs_instance(obj)
|
|
533
|
+
or is_attrs_schema(obj)
|
|
534
|
+
or is_dataclass(obj)
|
|
535
|
+
)
|
|
420
536
|
|
|
421
537
|
|
|
422
538
|
def is_schema_or_dict(obj: Any) -> "TypeGuard[Union[SupportedSchemaModel, dict[str, Any]]]":
|
|
@@ -485,7 +601,7 @@ def is_schema_or_dict_without_field(
|
|
|
485
601
|
return not is_schema_or_dict_with_field(obj, field_name)
|
|
486
602
|
|
|
487
603
|
|
|
488
|
-
def is_dto_data(v: Any) -> "TypeGuard[
|
|
604
|
+
def is_dto_data(v: Any) -> "TypeGuard[DTODataStub[Any]]":
|
|
489
605
|
"""Check if a value is a Litestar DTOData object.
|
|
490
606
|
|
|
491
607
|
Args:
|
|
@@ -525,149 +641,6 @@ def has_dict_attribute(obj: Any) -> "TypeGuard[DictProtocol]":
|
|
|
525
641
|
return isinstance(obj, DictProtocol)
|
|
526
642
|
|
|
527
643
|
|
|
528
|
-
def is_sync_transaction_capable(obj: Any) -> "TypeGuard[SyncTransactionCapableConnectionProtocol]":
|
|
529
|
-
"""Check if a connection supports sync transactions.
|
|
530
|
-
|
|
531
|
-
Args:
|
|
532
|
-
obj: Connection object to check.
|
|
533
|
-
|
|
534
|
-
Returns:
|
|
535
|
-
True if the connection has commit and rollback methods.
|
|
536
|
-
"""
|
|
537
|
-
from sqlspec.protocols import SyncTransactionCapableConnectionProtocol
|
|
538
|
-
|
|
539
|
-
return isinstance(obj, SyncTransactionCapableConnectionProtocol)
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
def is_async_transaction_capable(obj: Any) -> "TypeGuard[AsyncTransactionCapableConnectionProtocol]":
|
|
543
|
-
"""Check if a connection supports async transactions.
|
|
544
|
-
|
|
545
|
-
Args:
|
|
546
|
-
obj: Connection object to check.
|
|
547
|
-
|
|
548
|
-
Returns:
|
|
549
|
-
True if the connection has async commit and rollback methods.
|
|
550
|
-
"""
|
|
551
|
-
from sqlspec.protocols import AsyncTransactionCapableConnectionProtocol
|
|
552
|
-
|
|
553
|
-
return isinstance(obj, AsyncTransactionCapableConnectionProtocol)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
def is_sync_transaction_state_capable(obj: Any) -> "TypeGuard[SyncTransactionStateConnectionProtocol]":
|
|
557
|
-
"""Check if a connection can report sync transaction state.
|
|
558
|
-
|
|
559
|
-
Args:
|
|
560
|
-
obj: Connection object to check.
|
|
561
|
-
|
|
562
|
-
Returns:
|
|
563
|
-
True if the connection has in_transaction and begin methods.
|
|
564
|
-
"""
|
|
565
|
-
from sqlspec.protocols import SyncTransactionStateConnectionProtocol
|
|
566
|
-
|
|
567
|
-
return isinstance(obj, SyncTransactionStateConnectionProtocol)
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
def is_async_transaction_state_capable(obj: Any) -> "TypeGuard[AsyncTransactionStateConnectionProtocol]":
|
|
571
|
-
"""Check if a connection can report async transaction state.
|
|
572
|
-
|
|
573
|
-
Args:
|
|
574
|
-
obj: Connection object to check.
|
|
575
|
-
|
|
576
|
-
Returns:
|
|
577
|
-
True if the connection has in_transaction and async begin methods.
|
|
578
|
-
"""
|
|
579
|
-
from sqlspec.protocols import AsyncTransactionStateConnectionProtocol
|
|
580
|
-
|
|
581
|
-
return isinstance(obj, AsyncTransactionStateConnectionProtocol)
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
def is_sync_closeable_connection(obj: Any) -> "TypeGuard[SyncCloseableConnectionProtocol]":
|
|
585
|
-
"""Check if a connection can be closed synchronously.
|
|
586
|
-
|
|
587
|
-
Args:
|
|
588
|
-
obj: Connection object to check.
|
|
589
|
-
|
|
590
|
-
Returns:
|
|
591
|
-
True if the connection has a close method.
|
|
592
|
-
"""
|
|
593
|
-
from sqlspec.protocols import SyncCloseableConnectionProtocol
|
|
594
|
-
|
|
595
|
-
return isinstance(obj, SyncCloseableConnectionProtocol)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
def is_async_closeable_connection(obj: Any) -> "TypeGuard[AsyncCloseableConnectionProtocol]":
|
|
599
|
-
"""Check if a connection can be closed asynchronously.
|
|
600
|
-
|
|
601
|
-
Args:
|
|
602
|
-
obj: Connection object to check.
|
|
603
|
-
|
|
604
|
-
Returns:
|
|
605
|
-
True if the connection has an async close method.
|
|
606
|
-
"""
|
|
607
|
-
from sqlspec.protocols import AsyncCloseableConnectionProtocol
|
|
608
|
-
|
|
609
|
-
return isinstance(obj, AsyncCloseableConnectionProtocol)
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
def is_sync_copy_capable(obj: Any) -> "TypeGuard[SyncCopyCapableConnectionProtocol]":
|
|
613
|
-
"""Check if a connection supports sync COPY operations.
|
|
614
|
-
|
|
615
|
-
Args:
|
|
616
|
-
obj: Connection object to check.
|
|
617
|
-
|
|
618
|
-
Returns:
|
|
619
|
-
True if the connection has copy_from and copy_to methods.
|
|
620
|
-
"""
|
|
621
|
-
from sqlspec.protocols import SyncCopyCapableConnectionProtocol
|
|
622
|
-
|
|
623
|
-
return isinstance(obj, SyncCopyCapableConnectionProtocol)
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
def is_async_copy_capable(obj: Any) -> "TypeGuard[AsyncCopyCapableConnectionProtocol]":
|
|
627
|
-
"""Check if a connection supports async COPY operations.
|
|
628
|
-
|
|
629
|
-
Args:
|
|
630
|
-
obj: Connection object to check.
|
|
631
|
-
|
|
632
|
-
Returns:
|
|
633
|
-
True if the connection has async copy_from and copy_to methods.
|
|
634
|
-
"""
|
|
635
|
-
from sqlspec.protocols import AsyncCopyCapableConnectionProtocol
|
|
636
|
-
|
|
637
|
-
return isinstance(obj, AsyncCopyCapableConnectionProtocol)
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
def is_sync_pipeline_capable_driver(obj: Any) -> "TypeGuard[SyncPipelineCapableDriverProtocol]":
|
|
641
|
-
"""Check if a driver supports sync native pipeline execution.
|
|
642
|
-
|
|
643
|
-
Args:
|
|
644
|
-
obj: Driver object to check.
|
|
645
|
-
|
|
646
|
-
Returns:
|
|
647
|
-
True if the driver has _execute_pipeline_native method.
|
|
648
|
-
"""
|
|
649
|
-
from sqlspec.protocols import SyncPipelineCapableDriverProtocol
|
|
650
|
-
|
|
651
|
-
return isinstance(obj, SyncPipelineCapableDriverProtocol)
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
def is_async_pipeline_capable_driver(obj: Any) -> "TypeGuard[AsyncPipelineCapableDriverProtocol]":
|
|
655
|
-
"""Check if a driver supports async native pipeline execution.
|
|
656
|
-
|
|
657
|
-
Args:
|
|
658
|
-
obj: Driver object to check.
|
|
659
|
-
|
|
660
|
-
Returns:
|
|
661
|
-
True if the driver has async _execute_pipeline_native method.
|
|
662
|
-
"""
|
|
663
|
-
from sqlspec.protocols import AsyncPipelineCapableDriverProtocol
|
|
664
|
-
|
|
665
|
-
return isinstance(obj, AsyncPipelineCapableDriverProtocol)
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
# Dataclass utility functions
|
|
669
|
-
|
|
670
|
-
|
|
671
644
|
def extract_dataclass_fields(
|
|
672
645
|
obj: "DataclassProtocol",
|
|
673
646
|
exclude_none: bool = False,
|
|
@@ -701,15 +674,15 @@ def extract_dataclass_fields(
|
|
|
701
674
|
msg = f"Fields {common} are both included and excluded."
|
|
702
675
|
raise ValueError(msg)
|
|
703
676
|
|
|
704
|
-
dataclass_fields:
|
|
677
|
+
dataclass_fields: list[Field[Any]] = list(fields(obj))
|
|
705
678
|
if exclude_none:
|
|
706
|
-
dataclass_fields =
|
|
679
|
+
dataclass_fields = [field for field in dataclass_fields if getattr(obj, field.name) is not None]
|
|
707
680
|
if exclude_empty:
|
|
708
|
-
dataclass_fields =
|
|
681
|
+
dataclass_fields = [field for field in dataclass_fields if getattr(obj, field.name) is not Empty]
|
|
709
682
|
if include:
|
|
710
|
-
dataclass_fields =
|
|
683
|
+
dataclass_fields = [field for field in dataclass_fields if field.name in include]
|
|
711
684
|
if exclude:
|
|
712
|
-
dataclass_fields =
|
|
685
|
+
dataclass_fields = [field for field in dataclass_fields if field.name not in exclude]
|
|
713
686
|
|
|
714
687
|
return tuple(dataclass_fields)
|
|
715
688
|
|
|
@@ -721,9 +694,7 @@ def extract_dataclass_items(
|
|
|
721
694
|
include: "Optional[AbstractSet[str]]" = None,
|
|
722
695
|
exclude: "Optional[AbstractSet[str]]" = None,
|
|
723
696
|
) -> "tuple[tuple[str, Any], ...]":
|
|
724
|
-
"""Extract
|
|
725
|
-
|
|
726
|
-
Unlike the 'asdict' method exports by the stdlib, this function does not pickle values.
|
|
697
|
+
"""Extract name-value pairs from a dataclass instance.
|
|
727
698
|
|
|
728
699
|
Args:
|
|
729
700
|
obj: A dataclass instance.
|
|
@@ -746,11 +717,7 @@ def dataclass_to_dict(
|
|
|
746
717
|
convert_nested: bool = True,
|
|
747
718
|
exclude: "Optional[AbstractSet[str]]" = None,
|
|
748
719
|
) -> "dict[str, Any]":
|
|
749
|
-
"""Convert a dataclass to a dictionary.
|
|
750
|
-
|
|
751
|
-
This method has important differences to the standard library version:
|
|
752
|
-
- it does not deepcopy values
|
|
753
|
-
- it does not recurse into collections
|
|
720
|
+
"""Convert a dataclass instance to a dictionary.
|
|
754
721
|
|
|
755
722
|
Args:
|
|
756
723
|
obj: A dataclass instance.
|
|
@@ -772,13 +739,11 @@ def dataclass_to_dict(
|
|
|
772
739
|
return cast("dict[str, Any]", ret)
|
|
773
740
|
|
|
774
741
|
|
|
775
|
-
def schema_dump(
|
|
776
|
-
data: "Union[dict[str, Any], DataclassProtocol, Struct, BaseModel]", exclude_unset: bool = True
|
|
777
|
-
) -> "dict[str, Any]":
|
|
742
|
+
def schema_dump(data: Any, exclude_unset: bool = True) -> "dict[str, Any]":
|
|
778
743
|
"""Dump a data object to a dictionary.
|
|
779
744
|
|
|
780
745
|
Args:
|
|
781
|
-
data: :type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel`
|
|
746
|
+
data: :type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel` | :class:`AttrsInstance`
|
|
782
747
|
exclude_unset: :type:`bool` Whether to exclude unset values.
|
|
783
748
|
|
|
784
749
|
Returns:
|
|
@@ -791,20 +756,19 @@ def schema_dump(
|
|
|
791
756
|
if is_dataclass(data):
|
|
792
757
|
return dataclass_to_dict(data, exclude_empty=exclude_unset)
|
|
793
758
|
if is_pydantic_model(data):
|
|
794
|
-
return data.model_dump(exclude_unset=exclude_unset)
|
|
759
|
+
return data.model_dump(exclude_unset=exclude_unset) # type: ignore[no-any-return]
|
|
795
760
|
if is_msgspec_struct(data):
|
|
796
761
|
if exclude_unset:
|
|
797
762
|
return {f: val for f in data.__struct_fields__ if (val := getattr(data, f, None)) != UNSET}
|
|
798
763
|
return {f: getattr(data, f, None) for f in data.__struct_fields__}
|
|
764
|
+
if is_attrs_instance(data):
|
|
765
|
+
return attrs_asdict(data)
|
|
799
766
|
|
|
800
767
|
if has_dict_attribute(data):
|
|
801
768
|
return data.__dict__
|
|
802
769
|
return cast("dict[str, Any]", data)
|
|
803
770
|
|
|
804
771
|
|
|
805
|
-
# New type guards for hasattr() pattern replacement
|
|
806
|
-
|
|
807
|
-
|
|
808
772
|
def can_extract_parameters(obj: Any) -> "TypeGuard[FilterParameterProtocol]":
|
|
809
773
|
"""Check if an object can extract parameters."""
|
|
810
774
|
from sqlspec.protocols import FilterParameterProtocol
|
|
@@ -826,13 +790,6 @@ def has_parameter_value(obj: Any) -> "TypeGuard[ParameterValueProtocol]":
|
|
|
826
790
|
return isinstance(obj, ParameterValueProtocol)
|
|
827
791
|
|
|
828
792
|
|
|
829
|
-
def has_risk_level(obj: Any) -> "TypeGuard[HasRiskLevelProtocol]":
|
|
830
|
-
"""Check if an object has a risk_level attribute."""
|
|
831
|
-
from sqlspec.protocols import HasRiskLevelProtocol
|
|
832
|
-
|
|
833
|
-
return isinstance(obj, HasRiskLevelProtocol)
|
|
834
|
-
|
|
835
|
-
|
|
836
793
|
def supports_where(obj: Any) -> "TypeGuard[HasWhereProtocol]":
|
|
837
794
|
"""Check if an SQL expression supports WHERE clauses."""
|
|
838
795
|
from sqlspec.protocols import HasWhereProtocol
|
|
@@ -922,3 +879,257 @@ def has_to_statement(obj: Any) -> "TypeGuard[Any]":
|
|
|
922
879
|
from sqlspec.protocols import HasToStatementProtocol
|
|
923
880
|
|
|
924
881
|
return isinstance(obj, HasToStatementProtocol)
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def has_attr(obj: Any, attr: str) -> bool:
|
|
885
|
+
"""Safe replacement for hasattr() that works with mypyc.
|
|
886
|
+
|
|
887
|
+
Args:
|
|
888
|
+
obj: Object to check
|
|
889
|
+
attr: Attribute name to look for
|
|
890
|
+
|
|
891
|
+
Returns:
|
|
892
|
+
True if attribute exists, False otherwise
|
|
893
|
+
"""
|
|
894
|
+
try:
|
|
895
|
+
getattr(obj, attr)
|
|
896
|
+
except AttributeError:
|
|
897
|
+
return False
|
|
898
|
+
return True
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
def get_node_this(node: "exp.Expression", default: Optional[Any] = None) -> Any:
|
|
902
|
+
"""Safely get the 'this' attribute from a SQLGlot node.
|
|
903
|
+
|
|
904
|
+
Args:
|
|
905
|
+
node: The SQLGlot expression node
|
|
906
|
+
default: Default value if 'this' attribute doesn't exist
|
|
907
|
+
|
|
908
|
+
Returns:
|
|
909
|
+
The value of node.this or the default value
|
|
910
|
+
"""
|
|
911
|
+
try:
|
|
912
|
+
return node.this
|
|
913
|
+
except AttributeError:
|
|
914
|
+
return default
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def has_this_attribute(node: "exp.Expression") -> bool:
|
|
918
|
+
"""Check if a node has the 'this' attribute without using hasattr().
|
|
919
|
+
|
|
920
|
+
Args:
|
|
921
|
+
node: The SQLGlot expression node
|
|
922
|
+
|
|
923
|
+
Returns:
|
|
924
|
+
True if the node has a 'this' attribute, False otherwise
|
|
925
|
+
"""
|
|
926
|
+
try:
|
|
927
|
+
_ = node.this
|
|
928
|
+
except AttributeError:
|
|
929
|
+
return False
|
|
930
|
+
return True
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
def get_node_expressions(node: "exp.Expression", default: Optional[Any] = None) -> Any:
|
|
934
|
+
"""Safely get the 'expressions' attribute from a SQLGlot node.
|
|
935
|
+
|
|
936
|
+
Args:
|
|
937
|
+
node: The SQLGlot expression node
|
|
938
|
+
default: Default value if 'expressions' attribute doesn't exist
|
|
939
|
+
|
|
940
|
+
Returns:
|
|
941
|
+
The value of node.expressions or the default value
|
|
942
|
+
"""
|
|
943
|
+
try:
|
|
944
|
+
return node.expressions
|
|
945
|
+
except AttributeError:
|
|
946
|
+
return default
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def has_expressions_attribute(node: "exp.Expression") -> bool:
|
|
950
|
+
"""Check if a node has the 'expressions' attribute without using hasattr().
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
node: The SQLGlot expression node
|
|
954
|
+
|
|
955
|
+
Returns:
|
|
956
|
+
True if the node has an 'expressions' attribute, False otherwise
|
|
957
|
+
"""
|
|
958
|
+
try:
|
|
959
|
+
_ = node.expressions
|
|
960
|
+
except AttributeError:
|
|
961
|
+
return False
|
|
962
|
+
return True
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
def get_literal_parent(literal: "exp.Expression", default: Optional[Any] = None) -> Any:
|
|
966
|
+
"""Safely get the 'parent' attribute from a SQLGlot literal.
|
|
967
|
+
|
|
968
|
+
Args:
|
|
969
|
+
literal: The SQLGlot expression
|
|
970
|
+
default: Default value if 'parent' attribute doesn't exist
|
|
971
|
+
|
|
972
|
+
Returns:
|
|
973
|
+
The value of literal.parent or the default value
|
|
974
|
+
"""
|
|
975
|
+
try:
|
|
976
|
+
return literal.parent
|
|
977
|
+
except AttributeError:
|
|
978
|
+
return default
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
def has_parent_attribute(literal: "exp.Expression") -> bool:
|
|
982
|
+
"""Check if a literal has the 'parent' attribute without using hasattr().
|
|
983
|
+
|
|
984
|
+
Args:
|
|
985
|
+
literal: The SQLGlot expression
|
|
986
|
+
|
|
987
|
+
Returns:
|
|
988
|
+
True if the literal has a 'parent' attribute, False otherwise
|
|
989
|
+
"""
|
|
990
|
+
try:
|
|
991
|
+
_ = literal.parent
|
|
992
|
+
except AttributeError:
|
|
993
|
+
return False
|
|
994
|
+
return True
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
def is_string_literal(literal: "exp.Literal") -> bool:
|
|
998
|
+
"""Check if a literal is a string literal without using hasattr().
|
|
999
|
+
|
|
1000
|
+
Args:
|
|
1001
|
+
literal: The SQLGlot Literal expression
|
|
1002
|
+
|
|
1003
|
+
Returns:
|
|
1004
|
+
True if the literal is a string, False otherwise
|
|
1005
|
+
"""
|
|
1006
|
+
try:
|
|
1007
|
+
return bool(literal.is_string)
|
|
1008
|
+
except AttributeError:
|
|
1009
|
+
try:
|
|
1010
|
+
return isinstance(literal.this, str)
|
|
1011
|
+
except AttributeError:
|
|
1012
|
+
return False
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def is_number_literal(literal: "exp.Literal") -> bool:
|
|
1016
|
+
"""Check if a literal is a number literal without using hasattr().
|
|
1017
|
+
|
|
1018
|
+
Args:
|
|
1019
|
+
literal: The SQLGlot Literal expression
|
|
1020
|
+
|
|
1021
|
+
Returns:
|
|
1022
|
+
True if the literal is a number, False otherwise
|
|
1023
|
+
"""
|
|
1024
|
+
try:
|
|
1025
|
+
return bool(literal.is_number)
|
|
1026
|
+
except AttributeError:
|
|
1027
|
+
try:
|
|
1028
|
+
if literal.this is not None:
|
|
1029
|
+
float(str(literal.this))
|
|
1030
|
+
return True
|
|
1031
|
+
except (AttributeError, ValueError, TypeError):
|
|
1032
|
+
pass
|
|
1033
|
+
return False
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
def get_initial_expression(context: Any) -> "Optional[exp.Expression]":
|
|
1037
|
+
"""Safely get initial_expression from context.
|
|
1038
|
+
|
|
1039
|
+
Args:
|
|
1040
|
+
context: SQL processing context
|
|
1041
|
+
|
|
1042
|
+
Returns:
|
|
1043
|
+
The initial expression or None if not available
|
|
1044
|
+
"""
|
|
1045
|
+
try:
|
|
1046
|
+
return context.initial_expression # type: ignore[no-any-return]
|
|
1047
|
+
except AttributeError:
|
|
1048
|
+
return None
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
def expression_has_limit(expr: "Optional[exp.Expression]") -> bool:
|
|
1052
|
+
"""Check if an expression has a limit clause.
|
|
1053
|
+
|
|
1054
|
+
Args:
|
|
1055
|
+
expr: SQLGlot expression to check
|
|
1056
|
+
|
|
1057
|
+
Returns:
|
|
1058
|
+
True if expression has limit in args, False otherwise
|
|
1059
|
+
"""
|
|
1060
|
+
if expr is None:
|
|
1061
|
+
return False
|
|
1062
|
+
try:
|
|
1063
|
+
return "limit" in expr.args
|
|
1064
|
+
except AttributeError:
|
|
1065
|
+
return False
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def get_value_attribute(obj: Any) -> Any:
|
|
1069
|
+
"""Safely get the 'value' attribute from an object.
|
|
1070
|
+
|
|
1071
|
+
Args:
|
|
1072
|
+
obj: Object to get value from
|
|
1073
|
+
|
|
1074
|
+
Returns:
|
|
1075
|
+
The value attribute or the object itself if no value attribute
|
|
1076
|
+
"""
|
|
1077
|
+
try:
|
|
1078
|
+
return obj.value
|
|
1079
|
+
except AttributeError:
|
|
1080
|
+
return obj
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
def get_param_style_and_name(param: Any) -> "tuple[Optional[str], Optional[str]]":
|
|
1084
|
+
"""Safely get style and name attributes from a parameter.
|
|
1085
|
+
|
|
1086
|
+
Args:
|
|
1087
|
+
param: Parameter object
|
|
1088
|
+
|
|
1089
|
+
Returns:
|
|
1090
|
+
Tuple of (style, name) or (None, None) if attributes don't exist
|
|
1091
|
+
"""
|
|
1092
|
+
try:
|
|
1093
|
+
style = param.style
|
|
1094
|
+
name = param.name
|
|
1095
|
+
except AttributeError:
|
|
1096
|
+
return None, None
|
|
1097
|
+
return style, name
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
def is_copy_statement(expression: Any) -> "TypeGuard[exp.Expression]":
|
|
1101
|
+
"""Check if the SQL expression is a PostgreSQL COPY statement.
|
|
1102
|
+
|
|
1103
|
+
Args:
|
|
1104
|
+
expression: The SQL expression to check
|
|
1105
|
+
|
|
1106
|
+
Returns:
|
|
1107
|
+
True if this is a COPY statement, False otherwise
|
|
1108
|
+
"""
|
|
1109
|
+
from sqlglot import exp
|
|
1110
|
+
|
|
1111
|
+
if expression is None:
|
|
1112
|
+
return False
|
|
1113
|
+
|
|
1114
|
+
if has_attr(exp, "Copy") and isinstance(expression, getattr(exp, "Copy", type(None))):
|
|
1115
|
+
return True
|
|
1116
|
+
|
|
1117
|
+
if isinstance(expression, (exp.Command, exp.Anonymous)):
|
|
1118
|
+
sql_text = str(expression).strip().upper()
|
|
1119
|
+
return sql_text.startswith("COPY ")
|
|
1120
|
+
|
|
1121
|
+
return False
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
def is_typed_parameter(obj: Any) -> "TypeGuard[Any]":
|
|
1125
|
+
"""Check if an object is a typed parameter.
|
|
1126
|
+
|
|
1127
|
+
Args:
|
|
1128
|
+
obj: The object to check
|
|
1129
|
+
|
|
1130
|
+
Returns:
|
|
1131
|
+
True if the object is a TypedParameter, False otherwise
|
|
1132
|
+
"""
|
|
1133
|
+
from sqlspec.core.parameters import TypedParameter
|
|
1134
|
+
|
|
1135
|
+
return isinstance(obj, TypedParameter)
|