sqlspec 0.32.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.
Files changed (262) hide show
  1. sqlspec/__init__.py +104 -0
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +14 -0
  4. sqlspec/_serialization.py +312 -0
  5. sqlspec/_typing.py +784 -0
  6. sqlspec/adapters/__init__.py +0 -0
  7. sqlspec/adapters/adbc/__init__.py +5 -0
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  10. sqlspec/adapters/adbc/adk/store.py +880 -0
  11. sqlspec/adapters/adbc/config.py +436 -0
  12. sqlspec/adapters/adbc/data_dictionary.py +537 -0
  13. sqlspec/adapters/adbc/driver.py +841 -0
  14. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  15. sqlspec/adapters/adbc/litestar/store.py +504 -0
  16. sqlspec/adapters/adbc/type_converter.py +153 -0
  17. sqlspec/adapters/aiosqlite/__init__.py +29 -0
  18. sqlspec/adapters/aiosqlite/_types.py +13 -0
  19. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  21. sqlspec/adapters/aiosqlite/config.py +310 -0
  22. sqlspec/adapters/aiosqlite/data_dictionary.py +260 -0
  23. sqlspec/adapters/aiosqlite/driver.py +463 -0
  24. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  25. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  26. sqlspec/adapters/aiosqlite/pool.py +500 -0
  27. sqlspec/adapters/asyncmy/__init__.py +25 -0
  28. sqlspec/adapters/asyncmy/_types.py +12 -0
  29. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  31. sqlspec/adapters/asyncmy/config.py +246 -0
  32. sqlspec/adapters/asyncmy/data_dictionary.py +241 -0
  33. sqlspec/adapters/asyncmy/driver.py +632 -0
  34. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  35. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  36. sqlspec/adapters/asyncpg/__init__.py +23 -0
  37. sqlspec/adapters/asyncpg/_type_handlers.py +76 -0
  38. sqlspec/adapters/asyncpg/_types.py +23 -0
  39. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  41. sqlspec/adapters/asyncpg/config.py +464 -0
  42. sqlspec/adapters/asyncpg/data_dictionary.py +321 -0
  43. sqlspec/adapters/asyncpg/driver.py +720 -0
  44. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  45. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  46. sqlspec/adapters/bigquery/__init__.py +18 -0
  47. sqlspec/adapters/bigquery/_types.py +12 -0
  48. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  49. sqlspec/adapters/bigquery/adk/store.py +585 -0
  50. sqlspec/adapters/bigquery/config.py +298 -0
  51. sqlspec/adapters/bigquery/data_dictionary.py +256 -0
  52. sqlspec/adapters/bigquery/driver.py +1073 -0
  53. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  54. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  55. sqlspec/adapters/bigquery/type_converter.py +125 -0
  56. sqlspec/adapters/duckdb/__init__.py +24 -0
  57. sqlspec/adapters/duckdb/_types.py +12 -0
  58. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  59. sqlspec/adapters/duckdb/adk/store.py +563 -0
  60. sqlspec/adapters/duckdb/config.py +396 -0
  61. sqlspec/adapters/duckdb/data_dictionary.py +264 -0
  62. sqlspec/adapters/duckdb/driver.py +604 -0
  63. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  64. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  65. sqlspec/adapters/duckdb/pool.py +273 -0
  66. sqlspec/adapters/duckdb/type_converter.py +133 -0
  67. sqlspec/adapters/oracledb/__init__.py +32 -0
  68. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  69. sqlspec/adapters/oracledb/_types.py +39 -0
  70. sqlspec/adapters/oracledb/_uuid_handlers.py +130 -0
  71. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  72. sqlspec/adapters/oracledb/adk/store.py +1632 -0
  73. sqlspec/adapters/oracledb/config.py +469 -0
  74. sqlspec/adapters/oracledb/data_dictionary.py +717 -0
  75. sqlspec/adapters/oracledb/driver.py +1493 -0
  76. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  77. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  78. sqlspec/adapters/oracledb/migrations.py +532 -0
  79. sqlspec/adapters/oracledb/type_converter.py +207 -0
  80. sqlspec/adapters/psqlpy/__init__.py +16 -0
  81. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  82. sqlspec/adapters/psqlpy/_types.py +12 -0
  83. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  84. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  85. sqlspec/adapters/psqlpy/config.py +271 -0
  86. sqlspec/adapters/psqlpy/data_dictionary.py +179 -0
  87. sqlspec/adapters/psqlpy/driver.py +892 -0
  88. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  90. sqlspec/adapters/psqlpy/type_converter.py +102 -0
  91. sqlspec/adapters/psycopg/__init__.py +32 -0
  92. sqlspec/adapters/psycopg/_type_handlers.py +90 -0
  93. sqlspec/adapters/psycopg/_types.py +18 -0
  94. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  95. sqlspec/adapters/psycopg/adk/store.py +962 -0
  96. sqlspec/adapters/psycopg/config.py +487 -0
  97. sqlspec/adapters/psycopg/data_dictionary.py +630 -0
  98. sqlspec/adapters/psycopg/driver.py +1336 -0
  99. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  100. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  101. sqlspec/adapters/spanner/__init__.py +38 -0
  102. sqlspec/adapters/spanner/_type_handlers.py +186 -0
  103. sqlspec/adapters/spanner/_types.py +12 -0
  104. sqlspec/adapters/spanner/adk/__init__.py +5 -0
  105. sqlspec/adapters/spanner/adk/store.py +435 -0
  106. sqlspec/adapters/spanner/config.py +241 -0
  107. sqlspec/adapters/spanner/data_dictionary.py +95 -0
  108. sqlspec/adapters/spanner/dialect/__init__.py +6 -0
  109. sqlspec/adapters/spanner/dialect/_spangres.py +52 -0
  110. sqlspec/adapters/spanner/dialect/_spanner.py +123 -0
  111. sqlspec/adapters/spanner/driver.py +366 -0
  112. sqlspec/adapters/spanner/litestar/__init__.py +5 -0
  113. sqlspec/adapters/spanner/litestar/store.py +266 -0
  114. sqlspec/adapters/spanner/type_converter.py +46 -0
  115. sqlspec/adapters/sqlite/__init__.py +18 -0
  116. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  117. sqlspec/adapters/sqlite/_types.py +11 -0
  118. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  119. sqlspec/adapters/sqlite/adk/store.py +582 -0
  120. sqlspec/adapters/sqlite/config.py +221 -0
  121. sqlspec/adapters/sqlite/data_dictionary.py +256 -0
  122. sqlspec/adapters/sqlite/driver.py +527 -0
  123. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  124. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  125. sqlspec/adapters/sqlite/pool.py +140 -0
  126. sqlspec/base.py +811 -0
  127. sqlspec/builder/__init__.py +146 -0
  128. sqlspec/builder/_base.py +900 -0
  129. sqlspec/builder/_column.py +517 -0
  130. sqlspec/builder/_ddl.py +1642 -0
  131. sqlspec/builder/_delete.py +84 -0
  132. sqlspec/builder/_dml.py +381 -0
  133. sqlspec/builder/_expression_wrappers.py +46 -0
  134. sqlspec/builder/_factory.py +1537 -0
  135. sqlspec/builder/_insert.py +315 -0
  136. sqlspec/builder/_join.py +375 -0
  137. sqlspec/builder/_merge.py +848 -0
  138. sqlspec/builder/_parsing_utils.py +297 -0
  139. sqlspec/builder/_select.py +1615 -0
  140. sqlspec/builder/_update.py +161 -0
  141. sqlspec/builder/_vector_expressions.py +259 -0
  142. sqlspec/cli.py +764 -0
  143. sqlspec/config.py +1540 -0
  144. sqlspec/core/__init__.py +305 -0
  145. sqlspec/core/cache.py +785 -0
  146. sqlspec/core/compiler.py +603 -0
  147. sqlspec/core/filters.py +872 -0
  148. sqlspec/core/hashing.py +274 -0
  149. sqlspec/core/metrics.py +83 -0
  150. sqlspec/core/parameters/__init__.py +64 -0
  151. sqlspec/core/parameters/_alignment.py +266 -0
  152. sqlspec/core/parameters/_converter.py +413 -0
  153. sqlspec/core/parameters/_processor.py +341 -0
  154. sqlspec/core/parameters/_registry.py +201 -0
  155. sqlspec/core/parameters/_transformers.py +226 -0
  156. sqlspec/core/parameters/_types.py +430 -0
  157. sqlspec/core/parameters/_validator.py +123 -0
  158. sqlspec/core/pipeline.py +187 -0
  159. sqlspec/core/result.py +1124 -0
  160. sqlspec/core/splitter.py +940 -0
  161. sqlspec/core/stack.py +163 -0
  162. sqlspec/core/statement.py +835 -0
  163. sqlspec/core/type_conversion.py +235 -0
  164. sqlspec/driver/__init__.py +36 -0
  165. sqlspec/driver/_async.py +1027 -0
  166. sqlspec/driver/_common.py +1236 -0
  167. sqlspec/driver/_sync.py +1025 -0
  168. sqlspec/driver/mixins/__init__.py +7 -0
  169. sqlspec/driver/mixins/_result_tools.py +61 -0
  170. sqlspec/driver/mixins/_sql_translator.py +122 -0
  171. sqlspec/driver/mixins/_storage.py +311 -0
  172. sqlspec/exceptions.py +321 -0
  173. sqlspec/extensions/__init__.py +0 -0
  174. sqlspec/extensions/adk/__init__.py +53 -0
  175. sqlspec/extensions/adk/_types.py +51 -0
  176. sqlspec/extensions/adk/converters.py +172 -0
  177. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  178. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  179. sqlspec/extensions/adk/service.py +181 -0
  180. sqlspec/extensions/adk/store.py +536 -0
  181. sqlspec/extensions/aiosql/__init__.py +10 -0
  182. sqlspec/extensions/aiosql/adapter.py +471 -0
  183. sqlspec/extensions/fastapi/__init__.py +19 -0
  184. sqlspec/extensions/fastapi/extension.py +341 -0
  185. sqlspec/extensions/fastapi/providers.py +543 -0
  186. sqlspec/extensions/flask/__init__.py +36 -0
  187. sqlspec/extensions/flask/_state.py +72 -0
  188. sqlspec/extensions/flask/_utils.py +40 -0
  189. sqlspec/extensions/flask/extension.py +402 -0
  190. sqlspec/extensions/litestar/__init__.py +23 -0
  191. sqlspec/extensions/litestar/_utils.py +52 -0
  192. sqlspec/extensions/litestar/cli.py +92 -0
  193. sqlspec/extensions/litestar/config.py +90 -0
  194. sqlspec/extensions/litestar/handlers.py +316 -0
  195. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  196. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  197. sqlspec/extensions/litestar/plugin.py +638 -0
  198. sqlspec/extensions/litestar/providers.py +454 -0
  199. sqlspec/extensions/litestar/store.py +265 -0
  200. sqlspec/extensions/otel/__init__.py +58 -0
  201. sqlspec/extensions/prometheus/__init__.py +107 -0
  202. sqlspec/extensions/starlette/__init__.py +10 -0
  203. sqlspec/extensions/starlette/_state.py +26 -0
  204. sqlspec/extensions/starlette/_utils.py +52 -0
  205. sqlspec/extensions/starlette/extension.py +257 -0
  206. sqlspec/extensions/starlette/middleware.py +154 -0
  207. sqlspec/loader.py +716 -0
  208. sqlspec/migrations/__init__.py +36 -0
  209. sqlspec/migrations/base.py +728 -0
  210. sqlspec/migrations/commands.py +1140 -0
  211. sqlspec/migrations/context.py +142 -0
  212. sqlspec/migrations/fix.py +203 -0
  213. sqlspec/migrations/loaders.py +450 -0
  214. sqlspec/migrations/runner.py +1024 -0
  215. sqlspec/migrations/templates.py +234 -0
  216. sqlspec/migrations/tracker.py +403 -0
  217. sqlspec/migrations/utils.py +256 -0
  218. sqlspec/migrations/validation.py +203 -0
  219. sqlspec/observability/__init__.py +22 -0
  220. sqlspec/observability/_config.py +228 -0
  221. sqlspec/observability/_diagnostics.py +67 -0
  222. sqlspec/observability/_dispatcher.py +151 -0
  223. sqlspec/observability/_observer.py +180 -0
  224. sqlspec/observability/_runtime.py +381 -0
  225. sqlspec/observability/_spans.py +158 -0
  226. sqlspec/protocols.py +530 -0
  227. sqlspec/py.typed +0 -0
  228. sqlspec/storage/__init__.py +46 -0
  229. sqlspec/storage/_utils.py +104 -0
  230. sqlspec/storage/backends/__init__.py +1 -0
  231. sqlspec/storage/backends/base.py +163 -0
  232. sqlspec/storage/backends/fsspec.py +398 -0
  233. sqlspec/storage/backends/local.py +377 -0
  234. sqlspec/storage/backends/obstore.py +580 -0
  235. sqlspec/storage/errors.py +104 -0
  236. sqlspec/storage/pipeline.py +604 -0
  237. sqlspec/storage/registry.py +289 -0
  238. sqlspec/typing.py +219 -0
  239. sqlspec/utils/__init__.py +31 -0
  240. sqlspec/utils/arrow_helpers.py +95 -0
  241. sqlspec/utils/config_resolver.py +153 -0
  242. sqlspec/utils/correlation.py +132 -0
  243. sqlspec/utils/data_transformation.py +114 -0
  244. sqlspec/utils/dependencies.py +79 -0
  245. sqlspec/utils/deprecation.py +113 -0
  246. sqlspec/utils/fixtures.py +250 -0
  247. sqlspec/utils/logging.py +172 -0
  248. sqlspec/utils/module_loader.py +273 -0
  249. sqlspec/utils/portal.py +325 -0
  250. sqlspec/utils/schema.py +288 -0
  251. sqlspec/utils/serializers.py +396 -0
  252. sqlspec/utils/singleton.py +41 -0
  253. sqlspec/utils/sync_tools.py +277 -0
  254. sqlspec/utils/text.py +108 -0
  255. sqlspec/utils/type_converters.py +99 -0
  256. sqlspec/utils/type_guards.py +1324 -0
  257. sqlspec/utils/version.py +444 -0
  258. sqlspec-0.32.0.dist-info/METADATA +202 -0
  259. sqlspec-0.32.0.dist-info/RECORD +262 -0
  260. sqlspec-0.32.0.dist-info/WHEEL +4 -0
  261. sqlspec-0.32.0.dist-info/entry_points.txt +2 -0
  262. sqlspec-0.32.0.dist-info/licenses/LICENSE +21 -0
