neuroglia-python 0.7.2__tar.gz → 0.7.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.
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/PKG-INFO +1 -1
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/pyproject.toml +1 -1
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/__init__.py +1 -1
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/motor_query.py +89 -7
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/motor_repository.py +1 -1
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/queryable.py +62 -12
- neuroglia_python-0.7.4/src/neuroglia/hosting/configuration/data_access_layer.py +642 -0
- neuroglia_python-0.7.2/src/neuroglia/hosting/configuration/data_access_layer.py +0 -438
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/LICENSE +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/README.md +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/application/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/application/background_scheduler.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/coordination/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/coordination/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/coordination/leader_election.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/coordination/redis_backend.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/module_loader.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/operation_result.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/problem_details.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/type_extensions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/type_finder.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/core/type_registry.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/exceptions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/event_sourcing/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/event_sourcing/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/event_sourcing/event_sourcing_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/event_sourcing/event_store/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/event_sourcing/event_store/event_store.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/event_sourcing/read_model_reconciliator.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/filesystem/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/filesystem/filesystem_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/memory/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/memory/memory_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/enhanced_mongo_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/mongo_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/serialization_helper.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/mongo/typed_mongo_query.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/resources/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/resources/in_memory_storage_backend.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/resources/postgres_resource_store.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/resources/redis_resource_store.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/resources/resource_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/infrastructure/tracing_mixin.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/queries/generic/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/queries/generic/get_by_id_query.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/queries/generic/list_query.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/controller.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/serializers/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/serializers/xml_serializer.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/serializers/yaml_serializer.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/state_machine.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/resources/watcher.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/data/unit_of_work.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/dependency_injection/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/dependency_injection/service_provider.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/dependency_injection/service_provider_backup.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/cloud_event.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/decorators.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/infrastructure/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_bus.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_ingestor.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_middleware.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_publisher.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/eventing/tracing.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/expressions/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/expressions/javascript_expression_translator.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/extensions/cqrs_metrics_extensions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/extensions/mediator_extensions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/extensions/state_persistence_extensions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/hosting/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/hosting/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/hosting/web.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/integration/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/integration/cache_repository.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/integration/http_service_client.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/integration/models.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/logging/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/logging/logger.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mapping/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mapping/mapper.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/behaviors/domain_event_cloudevent_behavior.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/behaviors/domain_event_dispatching_middleware.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/mediator.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/metrics_middleware.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/pipeline_behavior.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/simple.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mediation/tracing_middleware.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mvc/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/mvc/controller_base.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/framework.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/logging.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/metrics.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/otel_sdk.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/settings.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/observability/tracing.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/py.typed +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/reactive/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/reactive/rx_async.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/serialization/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/serialization/abstractions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/serialization/aggregate_serializer.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/serialization/json.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/utils/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/utils/camel_model.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/utils/case_conversion.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/validation/__init__.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/validation/business_rules.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/validation/exceptions.py +0 -0
- {neuroglia_python-0.7.2 → neuroglia_python-0.7.4}/src/neuroglia/validation/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "neuroglia-python"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.4"
|
|
4
4
|
description = "Port from .NET to Python of the Neuroglia Framework"
|
|
5
5
|
authors = ["Charles d'Avernas <charles.davernas@neuroglia.io>", "Bruno van de Werve <bvandewe@gmail.com>"]
|
|
6
6
|
license = "Apache"
|
|
@@ -33,7 +33,7 @@ See Also:
|
|
|
33
33
|
|
|
34
34
|
import ast
|
|
35
35
|
from ast import NodeVisitor, expr
|
|
36
|
-
from typing import Any, Generic, List, Optional
|
|
36
|
+
from typing import TYPE_CHECKING, Any, Generic, List, Optional
|
|
37
37
|
|
|
38
38
|
import pymongo
|
|
39
39
|
from motor.motor_asyncio import AsyncIOMotorCollection, AsyncIOMotorCursor
|
|
@@ -43,6 +43,9 @@ from neuroglia.expressions.javascript_expression_translator import (
|
|
|
43
43
|
JavaScriptExpressionTranslator,
|
|
44
44
|
)
|
|
45
45
|
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from neuroglia.serialization import JsonSerializer
|
|
48
|
+
|
|
46
49
|
|
|
47
50
|
class MotorQuery(Generic[T], Queryable[T]):
|
|
48
51
|
"""
|
|
@@ -71,6 +74,62 @@ class MotorQuery(Generic[T], Queryable[T]):
|
|
|
71
74
|
"""
|
|
72
75
|
super().__init__(query_provider, expression)
|
|
73
76
|
|
|
77
|
+
async def to_list_async(self) -> list[T]:
|
|
78
|
+
"""
|
|
79
|
+
Asynchronously execute the query and return results as a list.
|
|
80
|
+
|
|
81
|
+
This is the async equivalent of to_list() for Motor queries.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
List of entities matching the query
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
```python
|
|
88
|
+
products = await repo.query_async() \\
|
|
89
|
+
.where(lambda p: p.price > 10) \\
|
|
90
|
+
.order_by(lambda p: p.name) \\
|
|
91
|
+
.to_list_async()
|
|
92
|
+
```
|
|
93
|
+
"""
|
|
94
|
+
# Get the provider and ensure it's a MotorQueryProvider
|
|
95
|
+
provider = self.provider
|
|
96
|
+
if not isinstance(provider, MotorQueryProvider):
|
|
97
|
+
raise TypeError("MotorQuery requires a MotorQueryProvider")
|
|
98
|
+
|
|
99
|
+
# Execute the query asynchronously
|
|
100
|
+
return await provider.execute_async(self.expression, List)
|
|
101
|
+
|
|
102
|
+
async def first_or_default_async(self, predicate=None) -> Optional[T]:
|
|
103
|
+
"""
|
|
104
|
+
Asynchronously get the first element matching the predicate, or None.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
predicate: Optional filter predicate
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
First matching entity or None
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
```python
|
|
114
|
+
product = await repo.query_async() \\
|
|
115
|
+
.where(lambda p: p.id == "prod123") \\
|
|
116
|
+
.first_or_default_async()
|
|
117
|
+
```
|
|
118
|
+
"""
|
|
119
|
+
# Get the provider and ensure it's a MotorQueryProvider
|
|
120
|
+
provider = self.provider
|
|
121
|
+
if not isinstance(provider, MotorQueryProvider):
|
|
122
|
+
raise TypeError("MotorQuery requires a MotorQueryProvider")
|
|
123
|
+
|
|
124
|
+
# If predicate provided, apply it as a where clause
|
|
125
|
+
query = self
|
|
126
|
+
if predicate is not None:
|
|
127
|
+
query = self.where(predicate)
|
|
128
|
+
|
|
129
|
+
# Execute and get list, then return first or None
|
|
130
|
+
results = await provider.execute_async(query.expression, List)
|
|
131
|
+
return results[0] if results else None
|
|
132
|
+
|
|
74
133
|
|
|
75
134
|
class MotorQueryBuilder(NodeVisitor):
|
|
76
135
|
"""
|
|
@@ -228,25 +287,28 @@ class MotorQueryProvider(QueryProvider):
|
|
|
228
287
|
Attributes:
|
|
229
288
|
_collection: Motor async collection to query
|
|
230
289
|
_entity_type: Type of entities in the collection
|
|
290
|
+
_serializer: JSON serializer for entity conversion
|
|
231
291
|
|
|
232
292
|
Example:
|
|
233
293
|
```python
|
|
234
|
-
provider = MotorQueryProvider(motor_collection, Product)
|
|
294
|
+
provider = MotorQueryProvider(motor_collection, Product, serializer)
|
|
235
295
|
query = provider.create_query(Product, expression)
|
|
236
296
|
results = await provider.execute_async(expression, List[Product])
|
|
237
297
|
```
|
|
238
298
|
"""
|
|
239
299
|
|
|
240
|
-
def __init__(self, collection: AsyncIOMotorCollection, entity_type: type):
|
|
300
|
+
def __init__(self, collection: AsyncIOMotorCollection, entity_type: type, serializer: "JsonSerializer"):
|
|
241
301
|
"""
|
|
242
302
|
Initialize the Motor query provider.
|
|
243
303
|
|
|
244
304
|
Args:
|
|
245
305
|
collection: Motor async collection
|
|
246
306
|
entity_type: Type of entities being queried
|
|
307
|
+
serializer: JSON serializer for entity conversion
|
|
247
308
|
"""
|
|
248
309
|
self._collection = collection
|
|
249
310
|
self._entity_type = entity_type
|
|
311
|
+
self._serializer = serializer
|
|
250
312
|
|
|
251
313
|
def create_query(self, element_type: type, expression: expr) -> Queryable:
|
|
252
314
|
"""
|
|
@@ -308,9 +370,29 @@ class MotorQueryProvider(QueryProvider):
|
|
|
308
370
|
is_list_type = query_type.__origin__ in (list, List)
|
|
309
371
|
|
|
310
372
|
if is_list_type:
|
|
311
|
-
# Return list of
|
|
312
|
-
|
|
373
|
+
# Return list of deserialized entities
|
|
374
|
+
documents = [doc async for doc in cursor]
|
|
375
|
+
return [self._deserialize_entity(doc) for doc in documents]
|
|
313
376
|
else:
|
|
314
|
-
# Return single
|
|
377
|
+
# Return single deserialized entity
|
|
315
378
|
results = await cursor.to_list(length=1)
|
|
316
|
-
return results[0] if results else None
|
|
379
|
+
return self._deserialize_entity(results[0]) if results else None
|
|
380
|
+
|
|
381
|
+
def _deserialize_entity(self, doc: dict) -> Any:
|
|
382
|
+
"""
|
|
383
|
+
Deserialize a MongoDB document to an entity instance.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
doc: MongoDB document dictionary
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
Deserialized entity instance
|
|
390
|
+
"""
|
|
391
|
+
# Remove MongoDB's _id field
|
|
392
|
+
doc.pop("_id", None)
|
|
393
|
+
|
|
394
|
+
# Serialize dict back to JSON for deserialization
|
|
395
|
+
json_str = self._serializer.serialize_to_text(doc)
|
|
396
|
+
|
|
397
|
+
# Deserialize using the entity type
|
|
398
|
+
return self._serializer.deserialize_from_text(json_str, self._entity_type)
|
|
@@ -552,7 +552,7 @@ class MotorRepository(Generic[TEntity, TKey], QueryableRepository[TEntity, TKey]
|
|
|
552
552
|
if entity_type is None:
|
|
553
553
|
raise TypeError("Cannot create query: entity type not set. Pass entity_type to constructor.")
|
|
554
554
|
|
|
555
|
-
return MotorQuery[TEntity](MotorQueryProvider(self.collection, entity_type))
|
|
555
|
+
return MotorQuery[TEntity](MotorQueryProvider(self.collection, entity_type, self._serializer))
|
|
556
556
|
|
|
557
557
|
async def get_all_async(self) -> list[TEntity]:
|
|
558
558
|
"""
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import inspect
|
|
3
|
-
import os
|
|
4
3
|
from abc import ABC, abstractclassmethod
|
|
5
4
|
from ast import Attribute, Name, NodeTransformer, expr
|
|
6
5
|
from collections.abc import Callable
|
|
@@ -146,7 +145,9 @@ class Queryable(Generic[T]):
|
|
|
146
145
|
|
|
147
146
|
def order_by(self, selector: Callable[[T], Any]):
|
|
148
147
|
"""Orders the sequence using the specified attribute"""
|
|
149
|
-
|
|
148
|
+
frame = inspect.currentframe().f_back
|
|
149
|
+
frame_info = inspect.getframeinfo(frame)
|
|
150
|
+
selector_source = self._get_lambda_source_code(selector, frame_info.positions.end_col_offset)
|
|
150
151
|
selector_tree = ast.parse(selector_source)
|
|
151
152
|
selector_lambda_expression = selector_tree.body[0].value
|
|
152
153
|
if not isinstance(selector_lambda_expression.body, Attribute):
|
|
@@ -160,7 +161,9 @@ class Queryable(Generic[T]):
|
|
|
160
161
|
|
|
161
162
|
def order_by_descending(self, selector: Callable[[T], Any]):
|
|
162
163
|
"""Orders the sequence in a descending fashion using the specified attribute"""
|
|
163
|
-
|
|
164
|
+
frame = inspect.currentframe().f_back
|
|
165
|
+
frame_info = inspect.getframeinfo(frame)
|
|
166
|
+
selector_source = self._get_lambda_source_code(selector, frame_info.positions.end_col_offset)
|
|
164
167
|
selector_tree = ast.parse(selector_source)
|
|
165
168
|
selector_lambda_expression = selector_tree.body[0].value
|
|
166
169
|
if not isinstance(selector_lambda_expression.body, Attribute):
|
|
@@ -175,8 +178,9 @@ class Queryable(Generic[T]):
|
|
|
175
178
|
def select(self, selector: Callable[[T], Any]):
|
|
176
179
|
"""Projects each element of a sequence into a new form"""
|
|
177
180
|
frame = inspect.currentframe().f_back
|
|
181
|
+
frame_info = inspect.getframeinfo(frame)
|
|
178
182
|
variables = {**frame.f_locals}
|
|
179
|
-
selector_source = self._get_lambda_source_code(selector)
|
|
183
|
+
selector_source = self._get_lambda_source_code(selector, frame_info.positions.end_col_offset)
|
|
180
184
|
selector_tree = ast.parse(selector_source)
|
|
181
185
|
selector_lambda_expression = selector_tree.body[0].value
|
|
182
186
|
if not isinstance(selector_lambda_expression.body, Attribute) and not isinstance(selector_lambda_expression.body, ast.List):
|
|
@@ -239,18 +243,64 @@ class Queryable(Generic[T]):
|
|
|
239
243
|
function (Callable): The lambda to get the source code of
|
|
240
244
|
max_col_offset (int): The maximum column offset to walk the AST tree for the target lamba
|
|
241
245
|
|
|
246
|
+
Returns:
|
|
247
|
+
Optional[str]: The lambda source code, or None if extraction fails
|
|
248
|
+
|
|
242
249
|
Notes:
|
|
243
250
|
Credits to https://gist.github.com/Xion/617c1496ff45f3673a5692c3b0e3f75a
|
|
251
|
+
|
|
252
|
+
This method handles the case where the lambda is on a continuation line
|
|
253
|
+
that starts with '.' (common in method chaining). In such cases, the raw
|
|
254
|
+
source line is invalid Python syntax, so we prepend a dummy identifier
|
|
255
|
+
to make it parseable.
|
|
256
|
+
|
|
257
|
+
For multi-line continuations (backslash or implicit), we only process
|
|
258
|
+
the first line containing the lambda, as that's sufficient for extraction.
|
|
244
259
|
"""
|
|
245
|
-
|
|
246
|
-
|
|
260
|
+
try:
|
|
261
|
+
source_lines, _ = inspect.getsourcelines(function)
|
|
262
|
+
except (OSError, TypeError) as e:
|
|
263
|
+
# Cannot extract source (e.g., built-in functions, C extensions)
|
|
247
264
|
return None
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
None
|
|
253
|
-
|
|
265
|
+
|
|
266
|
+
# For multi-line sources (continuation), use only the first line
|
|
267
|
+
# The lambda itself is typically on the first line
|
|
268
|
+
if len(source_lines) == 0:
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
# Strip the line and remove trailing backslash if present (line continuation)
|
|
272
|
+
source_text = source_lines[0].strip().rstrip("\\").strip()
|
|
273
|
+
|
|
274
|
+
# Handle continuation lines that start with '.' (method chaining)
|
|
275
|
+
# These are invalid Python syntax on their own
|
|
276
|
+
offset_adjustment = 0
|
|
277
|
+
if source_text.startswith("."):
|
|
278
|
+
source_text = "_" + source_text # Prepend dummy identifier
|
|
279
|
+
offset_adjustment = 1
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
source_ast = ast.parse(source_text)
|
|
283
|
+
except SyntaxError:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
# Adjust max_col_offset if we prepended a dummy identifier
|
|
287
|
+
# Set to None if not provided to match all lambdas
|
|
288
|
+
adjusted_max_col_offset = None
|
|
289
|
+
if max_col_offset is not None:
|
|
290
|
+
adjusted_max_col_offset = max_col_offset + offset_adjustment
|
|
291
|
+
|
|
292
|
+
# Find lambda node within the adjusted column offset
|
|
293
|
+
if adjusted_max_col_offset is not None:
|
|
294
|
+
lambda_node = next(
|
|
295
|
+
(node for node in ast.walk(source_ast) if isinstance(node, ast.Lambda) and node.col_offset <= adjusted_max_col_offset),
|
|
296
|
+
None,
|
|
297
|
+
)
|
|
298
|
+
else:
|
|
299
|
+
lambda_node = next(
|
|
300
|
+
(node for node in ast.walk(source_ast) if isinstance(node, ast.Lambda)),
|
|
301
|
+
None,
|
|
302
|
+
)
|
|
303
|
+
|
|
254
304
|
if lambda_node is None:
|
|
255
305
|
return None
|
|
256
306
|
lambda_text = source_text[lambda_node.col_offset : lambda_node.end_col_offset]
|