sqlspec 0.13.1__py3-none-any.whl → 0.16.2__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 +71 -8
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +930 -136
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +116 -285
- sqlspec/adapters/adbc/driver.py +462 -340
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +202 -150
- sqlspec/adapters/aiosqlite/driver.py +226 -247
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -199
- sqlspec/adapters/asyncmy/driver.py +257 -215
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +81 -214
- sqlspec/adapters/asyncpg/driver.py +284 -359
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -299
- sqlspec/adapters/bigquery/driver.py +474 -634
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +414 -397
- sqlspec/adapters/duckdb/driver.py +342 -393
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -458
- sqlspec/adapters/oracledb/driver.py +505 -531
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -307
- sqlspec/adapters/psqlpy/driver.py +504 -213
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -472
- sqlspec/adapters/psycopg/driver.py +704 -825
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +208 -142
- sqlspec/adapters/sqlite/driver.py +263 -278
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder/base.py → builder/_base.py} +184 -86
- sqlspec/{statement/builder/column.py → builder/_column.py} +97 -60
- sqlspec/{statement/builder/ddl.py → builder/_ddl.py} +61 -131
- sqlspec/{statement/builder → builder}/_ddl_utils.py +4 -10
- sqlspec/{statement/builder/delete.py → builder/_delete.py} +10 -30
- sqlspec/builder/_insert.py +421 -0
- sqlspec/builder/_merge.py +71 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +49 -26
- sqlspec/builder/_select.py +170 -0
- sqlspec/{statement/builder/update.py → builder/_update.py} +16 -20
- sqlspec/builder/mixins/__init__.py +55 -0
- sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
- sqlspec/{statement/builder/mixins/_delete_from.py → builder/mixins/_delete_operations.py} +8 -1
- sqlspec/builder/mixins/_insert_operations.py +244 -0
- sqlspec/{statement/builder/mixins/_join.py → builder/mixins/_join_operations.py} +45 -13
- sqlspec/{statement/builder/mixins/_merge_clauses.py → builder/mixins/_merge_operations.py} +188 -30
- sqlspec/builder/mixins/_order_limit_operations.py +135 -0
- sqlspec/builder/mixins/_pivot_operations.py +153 -0
- sqlspec/builder/mixins/_select_operations.py +604 -0
- sqlspec/builder/mixins/_update_operations.py +202 -0
- sqlspec/builder/mixins/_where_clause.py +644 -0
- sqlspec/cli.py +247 -0
- sqlspec/config.py +183 -138
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +871 -0
- sqlspec/core/compiler.py +417 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1237 -0
- sqlspec/core/result.py +677 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +676 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +422 -163
- sqlspec/driver/_common.py +545 -287
- sqlspec/driver/_sync.py +426 -160
- sqlspec/driver/mixins/__init__.py +2 -13
- sqlspec/driver/mixins/_result_tools.py +193 -0
- sqlspec/driver/mixins/_sql_translator.py +65 -14
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/__init__.py +2 -1
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +21 -16
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +423 -104
- 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 +51 -186
- 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 -2
- 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 +17 -38
- sqlspec/utils/text.py +12 -51
- sqlspec/utils/type_guards.py +482 -235
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/METADATA +7 -2
- sqlspec-0.16.2.dist-info/RECORD +134 -0
- sqlspec-0.16.2.dist-info/entry_points.txt +2 -0
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -512
- sqlspec/driver/mixins/_result_utils.py +0 -140
- sqlspec/driver/mixins/_storage.py +0 -926
- sqlspec/driver/mixins/_type_coercion.py +0 -130
- sqlspec/driver/parameters.py +0 -138
- sqlspec/service/__init__.py +0 -4
- sqlspec/service/_util.py +0 -147
- sqlspec/service/base.py +0 -1131
- sqlspec/service/pagination.py +0 -26
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/insert.py +0 -288
- sqlspec/statement/builder/merge.py +0 -95
- sqlspec/statement/builder/mixins/__init__.py +0 -65
- sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
- sqlspec/statement/builder/mixins/_case_builder.py +0 -91
- sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
- sqlspec/statement/builder/mixins/_from.py +0 -63
- sqlspec/statement/builder/mixins/_group_by.py +0 -118
- sqlspec/statement/builder/mixins/_having.py +0 -35
- sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
- sqlspec/statement/builder/mixins/_insert_into.py +0 -36
- sqlspec/statement/builder/mixins/_insert_values.py +0 -67
- sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
- sqlspec/statement/builder/mixins/_order_by.py +0 -46
- sqlspec/statement/builder/mixins/_pivot.py +0 -79
- sqlspec/statement/builder/mixins/_returning.py +0 -37
- sqlspec/statement/builder/mixins/_select_columns.py +0 -61
- sqlspec/statement/builder/mixins/_set_ops.py +0 -122
- sqlspec/statement/builder/mixins/_unpivot.py +0 -77
- sqlspec/statement/builder/mixins/_update_from.py +0 -55
- sqlspec/statement/builder/mixins/_update_set.py +0 -94
- sqlspec/statement/builder/mixins/_update_table.py +0 -29
- sqlspec/statement/builder/mixins/_where.py +0 -401
- sqlspec/statement/builder/mixins/_window_functions.py +0 -86
- sqlspec/statement/builder/select.py +0 -221
- sqlspec/statement/filters.py +0 -596
- sqlspec/statement/parameter_manager.py +0 -220
- sqlspec/statement/parameters.py +0 -867
- 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 -718
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1704
- sqlspec/statement/sql_compiler.py +0 -140
- sqlspec/utils/cached_property.py +0 -25
- sqlspec-0.13.1.dist-info/RECORD +0 -150
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/WHEEL +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.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,21 +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",
|
|
69
|
+
"has_expression_attr",
|
|
72
70
|
"has_expressions",
|
|
71
|
+
"has_expressions_attribute",
|
|
72
|
+
"has_parameter_builder",
|
|
73
73
|
"has_parameter_value",
|
|
74
|
+
"has_parent_attribute",
|
|
74
75
|
"has_query_builder_parameters",
|
|
75
|
-
"has_risk_level",
|
|
76
76
|
"has_sql_method",
|
|
77
|
+
"has_sqlglot_expression",
|
|
78
|
+
"has_this_attribute",
|
|
79
|
+
"has_to_statement",
|
|
77
80
|
"has_with_method",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
81
|
+
"is_attrs_instance",
|
|
82
|
+
"is_attrs_instance_with_field",
|
|
83
|
+
"is_attrs_instance_without_field",
|
|
84
|
+
"is_attrs_schema",
|
|
85
|
+
"is_copy_statement",
|
|
83
86
|
"is_dataclass",
|
|
84
87
|
"is_dataclass_instance",
|
|
85
88
|
"is_dataclass_with_field",
|
|
@@ -96,6 +99,7 @@ __all__ = (
|
|
|
96
99
|
"is_msgspec_struct",
|
|
97
100
|
"is_msgspec_struct_with_field",
|
|
98
101
|
"is_msgspec_struct_without_field",
|
|
102
|
+
"is_number_literal",
|
|
99
103
|
"is_object_store_item",
|
|
100
104
|
"is_pydantic_model",
|
|
101
105
|
"is_pydantic_model_with_field",
|
|
@@ -108,11 +112,8 @@ __all__ = (
|
|
|
108
112
|
"is_schema_without_field",
|
|
109
113
|
"is_select_builder",
|
|
110
114
|
"is_statement_filter",
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"is_sync_pipeline_capable_driver",
|
|
114
|
-
"is_sync_transaction_capable",
|
|
115
|
-
"is_sync_transaction_state_capable",
|
|
115
|
+
"is_string_literal",
|
|
116
|
+
"is_typed_parameter",
|
|
116
117
|
"schema_dump",
|
|
117
118
|
"supports_limit",
|
|
118
119
|
"supports_offset",
|
|
@@ -130,7 +131,7 @@ def is_statement_filter(obj: Any) -> "TypeGuard[StatementFilter]":
|
|
|
130
131
|
Returns:
|
|
131
132
|
True if the object is a StatementFilter, False otherwise
|
|
132
133
|
"""
|
|
133
|
-
from sqlspec.
|
|
134
|
+
from sqlspec.core.filters import StatementFilter as FilterProtocol
|
|
134
135
|
|
|
135
136
|
return isinstance(obj, FilterProtocol)
|
|
136
137
|
|
|
@@ -144,7 +145,7 @@ def is_limit_offset_filter(obj: Any) -> "TypeGuard[LimitOffsetFilter]":
|
|
|
144
145
|
Returns:
|
|
145
146
|
True if the object is a LimitOffsetFilter, False otherwise
|
|
146
147
|
"""
|
|
147
|
-
from sqlspec.
|
|
148
|
+
from sqlspec.core.filters import LimitOffsetFilter
|
|
148
149
|
|
|
149
150
|
return isinstance(obj, LimitOffsetFilter)
|
|
150
151
|
|
|
@@ -158,7 +159,7 @@ def is_select_builder(obj: Any) -> "TypeGuard[Select]":
|
|
|
158
159
|
Returns:
|
|
159
160
|
True if the object is a Select, False otherwise
|
|
160
161
|
"""
|
|
161
|
-
from sqlspec.
|
|
162
|
+
from sqlspec.builder import Select
|
|
162
163
|
|
|
163
164
|
return isinstance(obj, Select)
|
|
164
165
|
|
|
@@ -189,16 +190,16 @@ def is_indexable_row(row: Any) -> "TypeGuard[IndexableRow]":
|
|
|
189
190
|
return isinstance(row, IndexableRow)
|
|
190
191
|
|
|
191
192
|
|
|
192
|
-
def is_iterable_parameters(
|
|
193
|
+
def is_iterable_parameters(parameters: Any) -> "TypeGuard[Sequence[Any]]":
|
|
193
194
|
"""Check if parameters are iterable (but not string or dict).
|
|
194
195
|
|
|
195
196
|
Args:
|
|
196
|
-
|
|
197
|
+
parameters: The parameters to check
|
|
197
198
|
|
|
198
199
|
Returns:
|
|
199
200
|
True if the parameters are iterable, False otherwise
|
|
200
201
|
"""
|
|
201
|
-
return isinstance(
|
|
202
|
+
return isinstance(parameters, Sequence) and not isinstance(parameters, (str, bytes, dict))
|
|
202
203
|
|
|
203
204
|
|
|
204
205
|
def has_with_method(obj: Any) -> "TypeGuard[WithMethodProtocol]":
|
|
@@ -233,9 +234,6 @@ def can_convert_to_schema(obj: Any) -> "TypeGuard[Any]":
|
|
|
233
234
|
return isinstance(obj, ToSchemaMixin)
|
|
234
235
|
|
|
235
236
|
|
|
236
|
-
# Type guards migrated from typing.py
|
|
237
|
-
|
|
238
|
-
|
|
239
237
|
def is_dataclass_instance(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
240
238
|
"""Check if an object is a dataclass instance.
|
|
241
239
|
|
|
@@ -245,8 +243,14 @@ def is_dataclass_instance(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
|
245
243
|
Returns:
|
|
246
244
|
True if the object is a dataclass instance.
|
|
247
245
|
"""
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
250
254
|
|
|
251
255
|
|
|
252
256
|
def is_dataclass(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
@@ -258,8 +262,13 @@ def is_dataclass(obj: Any) -> "TypeGuard[DataclassProtocol]":
|
|
|
258
262
|
Returns:
|
|
259
263
|
bool
|
|
260
264
|
"""
|
|
261
|
-
if isinstance(obj, type)
|
|
262
|
-
|
|
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
|
|
263
272
|
return is_dataclass_instance(obj)
|
|
264
273
|
|
|
265
274
|
|
|
@@ -273,7 +282,14 @@ def is_dataclass_with_field(obj: Any, field_name: str) -> "TypeGuard[object]":
|
|
|
273
282
|
Returns:
|
|
274
283
|
bool
|
|
275
284
|
"""
|
|
276
|
-
|
|
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
|
|
277
293
|
|
|
278
294
|
|
|
279
295
|
def is_dataclass_without_field(obj: Any, field_name: str) -> "TypeGuard[object]":
|
|
@@ -286,11 +302,18 @@ def is_dataclass_without_field(obj: Any, field_name: str) -> "TypeGuard[object]"
|
|
|
286
302
|
Returns:
|
|
287
303
|
bool
|
|
288
304
|
"""
|
|
289
|
-
|
|
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
|
|
290
313
|
|
|
291
314
|
|
|
292
|
-
def is_pydantic_model(obj: Any) -> "TypeGuard[
|
|
293
|
-
"""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.
|
|
294
317
|
|
|
295
318
|
Args:
|
|
296
319
|
obj: Value to check.
|
|
@@ -298,10 +321,17 @@ def is_pydantic_model(obj: Any) -> "TypeGuard[BaseModel]":
|
|
|
298
321
|
Returns:
|
|
299
322
|
bool
|
|
300
323
|
"""
|
|
301
|
-
|
|
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)
|
|
302
332
|
|
|
303
333
|
|
|
304
|
-
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]":
|
|
305
335
|
"""Check if a pydantic model has a specific field.
|
|
306
336
|
|
|
307
337
|
Args:
|
|
@@ -311,10 +341,17 @@ def is_pydantic_model_with_field(obj: Any, field_name: str) -> "TypeGuard[BaseMo
|
|
|
311
341
|
Returns:
|
|
312
342
|
bool
|
|
313
343
|
"""
|
|
314
|
-
|
|
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
|
|
315
352
|
|
|
316
353
|
|
|
317
|
-
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]":
|
|
318
355
|
"""Check if a pydantic model does not have a specific field.
|
|
319
356
|
|
|
320
357
|
Args:
|
|
@@ -324,11 +361,18 @@ def is_pydantic_model_without_field(obj: Any, field_name: str) -> "TypeGuard[Bas
|
|
|
324
361
|
Returns:
|
|
325
362
|
bool
|
|
326
363
|
"""
|
|
327
|
-
|
|
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
|
|
328
372
|
|
|
329
373
|
|
|
330
|
-
def is_msgspec_struct(obj: Any) -> "TypeGuard[
|
|
331
|
-
"""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.
|
|
332
376
|
|
|
333
377
|
Args:
|
|
334
378
|
obj: Value to check.
|
|
@@ -336,10 +380,17 @@ def is_msgspec_struct(obj: Any) -> "TypeGuard[Struct]":
|
|
|
336
380
|
Returns:
|
|
337
381
|
bool
|
|
338
382
|
"""
|
|
339
|
-
|
|
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)
|
|
340
391
|
|
|
341
392
|
|
|
342
|
-
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]":
|
|
343
394
|
"""Check if a msgspec struct has a specific field.
|
|
344
395
|
|
|
345
396
|
Args:
|
|
@@ -349,10 +400,17 @@ def is_msgspec_struct_with_field(obj: Any, field_name: str) -> "TypeGuard[Struct
|
|
|
349
400
|
Returns:
|
|
350
401
|
bool
|
|
351
402
|
"""
|
|
352
|
-
|
|
403
|
+
if not is_msgspec_struct(obj):
|
|
404
|
+
return False
|
|
405
|
+
try:
|
|
406
|
+
_ = getattr(obj, field_name)
|
|
407
|
+
|
|
408
|
+
except AttributeError:
|
|
409
|
+
return False
|
|
410
|
+
return True
|
|
353
411
|
|
|
354
412
|
|
|
355
|
-
def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[
|
|
413
|
+
def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[StructStub]":
|
|
356
414
|
"""Check if a msgspec struct does not have a specific field.
|
|
357
415
|
|
|
358
416
|
Args:
|
|
@@ -362,7 +420,63 @@ def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[Str
|
|
|
362
420
|
Returns:
|
|
363
421
|
bool
|
|
364
422
|
"""
|
|
365
|
-
|
|
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)
|
|
366
480
|
|
|
367
481
|
|
|
368
482
|
def is_dict(obj: Any) -> "TypeGuard[dict[str, Any]]":
|
|
@@ -404,7 +518,7 @@ def is_dict_without_field(obj: Any, field_name: str) -> "TypeGuard[dict[str, Any
|
|
|
404
518
|
|
|
405
519
|
|
|
406
520
|
def is_schema(obj: Any) -> "TypeGuard[SupportedSchemaModel]":
|
|
407
|
-
"""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.
|
|
408
522
|
|
|
409
523
|
Args:
|
|
410
524
|
obj: Value to check.
|
|
@@ -412,7 +526,13 @@ def is_schema(obj: Any) -> "TypeGuard[SupportedSchemaModel]":
|
|
|
412
526
|
Returns:
|
|
413
527
|
bool
|
|
414
528
|
"""
|
|
415
|
-
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
|
+
)
|
|
416
536
|
|
|
417
537
|
|
|
418
538
|
def is_schema_or_dict(obj: Any) -> "TypeGuard[Union[SupportedSchemaModel, dict[str, Any]]]":
|
|
@@ -481,7 +601,7 @@ def is_schema_or_dict_without_field(
|
|
|
481
601
|
return not is_schema_or_dict_with_field(obj, field_name)
|
|
482
602
|
|
|
483
603
|
|
|
484
|
-
def is_dto_data(v: Any) -> "TypeGuard[
|
|
604
|
+
def is_dto_data(v: Any) -> "TypeGuard[DTODataStub[Any]]":
|
|
485
605
|
"""Check if a value is a Litestar DTOData object.
|
|
486
606
|
|
|
487
607
|
Args:
|
|
@@ -521,149 +641,6 @@ def has_dict_attribute(obj: Any) -> "TypeGuard[DictProtocol]":
|
|
|
521
641
|
return isinstance(obj, DictProtocol)
|
|
522
642
|
|
|
523
643
|
|
|
524
|
-
def is_sync_transaction_capable(obj: Any) -> "TypeGuard[SyncTransactionCapableConnectionProtocol]":
|
|
525
|
-
"""Check if a connection supports sync transactions.
|
|
526
|
-
|
|
527
|
-
Args:
|
|
528
|
-
obj: Connection object to check.
|
|
529
|
-
|
|
530
|
-
Returns:
|
|
531
|
-
True if the connection has commit and rollback methods.
|
|
532
|
-
"""
|
|
533
|
-
from sqlspec.protocols import SyncTransactionCapableConnectionProtocol
|
|
534
|
-
|
|
535
|
-
return isinstance(obj, SyncTransactionCapableConnectionProtocol)
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
def is_async_transaction_capable(obj: Any) -> "TypeGuard[AsyncTransactionCapableConnectionProtocol]":
|
|
539
|
-
"""Check if a connection supports async transactions.
|
|
540
|
-
|
|
541
|
-
Args:
|
|
542
|
-
obj: Connection object to check.
|
|
543
|
-
|
|
544
|
-
Returns:
|
|
545
|
-
True if the connection has async commit and rollback methods.
|
|
546
|
-
"""
|
|
547
|
-
from sqlspec.protocols import AsyncTransactionCapableConnectionProtocol
|
|
548
|
-
|
|
549
|
-
return isinstance(obj, AsyncTransactionCapableConnectionProtocol)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
def is_sync_transaction_state_capable(obj: Any) -> "TypeGuard[SyncTransactionStateConnectionProtocol]":
|
|
553
|
-
"""Check if a connection can report sync transaction state.
|
|
554
|
-
|
|
555
|
-
Args:
|
|
556
|
-
obj: Connection object to check.
|
|
557
|
-
|
|
558
|
-
Returns:
|
|
559
|
-
True if the connection has in_transaction and begin methods.
|
|
560
|
-
"""
|
|
561
|
-
from sqlspec.protocols import SyncTransactionStateConnectionProtocol
|
|
562
|
-
|
|
563
|
-
return isinstance(obj, SyncTransactionStateConnectionProtocol)
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
def is_async_transaction_state_capable(obj: Any) -> "TypeGuard[AsyncTransactionStateConnectionProtocol]":
|
|
567
|
-
"""Check if a connection can report async transaction state.
|
|
568
|
-
|
|
569
|
-
Args:
|
|
570
|
-
obj: Connection object to check.
|
|
571
|
-
|
|
572
|
-
Returns:
|
|
573
|
-
True if the connection has in_transaction and async begin methods.
|
|
574
|
-
"""
|
|
575
|
-
from sqlspec.protocols import AsyncTransactionStateConnectionProtocol
|
|
576
|
-
|
|
577
|
-
return isinstance(obj, AsyncTransactionStateConnectionProtocol)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
def is_sync_closeable_connection(obj: Any) -> "TypeGuard[SyncCloseableConnectionProtocol]":
|
|
581
|
-
"""Check if a connection can be closed synchronously.
|
|
582
|
-
|
|
583
|
-
Args:
|
|
584
|
-
obj: Connection object to check.
|
|
585
|
-
|
|
586
|
-
Returns:
|
|
587
|
-
True if the connection has a close method.
|
|
588
|
-
"""
|
|
589
|
-
from sqlspec.protocols import SyncCloseableConnectionProtocol
|
|
590
|
-
|
|
591
|
-
return isinstance(obj, SyncCloseableConnectionProtocol)
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
def is_async_closeable_connection(obj: Any) -> "TypeGuard[AsyncCloseableConnectionProtocol]":
|
|
595
|
-
"""Check if a connection can be closed asynchronously.
|
|
596
|
-
|
|
597
|
-
Args:
|
|
598
|
-
obj: Connection object to check.
|
|
599
|
-
|
|
600
|
-
Returns:
|
|
601
|
-
True if the connection has an async close method.
|
|
602
|
-
"""
|
|
603
|
-
from sqlspec.protocols import AsyncCloseableConnectionProtocol
|
|
604
|
-
|
|
605
|
-
return isinstance(obj, AsyncCloseableConnectionProtocol)
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
def is_sync_copy_capable(obj: Any) -> "TypeGuard[SyncCopyCapableConnectionProtocol]":
|
|
609
|
-
"""Check if a connection supports sync COPY operations.
|
|
610
|
-
|
|
611
|
-
Args:
|
|
612
|
-
obj: Connection object to check.
|
|
613
|
-
|
|
614
|
-
Returns:
|
|
615
|
-
True if the connection has copy_from and copy_to methods.
|
|
616
|
-
"""
|
|
617
|
-
from sqlspec.protocols import SyncCopyCapableConnectionProtocol
|
|
618
|
-
|
|
619
|
-
return isinstance(obj, SyncCopyCapableConnectionProtocol)
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
def is_async_copy_capable(obj: Any) -> "TypeGuard[AsyncCopyCapableConnectionProtocol]":
|
|
623
|
-
"""Check if a connection supports async COPY operations.
|
|
624
|
-
|
|
625
|
-
Args:
|
|
626
|
-
obj: Connection object to check.
|
|
627
|
-
|
|
628
|
-
Returns:
|
|
629
|
-
True if the connection has async copy_from and copy_to methods.
|
|
630
|
-
"""
|
|
631
|
-
from sqlspec.protocols import AsyncCopyCapableConnectionProtocol
|
|
632
|
-
|
|
633
|
-
return isinstance(obj, AsyncCopyCapableConnectionProtocol)
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
def is_sync_pipeline_capable_driver(obj: Any) -> "TypeGuard[SyncPipelineCapableDriverProtocol]":
|
|
637
|
-
"""Check if a driver supports sync native pipeline execution.
|
|
638
|
-
|
|
639
|
-
Args:
|
|
640
|
-
obj: Driver object to check.
|
|
641
|
-
|
|
642
|
-
Returns:
|
|
643
|
-
True if the driver has _execute_pipeline_native method.
|
|
644
|
-
"""
|
|
645
|
-
from sqlspec.protocols import SyncPipelineCapableDriverProtocol
|
|
646
|
-
|
|
647
|
-
return isinstance(obj, SyncPipelineCapableDriverProtocol)
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
def is_async_pipeline_capable_driver(obj: Any) -> "TypeGuard[AsyncPipelineCapableDriverProtocol]":
|
|
651
|
-
"""Check if a driver supports async native pipeline execution.
|
|
652
|
-
|
|
653
|
-
Args:
|
|
654
|
-
obj: Driver object to check.
|
|
655
|
-
|
|
656
|
-
Returns:
|
|
657
|
-
True if the driver has async _execute_pipeline_native method.
|
|
658
|
-
"""
|
|
659
|
-
from sqlspec.protocols import AsyncPipelineCapableDriverProtocol
|
|
660
|
-
|
|
661
|
-
return isinstance(obj, AsyncPipelineCapableDriverProtocol)
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
# Dataclass utility functions
|
|
665
|
-
|
|
666
|
-
|
|
667
644
|
def extract_dataclass_fields(
|
|
668
645
|
obj: "DataclassProtocol",
|
|
669
646
|
exclude_none: bool = False,
|
|
@@ -697,15 +674,15 @@ def extract_dataclass_fields(
|
|
|
697
674
|
msg = f"Fields {common} are both included and excluded."
|
|
698
675
|
raise ValueError(msg)
|
|
699
676
|
|
|
700
|
-
dataclass_fields:
|
|
677
|
+
dataclass_fields: list[Field[Any]] = list(fields(obj))
|
|
701
678
|
if exclude_none:
|
|
702
|
-
dataclass_fields =
|
|
679
|
+
dataclass_fields = [field for field in dataclass_fields if getattr(obj, field.name) is not None]
|
|
703
680
|
if exclude_empty:
|
|
704
|
-
dataclass_fields =
|
|
681
|
+
dataclass_fields = [field for field in dataclass_fields if getattr(obj, field.name) is not Empty]
|
|
705
682
|
if include:
|
|
706
|
-
dataclass_fields =
|
|
683
|
+
dataclass_fields = [field for field in dataclass_fields if field.name in include]
|
|
707
684
|
if exclude:
|
|
708
|
-
dataclass_fields =
|
|
685
|
+
dataclass_fields = [field for field in dataclass_fields if field.name not in exclude]
|
|
709
686
|
|
|
710
687
|
return tuple(dataclass_fields)
|
|
711
688
|
|
|
@@ -717,9 +694,7 @@ def extract_dataclass_items(
|
|
|
717
694
|
include: "Optional[AbstractSet[str]]" = None,
|
|
718
695
|
exclude: "Optional[AbstractSet[str]]" = None,
|
|
719
696
|
) -> "tuple[tuple[str, Any], ...]":
|
|
720
|
-
"""Extract
|
|
721
|
-
|
|
722
|
-
Unlike the 'asdict' method exports by the stdlib, this function does not pickle values.
|
|
697
|
+
"""Extract name-value pairs from a dataclass instance.
|
|
723
698
|
|
|
724
699
|
Args:
|
|
725
700
|
obj: A dataclass instance.
|
|
@@ -742,11 +717,7 @@ def dataclass_to_dict(
|
|
|
742
717
|
convert_nested: bool = True,
|
|
743
718
|
exclude: "Optional[AbstractSet[str]]" = None,
|
|
744
719
|
) -> "dict[str, Any]":
|
|
745
|
-
"""Convert a dataclass to a dictionary.
|
|
746
|
-
|
|
747
|
-
This method has important differences to the standard library version:
|
|
748
|
-
- it does not deepcopy values
|
|
749
|
-
- it does not recurse into collections
|
|
720
|
+
"""Convert a dataclass instance to a dictionary.
|
|
750
721
|
|
|
751
722
|
Args:
|
|
752
723
|
obj: A dataclass instance.
|
|
@@ -768,13 +739,11 @@ def dataclass_to_dict(
|
|
|
768
739
|
return cast("dict[str, Any]", ret)
|
|
769
740
|
|
|
770
741
|
|
|
771
|
-
def schema_dump(
|
|
772
|
-
data: "Union[dict[str, Any], DataclassProtocol, Struct, BaseModel]", exclude_unset: bool = True
|
|
773
|
-
) -> "dict[str, Any]":
|
|
742
|
+
def schema_dump(data: Any, exclude_unset: bool = True) -> "dict[str, Any]":
|
|
774
743
|
"""Dump a data object to a dictionary.
|
|
775
744
|
|
|
776
745
|
Args:
|
|
777
|
-
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`
|
|
778
747
|
exclude_unset: :type:`bool` Whether to exclude unset values.
|
|
779
748
|
|
|
780
749
|
Returns:
|
|
@@ -787,20 +756,19 @@ def schema_dump(
|
|
|
787
756
|
if is_dataclass(data):
|
|
788
757
|
return dataclass_to_dict(data, exclude_empty=exclude_unset)
|
|
789
758
|
if is_pydantic_model(data):
|
|
790
|
-
return data.model_dump(exclude_unset=exclude_unset)
|
|
759
|
+
return data.model_dump(exclude_unset=exclude_unset) # type: ignore[no-any-return]
|
|
791
760
|
if is_msgspec_struct(data):
|
|
792
761
|
if exclude_unset:
|
|
793
762
|
return {f: val for f in data.__struct_fields__ if (val := getattr(data, f, None)) != UNSET}
|
|
794
763
|
return {f: getattr(data, f, None) for f in data.__struct_fields__}
|
|
764
|
+
if is_attrs_instance(data):
|
|
765
|
+
return attrs_asdict(data)
|
|
795
766
|
|
|
796
767
|
if has_dict_attribute(data):
|
|
797
768
|
return data.__dict__
|
|
798
769
|
return cast("dict[str, Any]", data)
|
|
799
770
|
|
|
800
771
|
|
|
801
|
-
# New type guards for hasattr() pattern replacement
|
|
802
|
-
|
|
803
|
-
|
|
804
772
|
def can_extract_parameters(obj: Any) -> "TypeGuard[FilterParameterProtocol]":
|
|
805
773
|
"""Check if an object can extract parameters."""
|
|
806
774
|
from sqlspec.protocols import FilterParameterProtocol
|
|
@@ -822,13 +790,6 @@ def has_parameter_value(obj: Any) -> "TypeGuard[ParameterValueProtocol]":
|
|
|
822
790
|
return isinstance(obj, ParameterValueProtocol)
|
|
823
791
|
|
|
824
792
|
|
|
825
|
-
def has_risk_level(obj: Any) -> "TypeGuard[HasRiskLevelProtocol]":
|
|
826
|
-
"""Check if an object has a risk_level attribute."""
|
|
827
|
-
from sqlspec.protocols import HasRiskLevelProtocol
|
|
828
|
-
|
|
829
|
-
return isinstance(obj, HasRiskLevelProtocol)
|
|
830
|
-
|
|
831
|
-
|
|
832
793
|
def supports_where(obj: Any) -> "TypeGuard[HasWhereProtocol]":
|
|
833
794
|
"""Check if an SQL expression supports WHERE clauses."""
|
|
834
795
|
from sqlspec.protocols import HasWhereProtocol
|
|
@@ -880,9 +841,13 @@ def has_sql_method(obj: Any) -> "TypeGuard[HasSQLMethodProtocol]":
|
|
|
880
841
|
|
|
881
842
|
def has_query_builder_parameters(obj: Any) -> "TypeGuard[SQLBuilderProtocol]":
|
|
882
843
|
"""Check if an object is a query builder with parameters property."""
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
844
|
+
return (
|
|
845
|
+
hasattr(obj, "build")
|
|
846
|
+
and callable(getattr(obj, "build", None))
|
|
847
|
+
and hasattr(obj, "parameters")
|
|
848
|
+
and hasattr(obj, "add_parameter")
|
|
849
|
+
and callable(getattr(obj, "add_parameter", None))
|
|
850
|
+
)
|
|
886
851
|
|
|
887
852
|
|
|
888
853
|
def is_object_store_item(obj: Any) -> "TypeGuard[ObjectStoreItemProtocol]":
|
|
@@ -890,3 +855,285 @@ def is_object_store_item(obj: Any) -> "TypeGuard[ObjectStoreItemProtocol]":
|
|
|
890
855
|
from sqlspec.protocols import ObjectStoreItemProtocol
|
|
891
856
|
|
|
892
857
|
return isinstance(obj, ObjectStoreItemProtocol)
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def has_sqlglot_expression(obj: Any) -> "TypeGuard[Any]":
|
|
861
|
+
"""Check if an object has a sqlglot_expression property."""
|
|
862
|
+
from sqlspec.protocols import HasSQLGlotExpressionProtocol
|
|
863
|
+
|
|
864
|
+
return isinstance(obj, HasSQLGlotExpressionProtocol)
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def has_parameter_builder(obj: Any) -> "TypeGuard[Any]":
|
|
868
|
+
"""Check if an object has an add_parameter method."""
|
|
869
|
+
from sqlspec.protocols import HasParameterBuilderProtocol
|
|
870
|
+
|
|
871
|
+
return isinstance(obj, HasParameterBuilderProtocol)
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def has_expression_attr(obj: Any) -> "TypeGuard[Any]":
|
|
875
|
+
"""Check if an object has an _expression attribute."""
|
|
876
|
+
from sqlspec.protocols import HasExpressionProtocol
|
|
877
|
+
|
|
878
|
+
return isinstance(obj, HasExpressionProtocol)
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def has_to_statement(obj: Any) -> "TypeGuard[Any]":
|
|
882
|
+
"""Check if an object has a to_statement method."""
|
|
883
|
+
from sqlspec.protocols import HasToStatementProtocol
|
|
884
|
+
|
|
885
|
+
return isinstance(obj, HasToStatementProtocol)
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
def has_attr(obj: Any, attr: str) -> bool:
|
|
889
|
+
"""Safe replacement for hasattr() that works with mypyc.
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
obj: Object to check
|
|
893
|
+
attr: Attribute name to look for
|
|
894
|
+
|
|
895
|
+
Returns:
|
|
896
|
+
True if attribute exists, False otherwise
|
|
897
|
+
"""
|
|
898
|
+
try:
|
|
899
|
+
getattr(obj, attr)
|
|
900
|
+
except AttributeError:
|
|
901
|
+
return False
|
|
902
|
+
return True
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def get_node_this(node: "exp.Expression", default: Optional[Any] = None) -> Any:
|
|
906
|
+
"""Safely get the 'this' attribute from a SQLGlot node.
|
|
907
|
+
|
|
908
|
+
Args:
|
|
909
|
+
node: The SQLGlot expression node
|
|
910
|
+
default: Default value if 'this' attribute doesn't exist
|
|
911
|
+
|
|
912
|
+
Returns:
|
|
913
|
+
The value of node.this or the default value
|
|
914
|
+
"""
|
|
915
|
+
try:
|
|
916
|
+
return node.this
|
|
917
|
+
except AttributeError:
|
|
918
|
+
return default
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
def has_this_attribute(node: "exp.Expression") -> bool:
|
|
922
|
+
"""Check if a node has the 'this' attribute without using hasattr().
|
|
923
|
+
|
|
924
|
+
Args:
|
|
925
|
+
node: The SQLGlot expression node
|
|
926
|
+
|
|
927
|
+
Returns:
|
|
928
|
+
True if the node has a 'this' attribute, False otherwise
|
|
929
|
+
"""
|
|
930
|
+
try:
|
|
931
|
+
_ = node.this
|
|
932
|
+
except AttributeError:
|
|
933
|
+
return False
|
|
934
|
+
return True
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def get_node_expressions(node: "exp.Expression", default: Optional[Any] = None) -> Any:
|
|
938
|
+
"""Safely get the 'expressions' attribute from a SQLGlot node.
|
|
939
|
+
|
|
940
|
+
Args:
|
|
941
|
+
node: The SQLGlot expression node
|
|
942
|
+
default: Default value if 'expressions' attribute doesn't exist
|
|
943
|
+
|
|
944
|
+
Returns:
|
|
945
|
+
The value of node.expressions or the default value
|
|
946
|
+
"""
|
|
947
|
+
try:
|
|
948
|
+
return node.expressions
|
|
949
|
+
except AttributeError:
|
|
950
|
+
return default
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
def has_expressions_attribute(node: "exp.Expression") -> bool:
|
|
954
|
+
"""Check if a node has the 'expressions' attribute without using hasattr().
|
|
955
|
+
|
|
956
|
+
Args:
|
|
957
|
+
node: The SQLGlot expression node
|
|
958
|
+
|
|
959
|
+
Returns:
|
|
960
|
+
True if the node has an 'expressions' attribute, False otherwise
|
|
961
|
+
"""
|
|
962
|
+
try:
|
|
963
|
+
_ = node.expressions
|
|
964
|
+
except AttributeError:
|
|
965
|
+
return False
|
|
966
|
+
return True
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
def get_literal_parent(literal: "exp.Expression", default: Optional[Any] = None) -> Any:
|
|
970
|
+
"""Safely get the 'parent' attribute from a SQLGlot literal.
|
|
971
|
+
|
|
972
|
+
Args:
|
|
973
|
+
literal: The SQLGlot expression
|
|
974
|
+
default: Default value if 'parent' attribute doesn't exist
|
|
975
|
+
|
|
976
|
+
Returns:
|
|
977
|
+
The value of literal.parent or the default value
|
|
978
|
+
"""
|
|
979
|
+
try:
|
|
980
|
+
return literal.parent
|
|
981
|
+
except AttributeError:
|
|
982
|
+
return default
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
def has_parent_attribute(literal: "exp.Expression") -> bool:
|
|
986
|
+
"""Check if a literal has the 'parent' attribute without using hasattr().
|
|
987
|
+
|
|
988
|
+
Args:
|
|
989
|
+
literal: The SQLGlot expression
|
|
990
|
+
|
|
991
|
+
Returns:
|
|
992
|
+
True if the literal has a 'parent' attribute, False otherwise
|
|
993
|
+
"""
|
|
994
|
+
try:
|
|
995
|
+
_ = literal.parent
|
|
996
|
+
except AttributeError:
|
|
997
|
+
return False
|
|
998
|
+
return True
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def is_string_literal(literal: "exp.Literal") -> bool:
|
|
1002
|
+
"""Check if a literal is a string literal without using hasattr().
|
|
1003
|
+
|
|
1004
|
+
Args:
|
|
1005
|
+
literal: The SQLGlot Literal expression
|
|
1006
|
+
|
|
1007
|
+
Returns:
|
|
1008
|
+
True if the literal is a string, False otherwise
|
|
1009
|
+
"""
|
|
1010
|
+
try:
|
|
1011
|
+
return bool(literal.is_string)
|
|
1012
|
+
except AttributeError:
|
|
1013
|
+
try:
|
|
1014
|
+
return isinstance(literal.this, str)
|
|
1015
|
+
except AttributeError:
|
|
1016
|
+
return False
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
def is_number_literal(literal: "exp.Literal") -> bool:
|
|
1020
|
+
"""Check if a literal is a number literal without using hasattr().
|
|
1021
|
+
|
|
1022
|
+
Args:
|
|
1023
|
+
literal: The SQLGlot Literal expression
|
|
1024
|
+
|
|
1025
|
+
Returns:
|
|
1026
|
+
True if the literal is a number, False otherwise
|
|
1027
|
+
"""
|
|
1028
|
+
try:
|
|
1029
|
+
return bool(literal.is_number)
|
|
1030
|
+
except AttributeError:
|
|
1031
|
+
try:
|
|
1032
|
+
if literal.this is not None:
|
|
1033
|
+
float(str(literal.this))
|
|
1034
|
+
return True
|
|
1035
|
+
except (AttributeError, ValueError, TypeError):
|
|
1036
|
+
pass
|
|
1037
|
+
return False
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
def get_initial_expression(context: Any) -> "Optional[exp.Expression]":
|
|
1041
|
+
"""Safely get initial_expression from context.
|
|
1042
|
+
|
|
1043
|
+
Args:
|
|
1044
|
+
context: SQL processing context
|
|
1045
|
+
|
|
1046
|
+
Returns:
|
|
1047
|
+
The initial expression or None if not available
|
|
1048
|
+
"""
|
|
1049
|
+
try:
|
|
1050
|
+
return context.initial_expression # type: ignore[no-any-return]
|
|
1051
|
+
except AttributeError:
|
|
1052
|
+
return None
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
def expression_has_limit(expr: "Optional[exp.Expression]") -> bool:
|
|
1056
|
+
"""Check if an expression has a limit clause.
|
|
1057
|
+
|
|
1058
|
+
Args:
|
|
1059
|
+
expr: SQLGlot expression to check
|
|
1060
|
+
|
|
1061
|
+
Returns:
|
|
1062
|
+
True if expression has limit in args, False otherwise
|
|
1063
|
+
"""
|
|
1064
|
+
if expr is None:
|
|
1065
|
+
return False
|
|
1066
|
+
try:
|
|
1067
|
+
return "limit" in expr.args
|
|
1068
|
+
except AttributeError:
|
|
1069
|
+
return False
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
def get_value_attribute(obj: Any) -> Any:
|
|
1073
|
+
"""Safely get the 'value' attribute from an object.
|
|
1074
|
+
|
|
1075
|
+
Args:
|
|
1076
|
+
obj: Object to get value from
|
|
1077
|
+
|
|
1078
|
+
Returns:
|
|
1079
|
+
The value attribute or the object itself if no value attribute
|
|
1080
|
+
"""
|
|
1081
|
+
try:
|
|
1082
|
+
return obj.value
|
|
1083
|
+
except AttributeError:
|
|
1084
|
+
return obj
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
def get_param_style_and_name(param: Any) -> "tuple[Optional[str], Optional[str]]":
|
|
1088
|
+
"""Safely get style and name attributes from a parameter.
|
|
1089
|
+
|
|
1090
|
+
Args:
|
|
1091
|
+
param: Parameter object
|
|
1092
|
+
|
|
1093
|
+
Returns:
|
|
1094
|
+
Tuple of (style, name) or (None, None) if attributes don't exist
|
|
1095
|
+
"""
|
|
1096
|
+
try:
|
|
1097
|
+
style = param.style
|
|
1098
|
+
name = param.name
|
|
1099
|
+
except AttributeError:
|
|
1100
|
+
return None, None
|
|
1101
|
+
return style, name
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
def is_copy_statement(expression: Any) -> "TypeGuard[exp.Expression]":
|
|
1105
|
+
"""Check if the SQL expression is a PostgreSQL COPY statement.
|
|
1106
|
+
|
|
1107
|
+
Args:
|
|
1108
|
+
expression: The SQL expression to check
|
|
1109
|
+
|
|
1110
|
+
Returns:
|
|
1111
|
+
True if this is a COPY statement, False otherwise
|
|
1112
|
+
"""
|
|
1113
|
+
from sqlglot import exp
|
|
1114
|
+
|
|
1115
|
+
if expression is None:
|
|
1116
|
+
return False
|
|
1117
|
+
|
|
1118
|
+
if has_attr(exp, "Copy") and isinstance(expression, getattr(exp, "Copy", type(None))):
|
|
1119
|
+
return True
|
|
1120
|
+
|
|
1121
|
+
if isinstance(expression, (exp.Command, exp.Anonymous)):
|
|
1122
|
+
sql_text = str(expression).strip().upper()
|
|
1123
|
+
return sql_text.startswith("COPY ")
|
|
1124
|
+
|
|
1125
|
+
return False
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
def is_typed_parameter(obj: Any) -> "TypeGuard[Any]":
|
|
1129
|
+
"""Check if an object is a typed parameter.
|
|
1130
|
+
|
|
1131
|
+
Args:
|
|
1132
|
+
obj: The object to check
|
|
1133
|
+
|
|
1134
|
+
Returns:
|
|
1135
|
+
True if the object is a TypedParameter, False otherwise
|
|
1136
|
+
"""
|
|
1137
|
+
from sqlspec.core.parameters import TypedParameter
|
|
1138
|
+
|
|
1139
|
+
return isinstance(obj, TypedParameter)
|