sqlspec/__init__.py ADDED
@@ -0,0 +1,104 @@
1
+ """SQLSpec: Type-safe SQL query mapper for Python."""
2
+
3
+ from sqlspec import adapters, base, builder, core, driver, exceptions, extensions, loader, migrations, typing, utils
4
+ from sqlspec.__metadata__ import __version__
5
+ from sqlspec.base import SQLSpec
6
+ from sqlspec.builder import (
7
+ Column,
8
+ ColumnExpression,
9
+ CreateTable,
10
+ Delete,
11
+ DropTable,
12
+ FunctionColumn,
13
+ Insert,
14
+ Merge,
15
+ QueryBuilder,
16
+ Select,
17
+ SQLFactory,
18
+ Update,
19
+ sql,
20
+ )
21
+ from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
22
+ from sqlspec.core import (
23
+ SQL,
24
+ ArrowResult,
25
+ CacheConfig,
26
+ CacheStats,
27
+ ParameterConverter,
28
+ ParameterProcessor,
29
+ ParameterStyle,
30
+ ParameterStyleConfig,
31
+ ProcessedState,
32
+ SQLResult,
33
+ StackOperation,
34
+ StackResult,
35
+ Statement,
36
+ StatementConfig,
37
+ StatementStack,
38
+ )
39
+ from sqlspec.core import filters as filters
40
+ from sqlspec.driver import AsyncDriverAdapterBase, ExecutionResult, SyncDriverAdapterBase
41
+ from sqlspec.exceptions import StackExecutionError
42
+ from sqlspec.loader import SQLFile, SQLFileLoader
43
+ from sqlspec.typing import ConnectionT, PoolT, SchemaT, StatementParameters, SupportedSchemaModel
44
+ from sqlspec.utils.logging import suppress_erroneous_sqlglot_log_messages
45
+
46
+ suppress_erroneous_sqlglot_log_messages()
47
+
48
+ __all__ = (
49
+ "SQL",
50
+ "ArrowResult",
51
+ "AsyncDatabaseConfig",
52
+ "AsyncDriverAdapterBase",
53
+ "CacheConfig",
54
+ "CacheStats",
55
+ "Column",
56
+ "ColumnExpression",
57
+ "ConnectionT",
58
+ "CreateTable",
59
+ "Delete",
60
+ "DropTable",
61
+ "ExecutionResult",
62
+ "FunctionColumn",
63
+ "Insert",
64
+ "Merge",
65
+ "ParameterConverter",
66
+ "ParameterProcessor",
67
+ "ParameterStyle",
68
+ "ParameterStyleConfig",
69
+ "PoolT",
70
+ "ProcessedState",
71
+ "QueryBuilder",
72
+ "SQLFactory",
73
+ "SQLFile",
74
+ "SQLFileLoader",
75
+ "SQLResult",
76
+ "SQLSpec",
77
+ "SchemaT",
78
+ "Select",
79
+ "StackExecutionError",
80
+ "StackOperation",
81
+ "StackResult",
82
+ "Statement",
83
+ "StatementConfig",
84
+ "StatementParameters",
85
+ "StatementStack",
86
+ "SupportedSchemaModel",
87
+ "SyncDatabaseConfig",
88
+ "SyncDriverAdapterBase",
89
+ "Update",
90
+ "__version__",
91
+ "adapters",
92
+ "base",
93
+ "builder",
94
+ "core",
95
+ "driver",
96
+ "exceptions",
97
+ "extensions",
98
+ "filters",
99
+ "loader",
100
+ "migrations",
101
+ "sql",
102
+ "typing",
103
+ "utils",
104
+ )
sqlspec/__main__.py ADDED
@@ -0,0 +1,12 @@
1
+ from sqlspec.cli import add_migration_commands as build_cli_interface
2
+
3
+ __all__ = ("run_cli",)
4
+
5
+
6
+ def run_cli() -> None: # pragma: no cover
7
+ """SQLSpec CLI."""
8
+ build_cli_interface()()
9
+
10
+
11
+ if __name__ == "__main__": # pragma: no cover
12
+ run_cli()
@@ -0,0 +1,14 @@
1
+ """Metadata for the project."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, metadata, version
4
+
5
+ __all__ = ("__project__", "__version__")
6
+
7
+ try:
8
+ __version__ = version("sqlspec")
9
+ __project__ = metadata("sqlspec")["Name"]
10
+ except PackageNotFoundError: # pragma: no cover
11
+ __version__ = "0.0.1"
12
+ __project__ = "SQLSpec"
13
+ finally:
14
+ del version, PackageNotFoundError, metadata
@@ -0,0 +1,312 @@
1
+ """Enhanced serialization module with byte-aware encoding and class-based architecture.
2
+
3
+ Provides a Protocol-based serialization system that users can extend.
4
+ Supports msgspec, orjson, and standard library JSON with automatic fallback.
5
+
6
+ Features optional numpy array serialization when numpy is installed.
7
+ Arrays are automatically converted to lists during JSON encoding.
8
+ """
9
+
10
+ import contextlib
11
+ import datetime
12
+ import enum
13
+ import json
14
+ from abc import ABC, abstractmethod
15
+ from decimal import Decimal
16
+ from typing import Any, Final, Literal, Protocol, overload
17
+
18
+ from sqlspec._typing import NUMPY_INSTALLED
19
+ from sqlspec.typing import MSGSPEC_INSTALLED, ORJSON_INSTALLED, PYDANTIC_INSTALLED, BaseModel
20
+
21
+
22
+ def _type_to_string(value: Any) -> Any: # pragma: no cover
23
+ """Convert special types to strings for JSON serialization.
24
+
25
+ Handles datetime, date, enums, Decimal, Pydantic models, and numpy arrays.
26
+
27
+ Args:
28
+ value: Value to convert.
29
+
30
+ Returns:
31
+ Serializable representation of the value (string, list, dict, etc.).
32
+
33
+ Raises:
34
+ TypeError: If value cannot be serialized.
35
+ """
36
+ if isinstance(value, datetime.datetime):
37
+ return convert_datetime_to_gmt_iso(value)
38
+ if isinstance(value, datetime.date):
39
+ return convert_date_to_iso(value)
40
+ if isinstance(value, Decimal):
41
+ return float(value)
42
+ if isinstance(value, enum.Enum):
43
+ return str(value.value)
44
+ if PYDANTIC_INSTALLED and isinstance(value, BaseModel):
45
+ return value.model_dump_json()
46
+ if NUMPY_INSTALLED:
47
+ import numpy as np
48
+
49
+ if isinstance(value, np.ndarray):
50
+ return value.tolist()
51
+ try:
52
+ return str(value)
53
+ except Exception as exc:
54
+ msg = f"Cannot serialize {type(value).__name__}"
55
+ raise TypeError(msg) from exc
56
+
57
+
58
+ class JSONSerializer(Protocol):
59
+ """Protocol for JSON serialization implementations.
60
+
61
+ Users can implement this protocol to create custom serializers.
62
+ """
63
+
64
+ def encode(self, data: Any, *, as_bytes: bool = False) -> str | bytes:
65
+ """Encode data to JSON.
66
+
67
+ Args:
68
+ data: Data to encode.
69
+ as_bytes: Whether to return bytes instead of string.
70
+
71
+ Returns:
72
+ JSON string or bytes depending on as_bytes parameter.
73
+ """
74
+ ...
75
+
76
+ def decode(self, data: str | bytes, *, decode_bytes: bool = True) -> Any:
77
+ """Decode from JSON.
78
+
79
+ Args:
80
+ data: JSON string or bytes to decode.
81
+ decode_bytes: Whether to decode bytes input.
82
+
83
+ Returns:
84
+ Decoded Python object.
85
+ """
86
+ ...
87
+
88
+
89
+ class BaseJSONSerializer(ABC):
90
+ """Base class for JSON serializers with common functionality."""
91
+
92
+ __slots__ = ()
93
+
94
+ @abstractmethod
95
+ def encode(self, data: Any, *, as_bytes: bool = False) -> str | bytes:
96
+ """Encode data to JSON."""
97
+ ...
98
+
99
+ @abstractmethod
100
+ def decode(self, data: str | bytes, *, decode_bytes: bool = True) -> Any:
101
+ """Decode from JSON."""
102
+ ...
103
+
104
+
105
+ class MsgspecSerializer(BaseJSONSerializer):
106
+ """Msgspec-based JSON serializer."""
107
+
108
+ __slots__ = ("_decoder", "_encoder")
109
+
110
+ def __init__(self) -> None:
111
+ """Initialize msgspec encoder and decoder."""
112
+ from msgspec.json import Decoder, Encoder
113
+
114
+ self._encoder: Final[Encoder] = Encoder(enc_hook=_type_to_string)
115
+ self._decoder: Final[Decoder] = Decoder()
116
+
117
+ def encode(self, data: Any, *, as_bytes: bool = False) -> str | bytes:
118
+ """Encode data using msgspec."""
119
+ try:
120
+ if as_bytes:
121
+ return self._encoder.encode(data)
122
+ return self._encoder.encode(data).decode("utf-8")
123
+ except (TypeError, ValueError):
124
+ if ORJSON_INSTALLED:
125
+ return OrjsonSerializer().encode(data, as_bytes=as_bytes)
126
+ return StandardLibSerializer().encode(data, as_bytes=as_bytes)
127
+
128
+ def decode(self, data: str | bytes, *, decode_bytes: bool = True) -> Any:
129
+ """Decode data using msgspec."""
130
+ if isinstance(data, bytes):
131
+ if decode_bytes:
132
+ try:
133
+ return self._decoder.decode(data)
134
+ except (TypeError, ValueError):
135
+ if ORJSON_INSTALLED:
136
+ return OrjsonSerializer().decode(data, decode_bytes=decode_bytes)
137
+ return StandardLibSerializer().decode(data, decode_bytes=decode_bytes)
138
+ return data
139
+
140
+ try:
141
+ return self._decoder.decode(data.encode("utf-8"))
142
+ except (TypeError, ValueError):
143
+ if ORJSON_INSTALLED:
144
+ return OrjsonSerializer().decode(data, decode_bytes=decode_bytes)
145
+ return StandardLibSerializer().decode(data, decode_bytes=decode_bytes)
146
+
147
+
148
+ class OrjsonSerializer(BaseJSONSerializer):
149
+ """Orjson-based JSON serializer with native datetime/UUID support.
150
+
151
+ Automatically enables numpy serialization if numpy is installed.
152
+ """
153
+
154
+ __slots__ = ()
155
+
156
+ def encode(self, data: Any, *, as_bytes: bool = False) -> str | bytes:
157
+ """Encode data using orjson.
158
+
159
+ Args:
160
+ data: Data to encode.
161
+ as_bytes: Whether to return bytes instead of string.
162
+
163
+ Returns:
164
+ JSON string or bytes depending on as_bytes parameter.
165
+ """
166
+ from orjson import (
167
+ OPT_NAIVE_UTC, # pyright: ignore[reportUnknownVariableType]
168
+ OPT_SERIALIZE_UUID, # pyright: ignore[reportUnknownVariableType]
169
+ )
170
+ from orjson import dumps as _orjson_dumps # pyright: ignore[reportMissingImports]
171
+
172
+ options = OPT_NAIVE_UTC | OPT_SERIALIZE_UUID
173
+
174
+ if NUMPY_INSTALLED:
175
+ from orjson import OPT_SERIALIZE_NUMPY # pyright: ignore[reportUnknownVariableType]
176
+
177
+ options |= OPT_SERIALIZE_NUMPY
178
+
179
+ result = _orjson_dumps(data, default=_type_to_string, option=options)
180
+ return result if as_bytes else result.decode("utf-8")
181
+
182
+ def decode(self, data: str | bytes, *, decode_bytes: bool = True) -> Any:
183
+ """Decode data using orjson."""
184
+ from orjson import loads as _orjson_loads # pyright: ignore[reportMissingImports]
185
+
186
+ if isinstance(data, bytes):
187
+ if decode_bytes:
188
+ return _orjson_loads(data)
189
+ return data
190
+ return _orjson_loads(data)
191
+
192
+
193
+ class StandardLibSerializer(BaseJSONSerializer):
194
+ """Standard library JSON serializer as fallback."""
195
+
196
+ __slots__ = ()
197
+
198
+ def encode(self, data: Any, *, as_bytes: bool = False) -> str | bytes:
199
+ """Encode data using standard library json."""
200
+ json_str = json.dumps(data, default=_type_to_string)
201
+ return json_str.encode("utf-8") if as_bytes else json_str
202
+
203
+ def decode(self, data: str | bytes, *, decode_bytes: bool = True) -> Any:
204
+ """Decode data using standard library json."""
205
+ if isinstance(data, bytes):
206
+ if decode_bytes:
207
+ return json.loads(data.decode("utf-8"))
208
+ return data
209
+ return json.loads(data)
210
+
211
+
212
+ _default_serializer: JSONSerializer | None = None
213
+
214
+
215
+ def get_default_serializer() -> JSONSerializer:
216
+ """Get the default serializer based on available libraries.
217
+
218
+ Priority: msgspec > orjson > stdlib
219
+
220
+ Returns:
221
+ The best available JSON serializer.
222
+ """
223
+ global _default_serializer
224
+
225
+ if _default_serializer is None:
226
+ if MSGSPEC_INSTALLED:
227
+ with contextlib.suppress(ImportError):
228
+ _default_serializer = MsgspecSerializer()
229
+
230
+ if _default_serializer is None and ORJSON_INSTALLED:
231
+ with contextlib.suppress(ImportError):
232
+ _default_serializer = OrjsonSerializer()
233
+
234
+ if _default_serializer is None:
235
+ _default_serializer = StandardLibSerializer()
236
+
237
+ assert _default_serializer is not None
238
+ return _default_serializer
239
+
240
+
241
+ @overload
242
+ def encode_json(data: Any, *, as_bytes: Literal[False] = ...) -> str: ... # pragma: no cover
243
+
244
+
245
+ @overload
246
+ def encode_json(data: Any, *, as_bytes: Literal[True]) -> bytes: ... # pragma: no cover
247
+
248
+
249
+ def encode_json(data: Any, *, as_bytes: bool = False) -> str | bytes:
250
+ """Encode to JSON, optionally returning bytes.
251
+
252
+ Args:
253
+ data: The data to encode.
254
+ as_bytes: Whether to return bytes instead of string.
255
+
256
+ Returns:
257
+ JSON string or bytes depending on as_bytes parameter.
258
+ """
259
+ return get_default_serializer().encode(data, as_bytes=as_bytes)
260
+
261
+
262
+ def decode_json(data: str | bytes, *, decode_bytes: bool = True) -> Any:
263
+ """Decode from JSON string or bytes efficiently.
264
+
265
+ Args:
266
+ data: JSON string or bytes to decode.
267
+ decode_bytes: Whether to decode bytes input.
268
+
269
+ Returns:
270
+ Decoded Python object.
271
+ """
272
+ return get_default_serializer().decode(data, decode_bytes=decode_bytes)
273
+
274
+
275
+ def convert_datetime_to_gmt_iso(dt: datetime.datetime) -> str: # pragma: no cover
276
+ """Handle datetime serialization for nested timestamps.
277
+
278
+ Args:
279
+ dt: The datetime to convert.
280
+
281
+ Returns:
282
+ The ISO formatted datetime string.
283
+ """
284
+ if not dt.tzinfo:
285
+ dt = dt.replace(tzinfo=datetime.timezone.utc)
286
+ return dt.isoformat().replace("+00:00", "Z")
287
+
288
+
289
+ def convert_date_to_iso(dt: datetime.date) -> str: # pragma: no cover
290
+ """Handle datetime serialization for nested timestamps.
291
+
292
+ Args:
293
+ dt: The date to convert.
294
+
295
+ Returns:
296
+ The ISO formatted date string.
297
+ """
298
+ return dt.isoformat()
299
+
300
+
301
+ __all__ = (
302
+ "BaseJSONSerializer",
303
+ "JSONSerializer",
304
+ "MsgspecSerializer",
305
+ "OrjsonSerializer",
306
+ "StandardLibSerializer",
307
+ "convert_date_to_iso",
308
+ "convert_datetime_to_gmt_iso",
309
+ "decode_json",
310
+ "encode_json",
311
+ "get_default_serializer",
312
+ )