sqlspec 0.16.2__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.

Files changed (148) hide show
  1. 51ff5a9eadfdefd49f98__mypyc.cpython-39-aarch64-linux-gnu.so +0 -0
  2. sqlspec/__init__.py +92 -0
  3. sqlspec/__main__.py +12 -0
  4. sqlspec/__metadata__.py +14 -0
  5. sqlspec/_serialization.py +77 -0
  6. sqlspec/_sql.py +1782 -0
  7. sqlspec/_typing.py +680 -0
  8. sqlspec/adapters/__init__.py +0 -0
  9. sqlspec/adapters/adbc/__init__.py +5 -0
  10. sqlspec/adapters/adbc/_types.py +12 -0
  11. sqlspec/adapters/adbc/config.py +361 -0
  12. sqlspec/adapters/adbc/driver.py +512 -0
  13. sqlspec/adapters/aiosqlite/__init__.py +19 -0
  14. sqlspec/adapters/aiosqlite/_types.py +13 -0
  15. sqlspec/adapters/aiosqlite/config.py +253 -0
  16. sqlspec/adapters/aiosqlite/driver.py +248 -0
  17. sqlspec/adapters/asyncmy/__init__.py +19 -0
  18. sqlspec/adapters/asyncmy/_types.py +12 -0
  19. sqlspec/adapters/asyncmy/config.py +180 -0
  20. sqlspec/adapters/asyncmy/driver.py +274 -0
  21. sqlspec/adapters/asyncpg/__init__.py +21 -0
  22. sqlspec/adapters/asyncpg/_types.py +17 -0
  23. sqlspec/adapters/asyncpg/config.py +229 -0
  24. sqlspec/adapters/asyncpg/driver.py +344 -0
  25. sqlspec/adapters/bigquery/__init__.py +18 -0
  26. sqlspec/adapters/bigquery/_types.py +12 -0
  27. sqlspec/adapters/bigquery/config.py +298 -0
  28. sqlspec/adapters/bigquery/driver.py +558 -0
  29. sqlspec/adapters/duckdb/__init__.py +22 -0
  30. sqlspec/adapters/duckdb/_types.py +12 -0
  31. sqlspec/adapters/duckdb/config.py +504 -0
  32. sqlspec/adapters/duckdb/driver.py +368 -0
  33. sqlspec/adapters/oracledb/__init__.py +32 -0
  34. sqlspec/adapters/oracledb/_types.py +14 -0
  35. sqlspec/adapters/oracledb/config.py +317 -0
  36. sqlspec/adapters/oracledb/driver.py +538 -0
  37. sqlspec/adapters/psqlpy/__init__.py +16 -0
  38. sqlspec/adapters/psqlpy/_types.py +11 -0
  39. sqlspec/adapters/psqlpy/config.py +214 -0
  40. sqlspec/adapters/psqlpy/driver.py +530 -0
  41. sqlspec/adapters/psycopg/__init__.py +32 -0
  42. sqlspec/adapters/psycopg/_types.py +17 -0
  43. sqlspec/adapters/psycopg/config.py +426 -0
  44. sqlspec/adapters/psycopg/driver.py +796 -0
  45. sqlspec/adapters/sqlite/__init__.py +15 -0
  46. sqlspec/adapters/sqlite/_types.py +11 -0
  47. sqlspec/adapters/sqlite/config.py +240 -0
  48. sqlspec/adapters/sqlite/driver.py +294 -0
  49. sqlspec/base.py +571 -0
  50. sqlspec/builder/__init__.py +62 -0
  51. sqlspec/builder/_base.py +473 -0
  52. sqlspec/builder/_column.py +320 -0
  53. sqlspec/builder/_ddl.py +1346 -0
  54. sqlspec/builder/_ddl_utils.py +103 -0
  55. sqlspec/builder/_delete.py +76 -0
  56. sqlspec/builder/_insert.py +421 -0
  57. sqlspec/builder/_merge.py +71 -0
  58. sqlspec/builder/_parsing_utils.py +164 -0
  59. sqlspec/builder/_select.py +170 -0
  60. sqlspec/builder/_update.py +188 -0
  61. sqlspec/builder/mixins/__init__.py +55 -0
  62. sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
  63. sqlspec/builder/mixins/_delete_operations.py +41 -0
  64. sqlspec/builder/mixins/_insert_operations.py +244 -0
  65. sqlspec/builder/mixins/_join_operations.py +149 -0
  66. sqlspec/builder/mixins/_merge_operations.py +562 -0
  67. sqlspec/builder/mixins/_order_limit_operations.py +135 -0
  68. sqlspec/builder/mixins/_pivot_operations.py +153 -0
  69. sqlspec/builder/mixins/_select_operations.py +604 -0
  70. sqlspec/builder/mixins/_update_operations.py +202 -0
  71. sqlspec/builder/mixins/_where_clause.py +644 -0
  72. sqlspec/cli.py +247 -0
  73. sqlspec/config.py +395 -0
  74. sqlspec/core/__init__.py +63 -0
  75. sqlspec/core/cache.cpython-39-aarch64-linux-gnu.so +0 -0
  76. sqlspec/core/cache.py +871 -0
  77. sqlspec/core/compiler.cpython-39-aarch64-linux-gnu.so +0 -0
  78. sqlspec/core/compiler.py +417 -0
  79. sqlspec/core/filters.cpython-39-aarch64-linux-gnu.so +0 -0
  80. sqlspec/core/filters.py +830 -0
  81. sqlspec/core/hashing.cpython-39-aarch64-linux-gnu.so +0 -0
  82. sqlspec/core/hashing.py +310 -0
  83. sqlspec/core/parameters.cpython-39-aarch64-linux-gnu.so +0 -0
  84. sqlspec/core/parameters.py +1237 -0
  85. sqlspec/core/result.cpython-39-aarch64-linux-gnu.so +0 -0
  86. sqlspec/core/result.py +677 -0
  87. sqlspec/core/splitter.cpython-39-aarch64-linux-gnu.so +0 -0
  88. sqlspec/core/splitter.py +819 -0
  89. sqlspec/core/statement.cpython-39-aarch64-linux-gnu.so +0 -0
  90. sqlspec/core/statement.py +676 -0
  91. sqlspec/driver/__init__.py +19 -0
  92. sqlspec/driver/_async.py +502 -0
  93. sqlspec/driver/_common.py +631 -0
  94. sqlspec/driver/_sync.py +503 -0
  95. sqlspec/driver/mixins/__init__.py +6 -0
  96. sqlspec/driver/mixins/_result_tools.py +193 -0
  97. sqlspec/driver/mixins/_sql_translator.py +86 -0
  98. sqlspec/exceptions.py +193 -0
  99. sqlspec/extensions/__init__.py +0 -0
  100. sqlspec/extensions/aiosql/__init__.py +10 -0
  101. sqlspec/extensions/aiosql/adapter.py +461 -0
  102. sqlspec/extensions/litestar/__init__.py +6 -0
  103. sqlspec/extensions/litestar/_utils.py +52 -0
  104. sqlspec/extensions/litestar/cli.py +48 -0
  105. sqlspec/extensions/litestar/config.py +92 -0
  106. sqlspec/extensions/litestar/handlers.py +260 -0
  107. sqlspec/extensions/litestar/plugin.py +145 -0
  108. sqlspec/extensions/litestar/providers.py +454 -0
  109. sqlspec/loader.cpython-39-aarch64-linux-gnu.so +0 -0
  110. sqlspec/loader.py +760 -0
  111. sqlspec/migrations/__init__.py +35 -0
  112. sqlspec/migrations/base.py +414 -0
  113. sqlspec/migrations/commands.py +443 -0
  114. sqlspec/migrations/loaders.py +402 -0
  115. sqlspec/migrations/runner.py +213 -0
  116. sqlspec/migrations/tracker.py +140 -0
  117. sqlspec/migrations/utils.py +129 -0
  118. sqlspec/protocols.py +407 -0
  119. sqlspec/py.typed +0 -0
  120. sqlspec/storage/__init__.py +23 -0
  121. sqlspec/storage/backends/__init__.py +0 -0
  122. sqlspec/storage/backends/base.py +163 -0
  123. sqlspec/storage/backends/fsspec.py +386 -0
  124. sqlspec/storage/backends/obstore.py +459 -0
  125. sqlspec/storage/capabilities.py +102 -0
  126. sqlspec/storage/registry.py +239 -0
  127. sqlspec/typing.py +299 -0
  128. sqlspec/utils/__init__.py +3 -0
  129. sqlspec/utils/correlation.py +150 -0
  130. sqlspec/utils/deprecation.py +106 -0
  131. sqlspec/utils/fixtures.cpython-39-aarch64-linux-gnu.so +0 -0
  132. sqlspec/utils/fixtures.py +58 -0
  133. sqlspec/utils/logging.py +127 -0
  134. sqlspec/utils/module_loader.py +89 -0
  135. sqlspec/utils/serializers.py +4 -0
  136. sqlspec/utils/singleton.py +32 -0
  137. sqlspec/utils/sync_tools.cpython-39-aarch64-linux-gnu.so +0 -0
  138. sqlspec/utils/sync_tools.py +237 -0
  139. sqlspec/utils/text.cpython-39-aarch64-linux-gnu.so +0 -0
  140. sqlspec/utils/text.py +96 -0
  141. sqlspec/utils/type_guards.cpython-39-aarch64-linux-gnu.so +0 -0
  142. sqlspec/utils/type_guards.py +1139 -0
  143. sqlspec-0.16.2.dist-info/METADATA +365 -0
  144. sqlspec-0.16.2.dist-info/RECORD +148 -0
  145. sqlspec-0.16.2.dist-info/WHEEL +7 -0
  146. sqlspec-0.16.2.dist-info/entry_points.txt +2 -0
  147. sqlspec-0.16.2.dist-info/licenses/LICENSE +21 -0
  148. sqlspec-0.16.2.dist-info/licenses/NOTICE +29 -0
