python-cqrs 4.13.0__tar.gz → 4.14.0__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.
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/PKG-INFO +68 -26
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/README.md +63 -25
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/pyproject.toml +4 -1
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/__init__.py +14 -1
- python_cqrs-4.14.0/src/cqrs/container/__init__.py +18 -0
- python_cqrs-4.14.0/src/cqrs/container/di.py +75 -0
- python_cqrs-4.14.0/src/cqrs/container/dishka.py +48 -0
- python_cqrs-4.14.0/src/cqrs/container/protocol.py +43 -0
- python_cqrs-4.14.0/src/cqrs/container/scope.py +321 -0
- python_cqrs-4.14.0/src/cqrs/dispatcher/event.py +111 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/dispatcher/request.py +67 -46
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/dispatcher/saga.py +8 -4
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/dispatcher/streaming.py +70 -47
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/bootstrap.py +9 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/event_emitter.py +90 -51
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/mediator.py +114 -10
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/bootstrap.py +29 -50
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/bootstrap.py +20 -32
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/compensation.py +77 -15
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/execution.py +141 -125
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/recovery.py +29 -10
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/saga.py +49 -19
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/python_cqrs.egg-info/PKG-INFO +68 -26
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/python_cqrs.egg-info/SOURCES.txt +2 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/python_cqrs.egg-info/requires.txt +5 -0
- python_cqrs-4.13.0/src/cqrs/container/__init__.py +0 -3
- python_cqrs-4.13.0/src/cqrs/container/di.py +0 -26
- python_cqrs-4.13.0/src/cqrs/container/protocol.py +0 -20
- python_cqrs-4.13.0/src/cqrs/dispatcher/event.py +0 -96
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/LICENSE +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/setup.cfg +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/adapters/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/adapters/amqp.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/adapters/circuit_breaker.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/adapters/kafka.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/adapters/protocol.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/circuit_breaker.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/compressors/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/compressors/protocol.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/compressors/zlib.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/container/dependency_injector.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/deserializers/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/deserializers/exceptions.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/deserializers/json.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/dispatcher/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/dispatcher/exceptions.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/dispatcher/models.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/event.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/event_handler.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/event_processor.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/fallback.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/events/map.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/generic_utils.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/message_brokers/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/message_brokers/amqp.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/message_brokers/devnull.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/message_brokers/kafka.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/message_brokers/protocol.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/middlewares/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/middlewares/base.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/middlewares/logging.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/outbox/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/outbox/map.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/outbox/mock.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/outbox/repository.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/outbox/sqlalchemy.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/producer.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/cor_request_handler.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/fallback.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/map.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/mermaid.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/request.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/requests/request_handler.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/response.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/circuit_breaker.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/fallback.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/mermaid.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/models.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/step.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/storage/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/storage/enums.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/storage/memory.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/storage/models.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/storage/protocol.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/storage/sqlalchemy.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/saga/validation.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/serializers/__init__.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/serializers/default.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/sqlalchemy_types.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/cqrs/types.py +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
- {python_cqrs-4.13.0 → python_cqrs-4.14.0}/src/python_cqrs.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-cqrs
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.14.0
|
|
4
4
|
Summary: Event-Driven Architecture Framework for Distributed Systems
|
|
5
5
|
Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>, Dmitry Kutlubaev <kutlubaev00@mail.ru>
|
|
6
6
|
Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
|
|
@@ -48,6 +48,7 @@ Requires-Dist: asyncmy==0.2.9; extra == "dev"
|
|
|
48
48
|
Requires-Dist: asyncpg>=0.29.0; extra == "dev"
|
|
49
49
|
Requires-Dist: redis>=5.0.0; extra == "dev"
|
|
50
50
|
Requires-Dist: aiobreaker>=0.3.0; extra == "dev"
|
|
51
|
+
Requires-Dist: dishka>=1.0; extra == "dev"
|
|
51
52
|
Provides-Extra: examples
|
|
52
53
|
Requires-Dist: fastapi==0.109.*; extra == "examples"
|
|
53
54
|
Requires-Dist: faststream[kafka]==0.5.28; extra == "examples"
|
|
@@ -56,6 +57,7 @@ Requires-Dist: uvicorn==0.32.0; extra == "examples"
|
|
|
56
57
|
Requires-Dist: aiohttp==3.14.3; extra == "examples"
|
|
57
58
|
Requires-Dist: protobuf>=4.25.8; extra == "examples"
|
|
58
59
|
Requires-Dist: aiosqlite>=0.20.0; extra == "examples"
|
|
60
|
+
Requires-Dist: dishka>=1.0; extra == "examples"
|
|
59
61
|
Provides-Extra: kafka
|
|
60
62
|
Requires-Dist: aiokafka==0.10.0; extra == "kafka"
|
|
61
63
|
Requires-Dist: retry-async==0.1.*; extra == "kafka"
|
|
@@ -63,6 +65,8 @@ Provides-Extra: rabbit
|
|
|
63
65
|
Requires-Dist: aio-pika==9.3.0; extra == "rabbit"
|
|
64
66
|
Provides-Extra: sqlalchemy
|
|
65
67
|
Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlalchemy"
|
|
68
|
+
Provides-Extra: dishka
|
|
69
|
+
Requires-Dist: dishka>=1.0; extra == "dishka"
|
|
66
70
|
Dynamic: license-file
|
|
67
71
|
|
|
68
72
|
<div align="center">
|
|
@@ -117,6 +121,7 @@ Dynamic: license-file
|
|
|
117
121
|
- [Request Handlers](#request-handlers)
|
|
118
122
|
- [Mapping](#mapping)
|
|
119
123
|
- [DI container](#di-container)
|
|
124
|
+
- [Scoped dependencies](#scoped-dependencies)
|
|
120
125
|
- [Bootstrap](#bootstrap)
|
|
121
126
|
- [Saga Pattern](#saga-pattern)
|
|
122
127
|
- [Producing Notification Events](#producing-notification-events)
|
|
@@ -264,7 +269,7 @@ class CustomResponse(cqrs.IResponse):
|
|
|
264
269
|
return cls(result=kwargs["result"], status=kwargs["status"])
|
|
265
270
|
```
|
|
266
271
|
|
|
267
|
-
A complete example can be found in [request_response_types.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/request_response_types.py)
|
|
272
|
+
A complete example can be found in [request_response_types.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/request_response_types.py)
|
|
268
273
|
|
|
269
274
|
## Request Handlers
|
|
270
275
|
|
|
@@ -297,7 +302,7 @@ class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
|
|
|
297
302
|
```
|
|
298
303
|
|
|
299
304
|
A complete example can be found in
|
|
300
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/request_handler.py)
|
|
305
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/request_handler.py)
|
|
301
306
|
|
|
302
307
|
### Query handler
|
|
303
308
|
|
|
@@ -327,7 +332,7 @@ class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryR
|
|
|
327
332
|
```
|
|
328
333
|
|
|
329
334
|
A complete example can be found in
|
|
330
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/request_handler.py)
|
|
335
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/request_handler.py)
|
|
331
336
|
|
|
332
337
|
### Streaming Request Handler
|
|
333
338
|
|
|
@@ -363,7 +368,7 @@ class ProcessFilesCommandHandler(StreamingRequestHandler[ProcessFilesCommand, Fi
|
|
|
363
368
|
```
|
|
364
369
|
|
|
365
370
|
A complete example can be found in
|
|
366
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/streaming_handler_parallel_events.py)
|
|
371
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/streaming/streaming_handler_parallel_events.py)
|
|
367
372
|
|
|
368
373
|
### Chain of Responsibility Request Handler
|
|
369
374
|
|
|
@@ -426,7 +431,7 @@ def payment_mapper(mapper: cqrs.RequestMap) -> None:
|
|
|
426
431
|
```
|
|
427
432
|
|
|
428
433
|
A complete example can be found in
|
|
429
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor_request_handler.py)
|
|
434
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor/cor_request_handler.py)
|
|
430
435
|
|
|
431
436
|
#### Mermaid Diagram Generation
|
|
432
437
|
|
|
@@ -446,7 +451,7 @@ sequence_diagram = generator.sequence()
|
|
|
446
451
|
class_diagram = generator.class_diagram()
|
|
447
452
|
```
|
|
448
453
|
|
|
449
|
-
Complete example: [CoR Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor_mermaid.py)
|
|
454
|
+
Complete example: [CoR Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor/cor_mermaid.py)
|
|
450
455
|
|
|
451
456
|
## Mapping
|
|
452
457
|
|
|
@@ -507,7 +512,11 @@ def saga_mapper(mapper: cqrs.SagaMap) -> None:
|
|
|
507
512
|
Use the following example to set up dependency injection in your command, query and event handlers. This will make
|
|
508
513
|
dependency management simpler.
|
|
509
514
|
|
|
510
|
-
The package supports
|
|
515
|
+
The package supports three DI container libraries:
|
|
516
|
+
|
|
517
|
+
- [`di`](#di-library) (default)
|
|
518
|
+
- [`dependency-injector`](#dependency-injector-library)
|
|
519
|
+
- [dishka](https://github.com/reagento/dishka) via `DishkaCQRSContainer` (`pip install python-cqrs[dishka]`)
|
|
511
520
|
|
|
512
521
|
### di library
|
|
513
522
|
|
|
@@ -536,7 +545,7 @@ def setup_di() -> di.Container:
|
|
|
536
545
|
```
|
|
537
546
|
|
|
538
547
|
A complete example can be found in
|
|
539
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/
|
|
548
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/di/di_basic.py)
|
|
540
549
|
|
|
541
550
|
### dependency-injector library
|
|
542
551
|
|
|
@@ -552,7 +561,8 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|
|
552
561
|
service = providers.Factory(ServiceImplementation)
|
|
553
562
|
|
|
554
563
|
# Create CQRS container adapter
|
|
555
|
-
cqrs_container = DependencyInjectorCQRSContainer(
|
|
564
|
+
cqrs_container = DependencyInjectorCQRSContainer()
|
|
565
|
+
cqrs_container.attach_external_container(ApplicationContainer())
|
|
556
566
|
|
|
557
567
|
# Use with bootstrap
|
|
558
568
|
mediator = bootstrap.bootstrap(
|
|
@@ -563,8 +573,32 @@ mediator = bootstrap.bootstrap(
|
|
|
563
573
|
```
|
|
564
574
|
|
|
565
575
|
Complete examples can be found in:
|
|
566
|
-
- [Simple example](https://github.com/vadikko2/python-cqrs/blob/master/examples/
|
|
567
|
-
- [Practical example with FastAPI](https://github.com/vadikko2/python-cqrs/blob/master/examples/
|
|
576
|
+
- [Simple example](https://github.com/vadikko2/python-cqrs/blob/master/examples/di/dependency_injector_simple.py)
|
|
577
|
+
- [Practical example with FastAPI](https://github.com/vadikko2/python-cqrs/blob/master/examples/di/dependency_injector_practical.py)
|
|
578
|
+
|
|
579
|
+
### Scoped dependencies
|
|
580
|
+
|
|
581
|
+
By default, the container opens and closes a scope **inside** `resolve()`. A generator such as `async def session() -> AsyncIterator[AsyncSession]` therefore finishes **before** `handle`, which is why people inject factories.
|
|
582
|
+
|
|
583
|
+
Pass `scope_strategy=ScopeStrategy.SEND` and that session lives until `mediator.send()` returns — including domain-event handlers that run afterwards.
|
|
584
|
+
|
|
585
|
+
If you do not use generator providers, leave the default `ScopeStrategy.NONE` and change nothing.
|
|
586
|
+
|
|
587
|
+
```python
|
|
588
|
+
mediator = bootstrap.bootstrap(
|
|
589
|
+
di_container=container,
|
|
590
|
+
commands_mapper=commands_mapper,
|
|
591
|
+
scope_strategy=cqrs.ScopeStrategy.SEND,
|
|
592
|
+
)
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Copy-paste SQLAlchemy session + outbox under one SEND scope: the [Tutorial](https://mkdocs.python-cqrs.dev/scoped_dependencies/tutorial/) (`examples/di/scoped_dependencies_sqlalchemy.py`). Minimal `di` UoW: `examples/di/scoped_dependencies_di.py`.
|
|
596
|
+
|
|
597
|
+
- **SEND** — one UoW per `send()` (command, fallback, and domain events).
|
|
598
|
+
- **HANDLER** — a fresh UoW per handler, including fallback (the primary rolls back first).
|
|
599
|
+
- **NONE** — legacy one-shot resolve; the default.
|
|
600
|
+
|
|
601
|
+
`di`'s `scope="request"` is provider lifetime, not a CQRS scope — that is only `scope_strategy=`. Docs: [Scoped Dependencies](https://mkdocs.python-cqrs.dev/scoped_dependencies/).
|
|
568
602
|
|
|
569
603
|
## Bootstrap
|
|
570
604
|
|
|
@@ -729,6 +763,7 @@ await recover_saga(
|
|
|
729
763
|
context_builder=OrderContext,
|
|
730
764
|
container=di_container, # Same container used in bootstrap
|
|
731
765
|
storage=storage,
|
|
766
|
+
scope_strategy=cqrs.ScopeStrategy.SEND, # Match the original run
|
|
732
767
|
)
|
|
733
768
|
|
|
734
769
|
# Access execution history (SagaLog) for monitoring and debugging
|
|
@@ -761,7 +796,7 @@ sequence_diagram = generator.sequence()
|
|
|
761
796
|
class_diagram = generator.class_diagram()
|
|
762
797
|
```
|
|
763
798
|
|
|
764
|
-
Complete example: [Saga Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/saga_mermaid.py)
|
|
799
|
+
Complete example: [Saga Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/saga/saga_mermaid.py)
|
|
765
800
|
|
|
766
801
|
## Producing Notification Events
|
|
767
802
|
|
|
@@ -801,7 +836,7 @@ class JoinMeetingCommandHandler(cqrs.RequestHandler[JoinMeetingCommand, None]):
|
|
|
801
836
|
```
|
|
802
837
|
|
|
803
838
|
A complete example can be found in
|
|
804
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/event_producing.py)
|
|
839
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/event_producing.py)
|
|
805
840
|
|
|
806
841
|
After processing the command/request, if there are any Notification/ECST events,
|
|
807
842
|
the EventEmitter is invoked to produce the events via the message broker.
|
|
@@ -872,10 +907,10 @@ class JoinMeetingCommandHandler(cqrs.RequestHandler[JoinMeetingCommand, None]):
|
|
|
872
907
|
```
|
|
873
908
|
|
|
874
909
|
A complete example can be found in
|
|
875
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/save_events_into_outbox.py)
|
|
910
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/save_events_into_outbox.py)
|
|
876
911
|
|
|
877
912
|
A runnable FastAPI flow (route → command → transactional outbox → publisher stub) is in
|
|
878
|
-
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_outbox.py)
|
|
913
|
+
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/fastapi_outbox.py)
|
|
879
914
|
|
|
880
915
|
> [!TIP]
|
|
881
916
|
> The Outbox table is named `outbox` by default. To rename it, or to attach the model to your own declarative
|
|
@@ -888,9 +923,13 @@ A runnable FastAPI flow (route → command → transactional outbox → publishe
|
|
|
888
923
|
> [Database Support](https://mkdocs.python-cqrs.dev/outbox/databases/) docs for the DDL, the Alembic recipe and
|
|
889
924
|
> how to register a native type for your own database.
|
|
890
925
|
> [!TIP]
|
|
891
|
-
>
|
|
892
|
-
>
|
|
893
|
-
|
|
926
|
+
> For Protobuf events, implement `proto()` / `from_proto()` on the notification
|
|
927
|
+
> event (see [Protobuf messaging](#protobuf-messaging) and the schema assets in
|
|
928
|
+
> [examples/proto/](https://github.com/vadikko2/python-cqrs/tree/master/examples/proto)).
|
|
929
|
+
> Outbox persistence is unchanged — use
|
|
930
|
+
> [save_events_into_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/save_events_into_outbox.py);
|
|
931
|
+
> Protobuf applies when producing to the broker. Full guide:
|
|
932
|
+
> [Protobuf Integration](https://mkdocs.python-cqrs.dev/protobuf/).
|
|
894
933
|
|
|
895
934
|
## Producing Events from Outbox to Kafka
|
|
896
935
|
|
|
@@ -929,7 +968,7 @@ async with session_factory() as session:
|
|
|
929
968
|
```
|
|
930
969
|
|
|
931
970
|
A complete example can be found in
|
|
932
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/kafka_outboxed_event_producing.py)
|
|
971
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/kafka_outboxed_event_producing.py)
|
|
933
972
|
|
|
934
973
|
## Transaction log tailing
|
|
935
974
|
|
|
@@ -972,7 +1011,7 @@ class UserJoinedEventHandler(cqrs.EventHandler[UserJoined]):
|
|
|
972
1011
|
```
|
|
973
1012
|
|
|
974
1013
|
A complete example can be found in
|
|
975
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/domain_event_handler.py)
|
|
1014
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/domain_event_handler.py)
|
|
976
1015
|
|
|
977
1016
|
### Parallel Event Processing
|
|
978
1017
|
|
|
@@ -1003,6 +1042,7 @@ mediator = bootstrap.bootstrap_streaming(
|
|
|
1003
1042
|
> - Set `max_concurrent_event_handlers` to limit the number of simultaneously running event handlers
|
|
1004
1043
|
> - Set `concurrent_event_handle_enable=False` to disable parallel processing and process events sequentially
|
|
1005
1044
|
> - The default value for `max_concurrent_event_handlers` is `10` for `StreamingRequestMediator` and `1` for `RequestMediator`
|
|
1045
|
+
> - `ScopeStrategy.SEND` always uses sequential events (`concurrent=None` → `False`). Passing `concurrent_event_handle_enable=True` with `SEND` raises `ValueError`; use `HANDLER` or `NONE` for parallel events.
|
|
1006
1046
|
|
|
1007
1047
|
## Integration with presentation layers
|
|
1008
1048
|
|
|
@@ -1040,10 +1080,10 @@ async def join_metting(
|
|
|
1040
1080
|
```
|
|
1041
1081
|
|
|
1042
1082
|
A complete example can be found in
|
|
1043
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_integration.py)
|
|
1083
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/fastapi_integration.py)
|
|
1044
1084
|
|
|
1045
1085
|
End-to-end command + outbox sample (business row and outbox event in one transaction, plus a publisher stub):
|
|
1046
|
-
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_outbox.py)
|
|
1086
|
+
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/fastapi_outbox.py)
|
|
1047
1087
|
|
|
1048
1088
|
### Kafka events consuming
|
|
1049
1089
|
|
|
@@ -1091,7 +1131,7 @@ async def hello_world_event_handler(
|
|
|
1091
1131
|
```
|
|
1092
1132
|
|
|
1093
1133
|
A complete example can be found in
|
|
1094
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/kafka_event_consuming.py)
|
|
1134
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/kafka_event_consuming.py)
|
|
1095
1135
|
|
|
1096
1136
|
### FastAPI SSE Streaming
|
|
1097
1137
|
|
|
@@ -1112,7 +1152,7 @@ def streaming_mediator_factory() -> cqrs.StreamingRequestMediator:
|
|
|
1112
1152
|
domain_events_mapper=domain_events_mapper,
|
|
1113
1153
|
message_broker=broker,
|
|
1114
1154
|
max_concurrent_event_handlers=3,
|
|
1115
|
-
concurrent_event_handle_enable=True,
|
|
1155
|
+
concurrent_event_handle_enable=True, # HANDLER/NONE only; SEND forbids this
|
|
1116
1156
|
)
|
|
1117
1157
|
|
|
1118
1158
|
@app.post("/process-files")
|
|
@@ -1139,7 +1179,9 @@ async def process_files_stream(
|
|
|
1139
1179
|
```
|
|
1140
1180
|
|
|
1141
1181
|
A complete example can be found in
|
|
1142
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_sse_streaming.py)
|
|
1182
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/streaming/fastapi_sse_streaming.py)
|
|
1183
|
+
|
|
1184
|
+
If the client disconnects, close the iterator (`aclose()` / `async with aclosing(...)`) so a `SEND` UoW is not held until GC.
|
|
1143
1185
|
|
|
1144
1186
|
## Protobuf messaging
|
|
1145
1187
|
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
- [Request Handlers](#request-handlers)
|
|
51
51
|
- [Mapping](#mapping)
|
|
52
52
|
- [DI container](#di-container)
|
|
53
|
+
- [Scoped dependencies](#scoped-dependencies)
|
|
53
54
|
- [Bootstrap](#bootstrap)
|
|
54
55
|
- [Saga Pattern](#saga-pattern)
|
|
55
56
|
- [Producing Notification Events](#producing-notification-events)
|
|
@@ -197,7 +198,7 @@ class CustomResponse(cqrs.IResponse):
|
|
|
197
198
|
return cls(result=kwargs["result"], status=kwargs["status"])
|
|
198
199
|
```
|
|
199
200
|
|
|
200
|
-
A complete example can be found in [request_response_types.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/request_response_types.py)
|
|
201
|
+
A complete example can be found in [request_response_types.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/request_response_types.py)
|
|
201
202
|
|
|
202
203
|
## Request Handlers
|
|
203
204
|
|
|
@@ -230,7 +231,7 @@ class JoinMeetingCommandHandler(RequestHandler[JoinMeetingCommand, None]):
|
|
|
230
231
|
```
|
|
231
232
|
|
|
232
233
|
A complete example can be found in
|
|
233
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/request_handler.py)
|
|
234
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/request_handler.py)
|
|
234
235
|
|
|
235
236
|
### Query handler
|
|
236
237
|
|
|
@@ -260,7 +261,7 @@ class ReadMeetingQueryHandler(RequestHandler[ReadMeetingQuery, ReadMeetingQueryR
|
|
|
260
261
|
```
|
|
261
262
|
|
|
262
263
|
A complete example can be found in
|
|
263
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/request_handler.py)
|
|
264
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/request_handler.py)
|
|
264
265
|
|
|
265
266
|
### Streaming Request Handler
|
|
266
267
|
|
|
@@ -296,7 +297,7 @@ class ProcessFilesCommandHandler(StreamingRequestHandler[ProcessFilesCommand, Fi
|
|
|
296
297
|
```
|
|
297
298
|
|
|
298
299
|
A complete example can be found in
|
|
299
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/streaming_handler_parallel_events.py)
|
|
300
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/streaming/streaming_handler_parallel_events.py)
|
|
300
301
|
|
|
301
302
|
### Chain of Responsibility Request Handler
|
|
302
303
|
|
|
@@ -359,7 +360,7 @@ def payment_mapper(mapper: cqrs.RequestMap) -> None:
|
|
|
359
360
|
```
|
|
360
361
|
|
|
361
362
|
A complete example can be found in
|
|
362
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor_request_handler.py)
|
|
363
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor/cor_request_handler.py)
|
|
363
364
|
|
|
364
365
|
#### Mermaid Diagram Generation
|
|
365
366
|
|
|
@@ -379,7 +380,7 @@ sequence_diagram = generator.sequence()
|
|
|
379
380
|
class_diagram = generator.class_diagram()
|
|
380
381
|
```
|
|
381
382
|
|
|
382
|
-
Complete example: [CoR Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor_mermaid.py)
|
|
383
|
+
Complete example: [CoR Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/cor/cor_mermaid.py)
|
|
383
384
|
|
|
384
385
|
## Mapping
|
|
385
386
|
|
|
@@ -440,7 +441,11 @@ def saga_mapper(mapper: cqrs.SagaMap) -> None:
|
|
|
440
441
|
Use the following example to set up dependency injection in your command, query and event handlers. This will make
|
|
441
442
|
dependency management simpler.
|
|
442
443
|
|
|
443
|
-
The package supports
|
|
444
|
+
The package supports three DI container libraries:
|
|
445
|
+
|
|
446
|
+
- [`di`](#di-library) (default)
|
|
447
|
+
- [`dependency-injector`](#dependency-injector-library)
|
|
448
|
+
- [dishka](https://github.com/reagento/dishka) via `DishkaCQRSContainer` (`pip install python-cqrs[dishka]`)
|
|
444
449
|
|
|
445
450
|
### di library
|
|
446
451
|
|
|
@@ -469,7 +474,7 @@ def setup_di() -> di.Container:
|
|
|
469
474
|
```
|
|
470
475
|
|
|
471
476
|
A complete example can be found in
|
|
472
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/
|
|
477
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/di/di_basic.py)
|
|
473
478
|
|
|
474
479
|
### dependency-injector library
|
|
475
480
|
|
|
@@ -485,7 +490,8 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|
|
485
490
|
service = providers.Factory(ServiceImplementation)
|
|
486
491
|
|
|
487
492
|
# Create CQRS container adapter
|
|
488
|
-
cqrs_container = DependencyInjectorCQRSContainer(
|
|
493
|
+
cqrs_container = DependencyInjectorCQRSContainer()
|
|
494
|
+
cqrs_container.attach_external_container(ApplicationContainer())
|
|
489
495
|
|
|
490
496
|
# Use with bootstrap
|
|
491
497
|
mediator = bootstrap.bootstrap(
|
|
@@ -496,8 +502,32 @@ mediator = bootstrap.bootstrap(
|
|
|
496
502
|
```
|
|
497
503
|
|
|
498
504
|
Complete examples can be found in:
|
|
499
|
-
- [Simple example](https://github.com/vadikko2/python-cqrs/blob/master/examples/
|
|
500
|
-
- [Practical example with FastAPI](https://github.com/vadikko2/python-cqrs/blob/master/examples/
|
|
505
|
+
- [Simple example](https://github.com/vadikko2/python-cqrs/blob/master/examples/di/dependency_injector_simple.py)
|
|
506
|
+
- [Practical example with FastAPI](https://github.com/vadikko2/python-cqrs/blob/master/examples/di/dependency_injector_practical.py)
|
|
507
|
+
|
|
508
|
+
### Scoped dependencies
|
|
509
|
+
|
|
510
|
+
By default, the container opens and closes a scope **inside** `resolve()`. A generator such as `async def session() -> AsyncIterator[AsyncSession]` therefore finishes **before** `handle`, which is why people inject factories.
|
|
511
|
+
|
|
512
|
+
Pass `scope_strategy=ScopeStrategy.SEND` and that session lives until `mediator.send()` returns — including domain-event handlers that run afterwards.
|
|
513
|
+
|
|
514
|
+
If you do not use generator providers, leave the default `ScopeStrategy.NONE` and change nothing.
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
mediator = bootstrap.bootstrap(
|
|
518
|
+
di_container=container,
|
|
519
|
+
commands_mapper=commands_mapper,
|
|
520
|
+
scope_strategy=cqrs.ScopeStrategy.SEND,
|
|
521
|
+
)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Copy-paste SQLAlchemy session + outbox under one SEND scope: the [Tutorial](https://mkdocs.python-cqrs.dev/scoped_dependencies/tutorial/) (`examples/di/scoped_dependencies_sqlalchemy.py`). Minimal `di` UoW: `examples/di/scoped_dependencies_di.py`.
|
|
525
|
+
|
|
526
|
+
- **SEND** — one UoW per `send()` (command, fallback, and domain events).
|
|
527
|
+
- **HANDLER** — a fresh UoW per handler, including fallback (the primary rolls back first).
|
|
528
|
+
- **NONE** — legacy one-shot resolve; the default.
|
|
529
|
+
|
|
530
|
+
`di`'s `scope="request"` is provider lifetime, not a CQRS scope — that is only `scope_strategy=`. Docs: [Scoped Dependencies](https://mkdocs.python-cqrs.dev/scoped_dependencies/).
|
|
501
531
|
|
|
502
532
|
## Bootstrap
|
|
503
533
|
|
|
@@ -662,6 +692,7 @@ await recover_saga(
|
|
|
662
692
|
context_builder=OrderContext,
|
|
663
693
|
container=di_container, # Same container used in bootstrap
|
|
664
694
|
storage=storage,
|
|
695
|
+
scope_strategy=cqrs.ScopeStrategy.SEND, # Match the original run
|
|
665
696
|
)
|
|
666
697
|
|
|
667
698
|
# Access execution history (SagaLog) for monitoring and debugging
|
|
@@ -694,7 +725,7 @@ sequence_diagram = generator.sequence()
|
|
|
694
725
|
class_diagram = generator.class_diagram()
|
|
695
726
|
```
|
|
696
727
|
|
|
697
|
-
Complete example: [Saga Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/saga_mermaid.py)
|
|
728
|
+
Complete example: [Saga Mermaid Diagrams](https://github.com/vadikko2/python-cqrs/blob/master/examples/saga/saga_mermaid.py)
|
|
698
729
|
|
|
699
730
|
## Producing Notification Events
|
|
700
731
|
|
|
@@ -734,7 +765,7 @@ class JoinMeetingCommandHandler(cqrs.RequestHandler[JoinMeetingCommand, None]):
|
|
|
734
765
|
```
|
|
735
766
|
|
|
736
767
|
A complete example can be found in
|
|
737
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/event_producing.py)
|
|
768
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/event_producing.py)
|
|
738
769
|
|
|
739
770
|
After processing the command/request, if there are any Notification/ECST events,
|
|
740
771
|
the EventEmitter is invoked to produce the events via the message broker.
|
|
@@ -805,10 +836,10 @@ class JoinMeetingCommandHandler(cqrs.RequestHandler[JoinMeetingCommand, None]):
|
|
|
805
836
|
```
|
|
806
837
|
|
|
807
838
|
A complete example can be found in
|
|
808
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/save_events_into_outbox.py)
|
|
839
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/save_events_into_outbox.py)
|
|
809
840
|
|
|
810
841
|
A runnable FastAPI flow (route → command → transactional outbox → publisher stub) is in
|
|
811
|
-
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_outbox.py)
|
|
842
|
+
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/fastapi_outbox.py)
|
|
812
843
|
|
|
813
844
|
> [!TIP]
|
|
814
845
|
> The Outbox table is named `outbox` by default. To rename it, or to attach the model to your own declarative
|
|
@@ -821,9 +852,13 @@ A runnable FastAPI flow (route → command → transactional outbox → publishe
|
|
|
821
852
|
> [Database Support](https://mkdocs.python-cqrs.dev/outbox/databases/) docs for the DDL, the Alembic recipe and
|
|
822
853
|
> how to register a native type for your own database.
|
|
823
854
|
> [!TIP]
|
|
824
|
-
>
|
|
825
|
-
>
|
|
826
|
-
|
|
855
|
+
> For Protobuf events, implement `proto()` / `from_proto()` on the notification
|
|
856
|
+
> event (see [Protobuf messaging](#protobuf-messaging) and the schema assets in
|
|
857
|
+
> [examples/proto/](https://github.com/vadikko2/python-cqrs/tree/master/examples/proto)).
|
|
858
|
+
> Outbox persistence is unchanged — use
|
|
859
|
+
> [save_events_into_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/save_events_into_outbox.py);
|
|
860
|
+
> Protobuf applies when producing to the broker. Full guide:
|
|
861
|
+
> [Protobuf Integration](https://mkdocs.python-cqrs.dev/protobuf/).
|
|
827
862
|
|
|
828
863
|
## Producing Events from Outbox to Kafka
|
|
829
864
|
|
|
@@ -862,7 +897,7 @@ async with session_factory() as session:
|
|
|
862
897
|
```
|
|
863
898
|
|
|
864
899
|
A complete example can be found in
|
|
865
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/kafka_outboxed_event_producing.py)
|
|
900
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/kafka_outboxed_event_producing.py)
|
|
866
901
|
|
|
867
902
|
## Transaction log tailing
|
|
868
903
|
|
|
@@ -905,7 +940,7 @@ class UserJoinedEventHandler(cqrs.EventHandler[UserJoined]):
|
|
|
905
940
|
```
|
|
906
941
|
|
|
907
942
|
A complete example can be found in
|
|
908
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/domain_event_handler.py)
|
|
943
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/domain_event_handler.py)
|
|
909
944
|
|
|
910
945
|
### Parallel Event Processing
|
|
911
946
|
|
|
@@ -936,6 +971,7 @@ mediator = bootstrap.bootstrap_streaming(
|
|
|
936
971
|
> - Set `max_concurrent_event_handlers` to limit the number of simultaneously running event handlers
|
|
937
972
|
> - Set `concurrent_event_handle_enable=False` to disable parallel processing and process events sequentially
|
|
938
973
|
> - The default value for `max_concurrent_event_handlers` is `10` for `StreamingRequestMediator` and `1` for `RequestMediator`
|
|
974
|
+
> - `ScopeStrategy.SEND` always uses sequential events (`concurrent=None` → `False`). Passing `concurrent_event_handle_enable=True` with `SEND` raises `ValueError`; use `HANDLER` or `NONE` for parallel events.
|
|
939
975
|
|
|
940
976
|
## Integration with presentation layers
|
|
941
977
|
|
|
@@ -973,10 +1009,10 @@ async def join_metting(
|
|
|
973
1009
|
```
|
|
974
1010
|
|
|
975
1011
|
A complete example can be found in
|
|
976
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_integration.py)
|
|
1012
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/fastapi_integration.py)
|
|
977
1013
|
|
|
978
1014
|
End-to-end command + outbox sample (business row and outbox event in one transaction, plus a publisher stub):
|
|
979
|
-
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_outbox.py)
|
|
1015
|
+
[fastapi_outbox.py](https://github.com/vadikko2/python-cqrs/blob/master/examples/outbox/fastapi_outbox.py)
|
|
980
1016
|
|
|
981
1017
|
### Kafka events consuming
|
|
982
1018
|
|
|
@@ -1024,7 +1060,7 @@ async def hello_world_event_handler(
|
|
|
1024
1060
|
```
|
|
1025
1061
|
|
|
1026
1062
|
A complete example can be found in
|
|
1027
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/kafka_event_consuming.py)
|
|
1063
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/events/kafka_event_consuming.py)
|
|
1028
1064
|
|
|
1029
1065
|
### FastAPI SSE Streaming
|
|
1030
1066
|
|
|
@@ -1045,7 +1081,7 @@ def streaming_mediator_factory() -> cqrs.StreamingRequestMediator:
|
|
|
1045
1081
|
domain_events_mapper=domain_events_mapper,
|
|
1046
1082
|
message_broker=broker,
|
|
1047
1083
|
max_concurrent_event_handlers=3,
|
|
1048
|
-
concurrent_event_handle_enable=True,
|
|
1084
|
+
concurrent_event_handle_enable=True, # HANDLER/NONE only; SEND forbids this
|
|
1049
1085
|
)
|
|
1050
1086
|
|
|
1051
1087
|
@app.post("/process-files")
|
|
@@ -1072,7 +1108,9 @@ async def process_files_stream(
|
|
|
1072
1108
|
```
|
|
1073
1109
|
|
|
1074
1110
|
A complete example can be found in
|
|
1075
|
-
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/fastapi_sse_streaming.py)
|
|
1111
|
+
the [documentation](https://github.com/vadikko2/python-cqrs/blob/master/examples/streaming/fastapi_sse_streaming.py)
|
|
1112
|
+
|
|
1113
|
+
If the client disconnects, close the iterator (`aclose()` / `async with aclosing(...)`) so a `SEND` UoW is not held until GC.
|
|
1076
1114
|
|
|
1077
1115
|
## Protobuf messaging
|
|
1078
1116
|
|
|
@@ -31,7 +31,7 @@ maintainers = [{ name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru" }]
|
|
|
31
31
|
name = "python-cqrs"
|
|
32
32
|
readme = "README.md"
|
|
33
33
|
requires-python = ">=3.10"
|
|
34
|
-
version = "4.
|
|
34
|
+
version = "4.14.0"
|
|
35
35
|
|
|
36
36
|
[project.optional-dependencies]
|
|
37
37
|
aiobreaker = ["aiobreaker>=0.3.0"]
|
|
@@ -59,6 +59,7 @@ dev = [
|
|
|
59
59
|
"redis>=5.0.0",
|
|
60
60
|
# Circuit breaker for tests
|
|
61
61
|
"aiobreaker>=0.3.0", # from aiobreaker
|
|
62
|
+
"dishka>=1.0", # scoped-deps dishka adapter tests / examples
|
|
62
63
|
]
|
|
63
64
|
examples = [
|
|
64
65
|
"fastapi==0.109.*",
|
|
@@ -68,10 +69,12 @@ examples = [
|
|
|
68
69
|
"aiohttp==3.14.3",
|
|
69
70
|
"protobuf>=4.25.8",
|
|
70
71
|
"aiosqlite>=0.20.0",
|
|
72
|
+
"dishka>=1.0",
|
|
71
73
|
]
|
|
72
74
|
kafka = ["aiokafka==0.10.0", "retry-async==0.1.*"]
|
|
73
75
|
rabbit = ["aio-pika==9.3.0"]
|
|
74
76
|
sqlalchemy = ["sqlalchemy[asyncio]==2.0.*"]
|
|
77
|
+
dishka = ["dishka>=1.0"]
|
|
75
78
|
|
|
76
79
|
[project.urls]
|
|
77
80
|
Documentation = "https://mkdocs.python-cqrs.dev/"
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
from cqrs.compressors import Compressor, ZlibCompressor
|
|
2
2
|
from cqrs.container.di import DIContainer
|
|
3
|
-
from cqrs.container.protocol import Container
|
|
3
|
+
from cqrs.container.protocol import Container, SupportsScope
|
|
4
|
+
from cqrs.container.scope import (
|
|
5
|
+
ScopeAwareContainer,
|
|
6
|
+
ScopeStrategy,
|
|
7
|
+
bind_scope,
|
|
8
|
+
current_container,
|
|
9
|
+
enter_scope,
|
|
10
|
+
)
|
|
4
11
|
from cqrs.circuit_breaker import ICircuitBreaker
|
|
5
12
|
from cqrs.events import EventMap
|
|
6
13
|
from cqrs.events.fallback import EventHandlerFallback
|
|
@@ -105,7 +112,13 @@ __all__ = (
|
|
|
105
112
|
"SqlAlchemyOutboxedEventRepository",
|
|
106
113
|
"EventProducer",
|
|
107
114
|
"Container",
|
|
115
|
+
"SupportsScope",
|
|
108
116
|
"DIContainer",
|
|
117
|
+
"ScopeAwareContainer",
|
|
118
|
+
"ScopeStrategy",
|
|
119
|
+
"bind_scope",
|
|
120
|
+
"current_container",
|
|
121
|
+
"enter_scope",
|
|
109
122
|
"Compressor",
|
|
110
123
|
"ZlibCompressor",
|
|
111
124
|
"rebind_outbox_model",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from cqrs.container.protocol import Container, SupportsScope
|
|
2
|
+
from cqrs.container.scope import (
|
|
3
|
+
ScopeAwareContainer,
|
|
4
|
+
ScopeStrategy,
|
|
5
|
+
bind_scope,
|
|
6
|
+
current_container,
|
|
7
|
+
enter_scope,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
"Container",
|
|
12
|
+
"SupportsScope",
|
|
13
|
+
"ScopeAwareContainer",
|
|
14
|
+
"ScopeStrategy",
|
|
15
|
+
"bind_scope",
|
|
16
|
+
"current_container",
|
|
17
|
+
"enter_scope",
|
|
18
|
+
)
|