deev 2.1.2__tar.gz → 2.1.4__tar.gz
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.
- {deev-2.1.2/src/deev.egg-info → deev-2.1.4}/PKG-INFO +1 -1
- {deev-2.1.2 → deev-2.1.4}/pyproject.toml +1 -1
- {deev-2.1.2 → deev-2.1.4}/src/deev/__init__.py +2 -2
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/async_mongo_table_adapter.py +5 -1
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/mongo_table_adapter.py +5 -1
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/utils.py +21 -1
- {deev-2.1.2 → deev-2.1.4}/src/deev/translation/utils.py +75 -19
- {deev-2.1.2 → deev-2.1.4/src/deev.egg-info}/PKG-INFO +1 -1
- {deev-2.1.2 → deev-2.1.4}/LICENSE +0 -0
- {deev-2.1.2 → deev-2.1.4}/README.md +0 -0
- {deev-2.1.2 → deev-2.1.4}/setup.cfg +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/SKILL.md +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/_immutable_mixin.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/_migration_data.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/async_clickhouse_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/async_clickhouse_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/async_clickhouse_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/async_clickhouse_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/clickhouse_ddl_generator.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/clickhouse_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/clickhouse_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/clickhouse_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/clickhouse_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/clickhouse_type_mapper.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/clickhouse/utils.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/async_db_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/async_db_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/async_db_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/async_db_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/async_db_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/connection_string.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_error.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_migrator.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_parameters.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/db_type_mapper.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/description_field.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/noop_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/common/tlc_parser.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/db_migrate.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/entities/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/entities/entity_field_spec.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/entities/entity_spec.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/entities/index_options.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/entities/index_order.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/entities/utils.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/async_mongo_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/async_mongo_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/async_mongo_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/mongo_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/mongo_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/mongo_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mongodb/mongo_type_mapper.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/async_mssql_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/async_mssql_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/async_mssql_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/async_mssql_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/mssql_ddl_generator.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/mssql_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/mssql_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/mssql_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/mssql_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mssql/mssql_type_mapper.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/async_mysql_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/async_mysql_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/async_mysql_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/async_mysql_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/mysql_ddl_generator.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/mysql_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/mysql_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/mysql_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/mysql_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/mysql/mysql_type_mapper.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/py.typed +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/async_sqlite_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/async_sqlite_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/async_sqlite_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/async_sqlite_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/sqlite_ddl_generator.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/sqlite_proxy_connection.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/sqlite_proxy_cursor.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/sqlite_table_adapter.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/sqlite_transaction_context.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/sqlite/sqlite_type_mapper.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/translation/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/translation/deev_json_decoder.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/translation/deev_json_encoder.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/utils.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/validation/__init__.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/validation/utils.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev/validation/validation_error.py +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev.egg-info/SOURCES.txt +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev.egg-info/dependency_links.txt +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev.egg-info/entry_points.txt +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev.egg-info/requires.txt +0 -0
- {deev-2.1.2 → deev-2.1.4}/src/deev.egg-info/top_level.txt +0 -0
|
@@ -15,6 +15,7 @@ from ..common.db_parameters import DbParameters
|
|
|
15
15
|
from ..common.db_type_mapper import DbTypeMapper
|
|
16
16
|
from ..entities import EntitySpec, get_entity_spec
|
|
17
17
|
from ..translation import hydrate, splat, to_pyobject
|
|
18
|
+
from ..translation.utils import to_bsonobject
|
|
18
19
|
from .utils import parse_sql_where
|
|
19
20
|
from .mongo_type_mapper import MongoTypeMapper
|
|
20
21
|
|
|
@@ -218,7 +219,7 @@ class AsyncMongoTableAdapter(AsyncDbTableAdapter[TEntity]):
|
|
|
218
219
|
else dict[str, Any]()
|
|
219
220
|
)
|
|
220
221
|
if kwargs:
|
|
221
|
-
for k, v in kwargs.items():
|
|
222
|
+
for k, v in to_bsonobject(kwargs).items():
|
|
222
223
|
data[k] = v
|
|
223
224
|
primary_key = {
|
|
224
225
|
k: v
|
|
@@ -248,6 +249,7 @@ class AsyncMongoTableAdapter(AsyncDbTableAdapter[TEntity]):
|
|
|
248
249
|
Reads a record from the specified table with the key represented by `kwargs`.
|
|
249
250
|
"""
|
|
250
251
|
await self.__deferred_init()
|
|
252
|
+
kwargs = to_bsonobject(kwargs)
|
|
251
253
|
primary_key = {
|
|
252
254
|
k: v
|
|
253
255
|
for k, v in kwargs.items()
|
|
@@ -274,6 +276,7 @@ class AsyncMongoTableAdapter(AsyncDbTableAdapter[TEntity]):
|
|
|
274
276
|
|
|
275
277
|
async def delete(self, **kwargs: Any) -> None:
|
|
276
278
|
await self.__deferred_init()
|
|
279
|
+
kwargs = to_bsonobject(kwargs)
|
|
277
280
|
primary_key = {
|
|
278
281
|
k: v
|
|
279
282
|
for k, v in kwargs.items()
|
|
@@ -284,6 +287,7 @@ class AsyncMongoTableAdapter(AsyncDbTableAdapter[TEntity]):
|
|
|
284
287
|
|
|
285
288
|
async def exists(self, **kwargs: Any) -> bool:
|
|
286
289
|
await self.__deferred_init()
|
|
290
|
+
kwargs = to_bsonobject(kwargs)
|
|
287
291
|
primary_key = {
|
|
288
292
|
k: v
|
|
289
293
|
for k, v in kwargs.items()
|
|
@@ -13,6 +13,7 @@ from ..common.db_parameters import DbParameters
|
|
|
13
13
|
from ..common.db_type_mapper import DbTypeMapper
|
|
14
14
|
from ..entities import EntitySpec, get_entity_spec
|
|
15
15
|
from ..translation import hydrate, splat, to_pyobject
|
|
16
|
+
from ..translation.utils import to_bsonobject
|
|
16
17
|
from .utils import parse_sql_where
|
|
17
18
|
from .mongo_type_mapper import MongoTypeMapper
|
|
18
19
|
|
|
@@ -205,7 +206,7 @@ class MongoTableAdapter(Generic[TEntity]):
|
|
|
205
206
|
else dict[str, Any]()
|
|
206
207
|
)
|
|
207
208
|
if kwargs:
|
|
208
|
-
for k, v in kwargs.items():
|
|
209
|
+
for k, v in to_bsonobject(kwargs).items():
|
|
209
210
|
data[k] = v
|
|
210
211
|
primary_key = {
|
|
211
212
|
k: v
|
|
@@ -237,6 +238,7 @@ class MongoTableAdapter(Generic[TEntity]):
|
|
|
237
238
|
Reads a record from the specified table with the key represented by `kwargs`.
|
|
238
239
|
"""
|
|
239
240
|
self.__deferred_init()
|
|
241
|
+
kwargs = to_bsonobject(kwargs)
|
|
240
242
|
primary_key = {
|
|
241
243
|
k: v
|
|
242
244
|
for k, v in kwargs.items()
|
|
@@ -264,6 +266,7 @@ class MongoTableAdapter(Generic[TEntity]):
|
|
|
264
266
|
|
|
265
267
|
def delete(self, **kwargs: Any) -> None:
|
|
266
268
|
self.__deferred_init()
|
|
269
|
+
kwargs = to_bsonobject(kwargs)
|
|
267
270
|
primary_key = {
|
|
268
271
|
k: v
|
|
269
272
|
for k, v in kwargs.items()
|
|
@@ -274,6 +277,7 @@ class MongoTableAdapter(Generic[TEntity]):
|
|
|
274
277
|
|
|
275
278
|
def exists(self, **kwargs: Any) -> bool:
|
|
276
279
|
self.__deferred_init()
|
|
280
|
+
kwargs = to_bsonobject(kwargs)
|
|
277
281
|
primary_key = {
|
|
278
282
|
k: v
|
|
279
283
|
for k, v in kwargs.items()
|
|
@@ -9,6 +9,26 @@ from typing import (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
from ..common.db_error import DbError
|
|
12
|
+
from ..translation.utils import to_bsonobject
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _serialize_param(value: Any) -> Any:
|
|
16
|
+
"""Serialize a query parameter value for MongoDB storage format.
|
|
17
|
+
|
|
18
|
+
Converts ``datetime.date`` to midnight datetime, ``datetime.time`` to ISO string,
|
|
19
|
+
and passes through all other types unchanged (datetime, str, int, etc. are
|
|
20
|
+
already compatible with MongoDB storage).
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(value, dict) or isinstance(value, list):
|
|
23
|
+
return value # MongoDB operators like {'$gte': ...} pass through
|
|
24
|
+
return to_bsonobject(value)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_param(value: Any) -> Any:
|
|
28
|
+
"""Resolve a resolved parameter value, applying serialization."""
|
|
29
|
+
if isinstance(value, tuple):
|
|
30
|
+
return tuple(_serialize_param(v) for v in value)
|
|
31
|
+
return _serialize_param(value)
|
|
12
32
|
|
|
13
33
|
|
|
14
34
|
def parse_sql_where(where_clause: str | None, parameters: tuple[Any, ...]) -> dict[str, Any]:
|
|
@@ -31,7 +51,7 @@ def parse_sql_where(where_clause: str | None, parameters: tuple[Any, ...]) -> di
|
|
|
31
51
|
nonlocal param_idx
|
|
32
52
|
if param_idx >= len(parameters):
|
|
33
53
|
raise DbError('WHERE clause has more placeholders than provided parameters.')
|
|
34
|
-
val = parameters[param_idx]
|
|
54
|
+
val = _resolve_param(parameters[param_idx])
|
|
35
55
|
param_idx += 1
|
|
36
56
|
return val
|
|
37
57
|
|
|
@@ -106,10 +106,13 @@ def to_pyobject(value: Any, hint: type) -> Any:
|
|
|
106
106
|
|
|
107
107
|
Handles type coercion for ``int``, ``float``, ``bool``, ``UUID``, ``datetime``,
|
|
108
108
|
``date``, ``time``, ``timedelta``, ``Decimal``, ``Enum``, and complex types
|
|
109
|
-
(``dict``, ``list``, ``set``, ``tuple``) deserialized from JSON.
|
|
109
|
+
(``dict``, ``list``, ``set``, ``tuple``) deserialized from JSON or BSON.
|
|
110
|
+
|
|
111
|
+
For container types with element hints (e.g. ``list[date]``, ``dict[str, time]``),
|
|
112
|
+
each element is recursively converted using the appropriate element type hint.
|
|
110
113
|
|
|
111
114
|
:param value: The raw value from the database.
|
|
112
|
-
:param hint: The type hint for the field (e.g. ``Optional[str]``).
|
|
115
|
+
:param hint: The type hint for the field (e.g. ``Optional[str]`` or ``list[date]``).
|
|
113
116
|
:return: The value converted to the appropriate Python type.
|
|
114
117
|
"""
|
|
115
118
|
if value in (None, NoneType, 'null', 'NULL'):
|
|
@@ -132,6 +135,8 @@ def to_pyobject(value: Any, hint: type) -> Any:
|
|
|
132
135
|
if value.tzinfo is None:
|
|
133
136
|
return value.replace(tzinfo=timezone.utc)
|
|
134
137
|
return value
|
|
138
|
+
elif hint == date and isinstance(value, datetime):
|
|
139
|
+
return value.date()
|
|
135
140
|
elif hint == date and isinstance(value, str):
|
|
136
141
|
return date.fromisoformat(value)
|
|
137
142
|
elif hint == time and isinstance(value, str):
|
|
@@ -139,6 +144,8 @@ def to_pyobject(value: Any, hint: type) -> Any:
|
|
|
139
144
|
if tm.tzinfo is not None:
|
|
140
145
|
return tm.replace(tzinfo=timezone.utc)
|
|
141
146
|
return tm
|
|
147
|
+
elif hint == time and isinstance(value, time):
|
|
148
|
+
return value
|
|
142
149
|
elif hint == timedelta and isinstance(value, int):
|
|
143
150
|
return timedelta(microseconds=value)
|
|
144
151
|
elif hint == Decimal:
|
|
@@ -158,6 +165,33 @@ def to_pyobject(value: Any, hint: type) -> Any:
|
|
|
158
165
|
if not isinstance(d, org):
|
|
159
166
|
d = hint(d) # type: ignore[call-arg]
|
|
160
167
|
return d
|
|
168
|
+
elif org in (list, tuple, set):
|
|
169
|
+
args = get_args(hint)
|
|
170
|
+
if args:
|
|
171
|
+
element_hint = args[0]
|
|
172
|
+
if isinstance(value, (list, tuple, set)):
|
|
173
|
+
if element_hint is not Any:
|
|
174
|
+
if org is tuple:
|
|
175
|
+
return tuple(to_pyobject(v, element_hint) for v in value)
|
|
176
|
+
elif org is set:
|
|
177
|
+
return {to_pyobject(v, element_hint) for v in value}
|
|
178
|
+
else:
|
|
179
|
+
return [to_pyobject(v, element_hint) for v in value]
|
|
180
|
+
else:
|
|
181
|
+
return value
|
|
182
|
+
else:
|
|
183
|
+
return value
|
|
184
|
+
elif org in (Mapping, dict):
|
|
185
|
+
args = get_args(hint)
|
|
186
|
+
if len(args) >= 2:
|
|
187
|
+
key_hint, value_hint = args[0], args[1]
|
|
188
|
+
if isinstance(value, dict):
|
|
189
|
+
if key_hint is not Any and value_hint is not Any:
|
|
190
|
+
return {to_pyobject(k, key_hint): to_pyobject(v, value_hint) for k, v in value.items()}
|
|
191
|
+
else:
|
|
192
|
+
return value
|
|
193
|
+
else:
|
|
194
|
+
return value
|
|
161
195
|
else:
|
|
162
196
|
return value
|
|
163
197
|
|
|
@@ -192,41 +226,63 @@ def _to_json_value(value: Any) -> Any:
|
|
|
192
226
|
return value
|
|
193
227
|
|
|
194
228
|
|
|
195
|
-
def to_bsonobject(value: Any) -> Any:
|
|
229
|
+
def to_bsonobject(value: Any, hint: type | None = None) -> Any:
|
|
196
230
|
"""
|
|
197
231
|
Convert a value for MongoDB BSON storage.
|
|
198
232
|
|
|
199
|
-
|
|
200
|
-
|
|
233
|
+
Unlike :func:`_to_json_value`, this leaves types that BSON supports natively
|
|
234
|
+
as their Python equivalents — PyMongo handles the rest:
|
|
235
|
+
|
|
236
|
+
- ``datetime.datetime`` is passed through as-is (BSON native Date, 8 bytes)
|
|
237
|
+
- ``datetime.date`` is converted to a midnight ``datetime`` for storage
|
|
238
|
+
- ``datetime.time`` is converted to an ISO 8601 string ("HH:MM:SS.ffffff")
|
|
239
|
+
- ``uuid.UUID`` is left as a Python ``UUID`` object (BSON binary)
|
|
201
240
|
- ``set`` values are converted to ``list`` (BSON arrays)
|
|
202
|
-
- ``
|
|
203
|
-
|
|
241
|
+
- ``list``/``tuple``/``dict`` are recursively serialized element-by-element
|
|
242
|
+
|
|
243
|
+
When ``hint`` is provided, container element hints are extracted and used
|
|
244
|
+
to convert each element (e.g. ``list[date]`` converts each element via
|
|
245
|
+
``to_bsonobject(v, date)`` rather than relying on runtime ``isinstance``).
|
|
204
246
|
|
|
205
247
|
:param value: The Python value to convert.
|
|
248
|
+
:param hint: Optional type hint (e.g. ``list[date]``).
|
|
206
249
|
:return: A BSON-compatible value.
|
|
207
250
|
"""
|
|
208
251
|
if value in (None, NoneType, 'null', 'NULL'):
|
|
209
252
|
return None
|
|
210
253
|
if isinstance(value, datetime):
|
|
211
|
-
|
|
212
|
-
return _utc_z(value)
|
|
213
|
-
else:
|
|
214
|
-
return value.isoformat()
|
|
254
|
+
return value
|
|
215
255
|
elif isinstance(value, date):
|
|
216
|
-
return value.
|
|
256
|
+
return datetime(value.year, value.month, value.day)
|
|
217
257
|
elif isinstance(value, time):
|
|
218
|
-
|
|
219
|
-
return _utc_z_time(value)
|
|
220
|
-
else:
|
|
221
|
-
return value.isoformat()
|
|
258
|
+
return value.isoformat()
|
|
222
259
|
elif isinstance(value, Decimal):
|
|
223
260
|
return str(value)
|
|
224
261
|
# UUID: leave as UUID object — PyMongo handles native BSON binary
|
|
225
262
|
elif isinstance(value, set):
|
|
226
|
-
return
|
|
263
|
+
return [to_bsonobject(v) for v in value]
|
|
227
264
|
elif isinstance(value, Enum):
|
|
228
265
|
return value.value
|
|
229
|
-
|
|
266
|
+
elif isinstance(value, timedelta):
|
|
267
|
+
return value.days * 86_400_000_000 + value.seconds * 1_000_000 + value.microseconds
|
|
268
|
+
elif isinstance(value, list):
|
|
269
|
+
args = get_args(hint) if hint else ()
|
|
270
|
+
if args:
|
|
271
|
+
element_hint = args[0]
|
|
272
|
+
return [to_bsonobject(v, element_hint) for v in value]
|
|
273
|
+
return [to_bsonobject(v) for v in value]
|
|
274
|
+
elif isinstance(value, tuple):
|
|
275
|
+
args = get_args(hint) if hint else ()
|
|
276
|
+
if args:
|
|
277
|
+
element_hint = args[0]
|
|
278
|
+
return tuple(to_bsonobject(v, element_hint) for v in value)
|
|
279
|
+
return tuple(to_bsonobject(v) for v in value)
|
|
280
|
+
elif isinstance(value, dict):
|
|
281
|
+
args = get_args(hint) if hint else ()
|
|
282
|
+
if len(args) >= 2:
|
|
283
|
+
key_hint, value_hint = args[0], args[1]
|
|
284
|
+
return {to_bsonobject(k, key_hint): to_bsonobject(v, value_hint) for k, v in value.items()}
|
|
285
|
+
return {k: to_bsonobject(v) for k, v in value.items()}
|
|
230
286
|
return value
|
|
231
287
|
|
|
232
288
|
|
|
@@ -347,7 +403,7 @@ def splat(entity: object, attrs: list[str] | None = None, to_sql: bool = False,
|
|
|
347
403
|
elif to_sql:
|
|
348
404
|
result[attr_name] = to_sqlobject(attr_value, attr_hint)
|
|
349
405
|
elif to_bson:
|
|
350
|
-
result[attr_name] = to_bsonobject(attr_value)
|
|
406
|
+
result[attr_name] = to_bsonobject(attr_value, attr_hint)
|
|
351
407
|
else:
|
|
352
408
|
result[attr_name] = _to_json_value(attr_value)
|
|
353
409
|
elif field_spec.nullable:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|