@@ -0,0 +1,1139 @@
1
+ """Type guard functions for runtime type checking in SQLSpec.
2
+
3
+ This module provides type-safe runtime checks that help the type checker
4
+ understand type narrowing, replacing defensive hasattr() and duck typing patterns.
5
+ """
6
+
7
+ from collections.abc import Sequence
8
+ from collections.abc import Set as AbstractSet
9
+ from typing import TYPE_CHECKING, Any, Optional, Union, cast
10
+
11
+ from sqlspec.typing import (
12
+ ATTRS_INSTALLED,
13
+ LITESTAR_INSTALLED,
14
+ MSGSPEC_INSTALLED,
15
+ PYDANTIC_INSTALLED,
16
+ BaseModel,
17
+ DataclassProtocol,
18
+ DTOData,
19
+ Struct,
20
+ attrs_asdict,
21
+ attrs_has,
22
+ )
23
+
24
+ if TYPE_CHECKING:
25
+ from dataclasses import Field
26
+
27
+ from sqlglot import exp
28
+ from typing_extensions import TypeGuard
29
+
30
+ from sqlspec._typing import AttrsInstanceStub, BaseModelStub, DTODataStub, StructStub
31
+ from sqlspec.builder import Select
32
+ from sqlspec.core.filters import LimitOffsetFilter, StatementFilter
33
+ from sqlspec.protocols import (
34
+ BytesConvertibleProtocol,
35
+ DictProtocol,
36
+ FilterAppenderProtocol,
37
+ FilterParameterProtocol,
38
+ HasExpressionsProtocol,
39
+ HasLimitProtocol,
40
+ HasOffsetProtocol,
41
+ HasOrderByProtocol,
42
+ HasSQLMethodProtocol,
43
+ HasWhereProtocol,
44
+ IndexableRow,
45
+ ObjectStoreItemProtocol,
46
+ ParameterValueProtocol,
47
+ SQLBuilderProtocol,
48
+ WithMethodProtocol,
49
+ )
50
+ from sqlspec.typing import SupportedSchemaModel
51
+
52
+ __all__ = (
53
+ "can_append_to_statement",
54
+ "can_convert_to_schema",
55
+ "can_extract_parameters",
56
+ "dataclass_to_dict",
57
+ "expression_has_limit",
58
+ "extract_dataclass_fields",
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",
67
+ "has_bytes_conversion",
68
+ "has_dict_attribute",
69
+ "has_expression_attr",
70
+ "has_expressions",
71
+ "has_expressions_attribute",
72
+ "has_parameter_builder",
73
+ "has_parameter_value",
74
+ "has_parent_attribute",
75
+ "has_query_builder_parameters",
76
+ "has_sql_method",
77
+ "has_sqlglot_expression",
78
+ "has_this_attribute",
79
+ "has_to_statement",
80
+ "has_with_method",
81
+ "is_attrs_instance",
82
+ "is_attrs_instance_with_field",
83
+ "is_attrs_instance_without_field",
84
+ "is_attrs_schema",
85
+ "is_copy_statement",
86
+ "is_dataclass",
87
+ "is_dataclass_instance",
88
+ "is_dataclass_with_field",
89
+ "is_dataclass_without_field",
90
+ "is_dict",
91
+ "is_dict_row",
92
+ "is_dict_with_field",
93
+ "is_dict_without_field",
94
+ "is_dto_data",
95
+ "is_expression",
96
+ "is_indexable_row",
97
+ "is_iterable_parameters",
98
+ "is_limit_offset_filter",
99
+ "is_msgspec_struct",
100
+ "is_msgspec_struct_with_field",
101
+ "is_msgspec_struct_without_field",
102
+ "is_number_literal",
103
+ "is_object_store_item",
104
+ "is_pydantic_model",
105
+ "is_pydantic_model_with_field",
106
+ "is_pydantic_model_without_field",
107
+ "is_schema",
108
+ "is_schema_or_dict",
109
+ "is_schema_or_dict_with_field",
110
+ "is_schema_or_dict_without_field",
111
+ "is_schema_with_field",
112
+ "is_schema_without_field",
113
+ "is_select_builder",
114
+ "is_statement_filter",
115
+ "is_string_literal",
116
+ "is_typed_parameter",
117
+ "schema_dump",
118
+ "supports_limit",
119
+ "supports_offset",
120
+ "supports_order_by",
121
+ "supports_where",
122
+ )
123
+
124
+
125
+ def is_statement_filter(obj: Any) -> "TypeGuard[StatementFilter]":
126
+ """Check if an object implements the StatementFilter protocol.
127
+
128
+ Args:
129
+ obj: The object to check
130
+
131
+ Returns:
132
+ True if the object is a StatementFilter, False otherwise
133
+ """
134
+ from sqlspec.core.filters import StatementFilter as FilterProtocol
135
+
136
+ return isinstance(obj, FilterProtocol)
137
+
138
+
139
+ def is_limit_offset_filter(obj: Any) -> "TypeGuard[LimitOffsetFilter]":
140
+ """Check if an object is a LimitOffsetFilter.
141
+
142
+ Args:
143
+ obj: The object to check
144
+
145
+ Returns:
146
+ True if the object is a LimitOffsetFilter, False otherwise
147
+ """
148
+ from sqlspec.core.filters import LimitOffsetFilter
149
+
150
+ return isinstance(obj, LimitOffsetFilter)
151
+
152
+
153
+ def is_select_builder(obj: Any) -> "TypeGuard[Select]":
154
+ """Check if an object is a Select.
155
+
156
+ Args:
157
+ obj: The object to check
158
+
159
+ Returns:
160
+ True if the object is a Select, False otherwise
161
+ """
162
+ from sqlspec.builder import Select
163
+
164
+ return isinstance(obj, Select)
165
+
166
+
167
+ def is_dict_row(row: Any) -> "TypeGuard[dict[str, Any]]":
168
+ """Check if a row is a dictionary.
169
+
170
+ Args:
171
+ row: The row to check
172
+
173
+ Returns:
174
+ True if the row is a dictionary, False otherwise
175
+ """
176
+ return isinstance(row, dict)
177
+
178
+
179
+ def is_indexable_row(row: Any) -> "TypeGuard[IndexableRow]":
180
+ """Check if a row supports index access via protocol.
181
+
182
+ Args:
183
+ row: The row to check
184
+
185
+ Returns:
186
+ True if the row is indexable, False otherwise
187
+ """
188
+ from sqlspec.protocols import IndexableRow
189
+
190
+ return isinstance(row, IndexableRow)
191
+
192
+
193
+ def is_iterable_parameters(parameters: Any) -> "TypeGuard[Sequence[Any]]":
194
+ """Check if parameters are iterable (but not string or dict).
195
+
196
+ Args:
197
+ parameters: The parameters to check
198
+
199
+ Returns:
200
+ True if the parameters are iterable, False otherwise
201
+ """
202
+ return isinstance(parameters, Sequence) and not isinstance(parameters, (str, bytes, dict))
203
+
204
+
205
+ def has_with_method(obj: Any) -> "TypeGuard[WithMethodProtocol]":
206
+ """Check if an object has a callable 'with_' method.
207
+
208
+ This is a more specific check than hasattr for SQLGlot expressions.
209
+
210
+ Args:
211
+ obj: The object to check
212
+
213
+ Returns:
214
+ True if the object has a callable with_ method, False otherwise
215
+ """
216
+ from sqlspec.protocols import WithMethodProtocol
217
+
218
+ return isinstance(obj, WithMethodProtocol)
219
+
220
+
221
+ def can_convert_to_schema(obj: Any) -> "TypeGuard[Any]":
222
+ """Check if an object has the ToSchemaMixin capabilities.
223
+
224
+ This provides better DX than isinstance checks for driver mixins.
225
+
226
+ Args:
227
+ obj: The object to check (typically a driver instance)
228
+
229
+ Returns:
230
+ True if the object has to_schema method, False otherwise
231
+ """
232
+ from sqlspec.driver.mixins import ToSchemaMixin
233
+
234
+ return isinstance(obj, ToSchemaMixin)
235
+
236
+
237
+ def is_dataclass_instance(obj: Any) -> "TypeGuard[DataclassProtocol]":
238
+ """Check if an object is a dataclass instance.
239
+
240
+ Args:
241
+ obj: An object to check.
242
+
243
+ Returns:
244
+ True if the object is a dataclass instance.
245
+ """
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
+
255
+
256
+ def is_dataclass(obj: Any) -> "TypeGuard[DataclassProtocol]":
257
+ """Check if an object is a dataclass.
258
+
259
+ Args:
260
+ obj: Value to check.
261
+
262
+ Returns:
263
+ bool
264
+ """
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
272
+ return is_dataclass_instance(obj)
273
+
274
+
275
+ def is_dataclass_with_field(obj: Any, field_name: str) -> "TypeGuard[object]":
276
+ """Check if an object is a dataclass and has a specific field.
277
+
278
+ Args:
279
+ obj: Value to check.
280
+ field_name: Field name to check for.
281
+
282
+ Returns:
283
+ bool
284
+ """
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
293
+
294
+
295
+ def is_dataclass_without_field(obj: Any, field_name: str) -> "TypeGuard[object]":
296
+ """Check if an object is a dataclass and does not have a specific field.
297
+
298
+ Args:
299
+ obj: Value to check.
300
+ field_name: Field name to check for.
301
+
302
+ Returns:
303
+ bool
304
+ """
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
313
+
314
+
315
+ def is_pydantic_model(obj: Any) -> "TypeGuard[Any]":
316
+ """Check if a value is a pydantic model class or instance.
317
+
318
+ Args:
319
+ obj: Value to check.
320
+
321
+ Returns:
322
+ bool
323
+ """
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)
332
+
333
+
334
+ def is_pydantic_model_with_field(obj: Any, field_name: str) -> "TypeGuard[BaseModelStub]":
335
+ """Check if a pydantic model has a specific field.
336
+
337
+ Args:
338
+ obj: Value to check.
339
+ field_name: Field name to check for.
340
+
341
+ Returns:
342
+ bool
343
+ """
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
352
+
353
+
354
+ def is_pydantic_model_without_field(obj: Any, field_name: str) -> "TypeGuard[BaseModelStub]":
355
+ """Check if a pydantic model does not have a specific field.
356
+
357
+ Args:
358
+ obj: Value to check.
359
+ field_name: Field name to check for.
360
+
361
+ Returns:
362
+ bool
363
+ """
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
372
+
373
+
374
+ def is_msgspec_struct(obj: Any) -> "TypeGuard[StructStub]":
375
+ """Check if a value is a msgspec struct class or instance.
376
+
377
+ Args:
378
+ obj: Value to check.
379
+
380
+ Returns:
381
+ bool
382
+ """
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)
391
+
392
+
393
+ def is_msgspec_struct_with_field(obj: Any, field_name: str) -> "TypeGuard[StructStub]":
394
+ """Check if a msgspec struct has a specific field.
395
+
396
+ Args:
397
+ obj: Value to check.
398
+ field_name: Field name to check for.
399
+
400
+ Returns:
401
+ bool
402
+ """
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
411
+
412
+
413
+ def is_msgspec_struct_without_field(obj: Any, field_name: str) -> "TypeGuard[StructStub]":
414
+ """Check if a msgspec struct does not have a specific field.
415
+
416
+ Args:
417
+ obj: Value to check.
418
+ field_name: Field name to check for.
419
+
420
+ Returns:
421
+ bool
422
+ """
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)
480
+
481
+
482
+ def is_dict(obj: Any) -> "TypeGuard[dict[str, Any]]":
483
+ """Check if a value is a dictionary.
484
+
485
+ Args:
486
+ obj: Value to check.
487
+
488
+ Returns:
489
+ bool
490
+ """
491
+ return isinstance(obj, dict)
492
+
493
+
494
+ def is_dict_with_field(obj: Any, field_name: str) -> "TypeGuard[dict[str, Any]]":
495
+ """Check if a dictionary has a specific field.
496
+
497
+ Args:
498
+ obj: Value to check.
499
+ field_name: Field name to check for.
500
+
501
+ Returns:
502
+ bool
503
+ """
504
+ return is_dict(obj) and field_name in obj
505
+
506
+
507
+ def is_dict_without_field(obj: Any, field_name: str) -> "TypeGuard[dict[str, Any]]":
508
+ """Check if a dictionary does not have a specific field.
509
+
510
+ Args:
511
+ obj: Value to check.
512
+ field_name: Field name to check for.
513
+
514
+ Returns:
515
+ bool
516
+ """
517
+ return is_dict(obj) and field_name not in obj
518
+
519
+
520
+ def is_schema(obj: Any) -> "TypeGuard[SupportedSchemaModel]":
521
+ """Check if a value is a msgspec Struct, Pydantic model, attrs instance, or schema class.
522
+
523
+ Args:
524
+ obj: Value to check.
525
+
526
+ Returns:
527
+ bool
528
+ """
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
+ )
536
+
537
+
538
+ def is_schema_or_dict(obj: Any) -> "TypeGuard[Union[SupportedSchemaModel, dict[str, Any]]]":
539
+ """Check if a value is a msgspec Struct, Pydantic model, or dict.
540
+
541
+ Args:
542
+ obj: Value to check.
543
+
544
+ Returns:
545
+ bool
546
+ """
547
+ return is_schema(obj) or is_dict(obj)
548
+
549
+
550
+ def is_schema_with_field(obj: Any, field_name: str) -> "TypeGuard[SupportedSchemaModel]":
551
+ """Check if a value is a msgspec Struct or Pydantic model with a specific field.
552
+
553
+ Args:
554
+ obj: Value to check.
555
+ field_name: Field name to check for.
556
+
557
+ Returns:
558
+ bool
559
+ """
560
+ return is_msgspec_struct_with_field(obj, field_name) or is_pydantic_model_with_field(obj, field_name)
561
+
562
+
563
+ def is_schema_without_field(obj: Any, field_name: str) -> "TypeGuard[SupportedSchemaModel]":
564
+ """Check if a value is a msgspec Struct or Pydantic model without a specific field.
565
+
566
+ Args:
567
+ obj: Value to check.
568
+ field_name: Field name to check for.
569
+
570
+ Returns:
571
+ bool
572
+ """
573
+ return not is_schema_with_field(obj, field_name)
574
+
575
+
576
+ def is_schema_or_dict_with_field(obj: Any, field_name: str) -> "TypeGuard[Union[SupportedSchemaModel, dict[str, Any]]]":
577
+ """Check if a value is a msgspec Struct, Pydantic model, or dict with a specific field.
578
+
579
+ Args:
580
+ obj: Value to check.
581
+ field_name: Field name to check for.
582
+
583
+ Returns:
584
+ bool
585
+ """
586
+ return is_schema_with_field(obj, field_name) or is_dict_with_field(obj, field_name)
587
+
588
+
589
+ def is_schema_or_dict_without_field(
590
+ obj: Any, field_name: str
591
+ ) -> "TypeGuard[Union[SupportedSchemaModel, dict[str, Any]]]":
592
+ """Check if a value is a msgspec Struct, Pydantic model, or dict without a specific field.
593
+
594
+ Args:
595
+ obj: Value to check.
596
+ field_name: Field name to check for.
597
+
598
+ Returns:
599
+ bool
600
+ """
601
+ return not is_schema_or_dict_with_field(obj, field_name)
602
+
603
+
604
+ def is_dto_data(v: Any) -> "TypeGuard[DTODataStub[Any]]":
605
+ """Check if a value is a Litestar DTOData object.
606
+
607
+ Args:
608
+ v: Value to check.
609
+
610
+ Returns:
611
+ bool
612
+ """
613
+ return LITESTAR_INSTALLED and isinstance(v, DTOData)
614
+
615
+
616
+ def is_expression(obj: Any) -> "TypeGuard[exp.Expression]":
617
+ """Check if a value is a sqlglot Expression.
618
+
619
+ Args:
620
+ obj: Value to check.
621
+
622
+ Returns:
623
+ bool
624
+ """
625
+ from sqlglot import exp
626
+
627
+ return isinstance(obj, exp.Expression)
628
+
629
+
630
+ def has_dict_attribute(obj: Any) -> "TypeGuard[DictProtocol]":
631
+ """Check if an object has a __dict__ attribute.
632
+
633
+ Args:
634
+ obj: Value to check.
635
+
636
+ Returns:
637
+ bool
638
+ """
639
+ from sqlspec.protocols import DictProtocol
640
+
641
+ return isinstance(obj, DictProtocol)
642
+
643
+
644
+ def extract_dataclass_fields(
645
+ obj: "DataclassProtocol",
646
+ exclude_none: bool = False,
647
+ exclude_empty: bool = False,
648
+ include: "Optional[AbstractSet[str]]" = None,
649
+ exclude: "Optional[AbstractSet[str]]" = None,
650
+ ) -> "tuple[Field[Any], ...]":
651
+ """Extract dataclass fields.
652
+
653
+ Args:
654
+ obj: A dataclass instance.
655
+ exclude_none: Whether to exclude None values.
656
+ exclude_empty: Whether to exclude Empty values.
657
+ include: An iterable of fields to include.
658
+ exclude: An iterable of fields to exclude.
659
+
660
+ Raises:
661
+ ValueError: If there are fields that are both included and excluded.
662
+
663
+ Returns:
664
+ A tuple of dataclass fields.
665
+ """
666
+ from dataclasses import Field, fields
667
+
668
+ from sqlspec._typing import Empty
669
+
670
+ include = include or set()
671
+ exclude = exclude or set()
672
+
673
+ if common := (include & exclude):
674
+ msg = f"Fields {common} are both included and excluded."
675
+ raise ValueError(msg)
676
+
677
+ dataclass_fields: list[Field[Any]] = list(fields(obj))
678
+ if exclude_none:
679
+ dataclass_fields = [field for field in dataclass_fields if getattr(obj, field.name) is not None]
680
+ if exclude_empty:
681
+ dataclass_fields = [field for field in dataclass_fields if getattr(obj, field.name) is not Empty]
682
+ if include:
683
+ dataclass_fields = [field for field in dataclass_fields if field.name in include]
684
+ if exclude:
685
+ dataclass_fields = [field for field in dataclass_fields if field.name not in exclude]
686
+
687
+ return tuple(dataclass_fields)
688
+
689
+
690
+ def extract_dataclass_items(
691
+ obj: "DataclassProtocol",
692
+ exclude_none: bool = False,
693
+ exclude_empty: bool = False,
694
+ include: "Optional[AbstractSet[str]]" = None,
695
+ exclude: "Optional[AbstractSet[str]]" = None,
696
+ ) -> "tuple[tuple[str, Any], ...]":
697
+ """Extract name-value pairs from a dataclass instance.
698
+
699
+ Args:
700
+ obj: A dataclass instance.
701
+ exclude_none: Whether to exclude None values.
702
+ exclude_empty: Whether to exclude Empty values.
703
+ include: An iterable of fields to include.
704
+ exclude: An iterable of fields to exclude.
705
+
706
+ Returns:
707
+ A tuple of key/value pairs.
708
+ """
709
+ dataclass_fields = extract_dataclass_fields(obj, exclude_none, exclude_empty, include, exclude)
710
+ return tuple((field.name, getattr(obj, field.name)) for field in dataclass_fields)
711
+
712
+
713
+ def dataclass_to_dict(
714
+ obj: "DataclassProtocol",
715
+ exclude_none: bool = False,
716
+ exclude_empty: bool = False,
717
+ convert_nested: bool = True,
718
+ exclude: "Optional[AbstractSet[str]]" = None,
719
+ ) -> "dict[str, Any]":
720
+ """Convert a dataclass instance to a dictionary.
721
+
722
+ Args:
723
+ obj: A dataclass instance.
724
+ exclude_none: Whether to exclude None values.
725
+ exclude_empty: Whether to exclude Empty values.
726
+ convert_nested: Whether to recursively convert nested dataclasses.
727
+ exclude: An iterable of fields to exclude.
728
+
729
+ Returns:
730
+ A dictionary of key/value pairs.
731
+ """
732
+ ret = {}
733
+ for field in extract_dataclass_fields(obj, exclude_none, exclude_empty, exclude=exclude):
734
+ value = getattr(obj, field.name)
735
+ if is_dataclass_instance(value) and convert_nested:
736
+ ret[field.name] = dataclass_to_dict(value, exclude_none, exclude_empty)
737
+ else:
738
+ ret[field.name] = getattr(obj, field.name)
739
+ return cast("dict[str, Any]", ret)
740
+
741
+
742
+ def schema_dump(data: Any, exclude_unset: bool = True) -> "dict[str, Any]":
743
+ """Dump a data object to a dictionary.
744
+
745
+ Args:
746
+ data: :type:`dict[str, Any]` | :class:`DataclassProtocol` | :class:`msgspec.Struct` | :class:`pydantic.BaseModel` | :class:`AttrsInstance`
747
+ exclude_unset: :type:`bool` Whether to exclude unset values.
748
+
749
+ Returns:
750
+ :type:`dict[str, Any]`
751
+ """
752
+ from sqlspec._typing import UNSET
753
+
754
+ if is_dict(data):
755
+ return data
756
+ if is_dataclass(data):
757
+ return dataclass_to_dict(data, exclude_empty=exclude_unset)
758
+ if is_pydantic_model(data):
759
+ return data.model_dump(exclude_unset=exclude_unset) # type: ignore[no-any-return]
760
+ if is_msgspec_struct(data):
761
+ if exclude_unset:
762
+ return {f: val for f in data.__struct_fields__ if (val := getattr(data, f, None)) != UNSET}
763
+ return {f: getattr(data, f, None) for f in data.__struct_fields__}
764
+ if is_attrs_instance(data):
765
+ return attrs_asdict(data)
766
+
767
+ if has_dict_attribute(data):
768
+ return data.__dict__
769
+ return cast("dict[str, Any]", data)
770
+
771
+
772
+ def can_extract_parameters(obj: Any) -> "TypeGuard[FilterParameterProtocol]":
773
+ """Check if an object can extract parameters."""
774
+ from sqlspec.protocols import FilterParameterProtocol
775
+
776
+ return isinstance(obj, FilterParameterProtocol)
777
+
778
+
779
+ def can_append_to_statement(obj: Any) -> "TypeGuard[FilterAppenderProtocol]":
780
+ """Check if an object can append to SQL statements."""
781
+ from sqlspec.protocols import FilterAppenderProtocol
782
+
783
+ return isinstance(obj, FilterAppenderProtocol)
784
+
785
+
786
+ def has_parameter_value(obj: Any) -> "TypeGuard[ParameterValueProtocol]":
787
+ """Check if an object has a value attribute (parameter wrapper)."""
788
+ from sqlspec.protocols import ParameterValueProtocol
789
+
790
+ return isinstance(obj, ParameterValueProtocol)
791
+
792
+
793
+ def supports_where(obj: Any) -> "TypeGuard[HasWhereProtocol]":
794
+ """Check if an SQL expression supports WHERE clauses."""
795
+ from sqlspec.protocols import HasWhereProtocol
796
+
797
+ return isinstance(obj, HasWhereProtocol)
798
+
799
+
800
+ def supports_limit(obj: Any) -> "TypeGuard[HasLimitProtocol]":
801
+ """Check if an SQL expression supports LIMIT clauses."""
802
+ from sqlspec.protocols import HasLimitProtocol
803
+
804
+ return isinstance(obj, HasLimitProtocol)
805
+
806
+
807
+ def supports_offset(obj: Any) -> "TypeGuard[HasOffsetProtocol]":
808
+ """Check if an SQL expression supports OFFSET clauses."""
809
+ from sqlspec.protocols import HasOffsetProtocol
810
+
811
+ return isinstance(obj, HasOffsetProtocol)
812
+
813
+
814
+ def supports_order_by(obj: Any) -> "TypeGuard[HasOrderByProtocol]":
815
+ """Check if an SQL expression supports ORDER BY clauses."""
816
+ from sqlspec.protocols import HasOrderByProtocol
817
+
818
+ return isinstance(obj, HasOrderByProtocol)
819
+
820
+
821
+ def has_bytes_conversion(obj: Any) -> "TypeGuard[BytesConvertibleProtocol]":
822
+ """Check if an object can be converted to bytes."""
823
+ from sqlspec.protocols import BytesConvertibleProtocol
824
+
825
+ return isinstance(obj, BytesConvertibleProtocol)
826
+
827
+
828
+ def has_expressions(obj: Any) -> "TypeGuard[HasExpressionsProtocol]":
829
+ """Check if an object has an expressions attribute."""
830
+ from sqlspec.protocols import HasExpressionsProtocol
831
+
832
+ return isinstance(obj, HasExpressionsProtocol)
833
+
834
+
835
+ def has_sql_method(obj: Any) -> "TypeGuard[HasSQLMethodProtocol]":
836
+ """Check if an object has a sql() method for rendering SQL."""
837
+ from sqlspec.protocols import HasSQLMethodProtocol
838
+
839
+ return isinstance(obj, HasSQLMethodProtocol)
840
+
841
+
842
+ def has_query_builder_parameters(obj: Any) -> "TypeGuard[SQLBuilderProtocol]":
843
+ """Check if an object is a query builder with parameters property."""
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
+ )
851
+
852
+
853
+ def is_object_store_item(obj: Any) -> "TypeGuard[ObjectStoreItemProtocol]":
854
+ """Check if an object is an object store item with path/key attributes."""
855
+ from sqlspec.protocols import ObjectStoreItemProtocol
856
+
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)