eventsourcing 9.4.0a5__tar.gz → 9.4.0a6__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.
Potentially problematic release.
This version of eventsourcing might be problematic. Click here for more details.
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/PKG-INFO +2 -4
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/application.py +21 -27
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/domain.py +49 -51
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/interface.py +5 -2
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/persistence.py +29 -36
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/popo.py +17 -16
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/postgres.py +18 -17
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/projection.py +8 -17
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/sqlite.py +18 -17
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/system.py +49 -62
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/tests/application.py +3 -3
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/tests/persistence.py +12 -12
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/utils.py +8 -20
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/pyproject.toml +6 -12
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/AUTHORS +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/LICENSE +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/README.md +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/__init__.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/cipher.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/compressor.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/cryptography.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/dispatch.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/py.typed +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/tests/__init__.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/tests/domain.py +0 -0
- {eventsourcing-9.4.0a5 → eventsourcing-9.4.0a6}/eventsourcing/tests/postgres_utils.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: eventsourcing
|
|
3
|
-
Version: 9.4.
|
|
3
|
+
Version: 9.4.0a6
|
|
4
4
|
Summary: Event sourcing in Python
|
|
5
5
|
License: BSD 3-Clause
|
|
6
6
|
Keywords: event sourcing,event store,domain driven design,domain-driven design,ddd,cqrs,cqs
|
|
7
7
|
Author: John Bywater
|
|
8
8
|
Author-email: john.bywater@appropriatesoftware.net
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9, !=2.7.*, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Intended Audience :: Education
|
|
@@ -16,7 +16,6 @@ Classifier: License :: Other/Proprietary License
|
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -26,7 +25,6 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
26
25
|
Provides-Extra: crypto
|
|
27
26
|
Provides-Extra: cryptography
|
|
28
27
|
Provides-Extra: postgres
|
|
29
|
-
Requires-Dist: backports.zoneinfo ; python_version < "3.9"
|
|
30
28
|
Requires-Dist: cryptography (>=44.0,<44.1) ; extra == "cryptography"
|
|
31
29
|
Requires-Dist: psycopg[pool] (<=3.2.99999) ; extra == "postgres"
|
|
32
30
|
Requires-Dist: pycryptodome (>=3.22,<3.23) ; extra == "crypto"
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
5
6
|
from copy import deepcopy
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from itertools import chain
|
|
@@ -11,15 +12,8 @@ from typing import (
|
|
|
11
12
|
Any,
|
|
12
13
|
Callable,
|
|
13
14
|
ClassVar,
|
|
14
|
-
Dict,
|
|
15
15
|
Generic,
|
|
16
|
-
Iterable,
|
|
17
|
-
Iterator,
|
|
18
|
-
List,
|
|
19
16
|
Optional,
|
|
20
|
-
Sequence,
|
|
21
|
-
Tuple,
|
|
22
|
-
Type,
|
|
23
17
|
TypeVar,
|
|
24
18
|
cast,
|
|
25
19
|
)
|
|
@@ -96,7 +90,7 @@ T = TypeVar("T")
|
|
|
96
90
|
|
|
97
91
|
class Cache(Generic[S, T]):
|
|
98
92
|
def __init__(self) -> None:
|
|
99
|
-
self.cache:
|
|
93
|
+
self.cache: dict[S, Any] = {}
|
|
100
94
|
|
|
101
95
|
def get(self, key: S, *, evict: bool = False) -> T:
|
|
102
96
|
if evict:
|
|
@@ -127,7 +121,7 @@ class LRUCache(Cache[S, T]):
|
|
|
127
121
|
self.maxsize = maxsize
|
|
128
122
|
self.full = False
|
|
129
123
|
self.lock = Lock() # because linkedlist updates aren't threadsafe
|
|
130
|
-
self.root:
|
|
124
|
+
self.root: list[Any] = [] # root of the circular doubly linked list
|
|
131
125
|
self.clear()
|
|
132
126
|
|
|
133
127
|
def clear(self) -> None:
|
|
@@ -253,7 +247,7 @@ class Repository:
|
|
|
253
247
|
self._fastforward_locks_cache: LRUCache[UUID, Lock] = LRUCache(
|
|
254
248
|
maxsize=self.FASTFORWARD_LOCKS_CACHE_MAXSIZE
|
|
255
249
|
)
|
|
256
|
-
self._fastforward_locks_inuse:
|
|
250
|
+
self._fastforward_locks_inuse: dict[UUID, tuple[Lock, int]] = {}
|
|
257
251
|
|
|
258
252
|
def get(
|
|
259
253
|
self,
|
|
@@ -415,12 +409,12 @@ class Section:
|
|
|
415
409
|
Constructor arguments:
|
|
416
410
|
|
|
417
411
|
:param Optional[str] id: section ID of this section e.g. "1,10"
|
|
418
|
-
:param
|
|
412
|
+
:param list[Notification] items: a list of event notifications
|
|
419
413
|
:param Optional[str] next_id: section ID of the following section
|
|
420
414
|
"""
|
|
421
415
|
|
|
422
416
|
id: str | None
|
|
423
|
-
items:
|
|
417
|
+
items: list[Notification]
|
|
424
418
|
next_id: str | None
|
|
425
419
|
|
|
426
420
|
|
|
@@ -446,7 +440,7 @@ class NotificationLog(ABC):
|
|
|
446
440
|
topics: Sequence[str] = (),
|
|
447
441
|
*,
|
|
448
442
|
inclusive_of_start: bool = True,
|
|
449
|
-
) ->
|
|
443
|
+
) -> list[Notification]:
|
|
450
444
|
"""
|
|
451
445
|
Returns a selection of
|
|
452
446
|
:class:`~eventsourcing.persistence.Notification` objects
|
|
@@ -536,7 +530,7 @@ class LocalNotificationLog(NotificationLog):
|
|
|
536
530
|
topics: Sequence[str] = (),
|
|
537
531
|
*,
|
|
538
532
|
inclusive_of_start: bool = True,
|
|
539
|
-
) ->
|
|
533
|
+
) -> list[Notification]:
|
|
540
534
|
"""
|
|
541
535
|
Returns a selection of
|
|
542
536
|
:class:`~eventsourcing.persistence.Notification` objects
|
|
@@ -573,9 +567,9 @@ class ProcessingEvent:
|
|
|
573
567
|
Initialises the process event with the given tracking object.
|
|
574
568
|
"""
|
|
575
569
|
self.tracking = tracking
|
|
576
|
-
self.events:
|
|
577
|
-
self.aggregates:
|
|
578
|
-
self.saved_kwargs:
|
|
570
|
+
self.events: list[DomainEventProtocol] = []
|
|
571
|
+
self.aggregates: dict[UUID, MutableOrImmutableAggregate] = {}
|
|
572
|
+
self.saved_kwargs: dict[Any, Any] = {}
|
|
579
573
|
|
|
580
574
|
def collect_events(
|
|
581
575
|
self,
|
|
@@ -618,15 +612,15 @@ class Application:
|
|
|
618
612
|
"""
|
|
619
613
|
|
|
620
614
|
name = "Application"
|
|
621
|
-
env: ClassVar[
|
|
615
|
+
env: ClassVar[dict[str, str]] = {}
|
|
622
616
|
is_snapshotting_enabled: bool = False
|
|
623
617
|
snapshotting_intervals: ClassVar[
|
|
624
|
-
|
|
618
|
+
dict[type[MutableOrImmutableAggregate], int] | None
|
|
625
619
|
] = None
|
|
626
620
|
snapshotting_projectors: ClassVar[
|
|
627
|
-
|
|
621
|
+
dict[type[MutableOrImmutableAggregate], ProjectorFunction[Any, Any]] | None
|
|
628
622
|
] = None
|
|
629
|
-
snapshot_class:
|
|
623
|
+
snapshot_class: type[SnapshotProtocol] = Snapshot
|
|
630
624
|
log_section_size = 10
|
|
631
625
|
notify_topics: Sequence[str] = []
|
|
632
626
|
|
|
@@ -792,7 +786,7 @@ class Application:
|
|
|
792
786
|
self,
|
|
793
787
|
*objs: MutableOrImmutableAggregate | DomainEventProtocol | None,
|
|
794
788
|
**kwargs: Any,
|
|
795
|
-
) ->
|
|
789
|
+
) -> list[Recording]:
|
|
796
790
|
"""
|
|
797
791
|
Collects pending events from given aggregates and
|
|
798
792
|
puts them in the application's event store.
|
|
@@ -805,7 +799,7 @@ class Application:
|
|
|
805
799
|
self.notify(processing_event.events) # Deprecated.
|
|
806
800
|
return recordings
|
|
807
801
|
|
|
808
|
-
def _record(self, processing_event: ProcessingEvent) ->
|
|
802
|
+
def _record(self, processing_event: ProcessingEvent) -> list[Recording]:
|
|
809
803
|
"""
|
|
810
804
|
Records given process event in the application's recorder.
|
|
811
805
|
"""
|
|
@@ -885,7 +879,7 @@ class Application:
|
|
|
885
879
|
snapshot = snapshot_class.take(aggregate)
|
|
886
880
|
self.snapshots.put([snapshot])
|
|
887
881
|
|
|
888
|
-
def notify(self, new_events:
|
|
882
|
+
def notify(self, new_events: list[DomainEventProtocol]) -> None:
|
|
889
883
|
"""
|
|
890
884
|
Deprecated.
|
|
891
885
|
|
|
@@ -895,7 +889,7 @@ class Application:
|
|
|
895
889
|
need to take action when new domain events have been saved.
|
|
896
890
|
"""
|
|
897
891
|
|
|
898
|
-
def _notify(self, recordings:
|
|
892
|
+
def _notify(self, recordings: list[Recording]) -> None:
|
|
899
893
|
"""
|
|
900
894
|
Called after new aggregate events have been saved. This
|
|
901
895
|
method on this class doesn't actually do anything,
|
|
@@ -944,7 +938,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
944
938
|
self,
|
|
945
939
|
events: EventStore,
|
|
946
940
|
originator_id: UUID,
|
|
947
|
-
logged_cls:
|
|
941
|
+
logged_cls: type[TDomainEvent], # TODO: Rename to 'event_class' in v10.
|
|
948
942
|
):
|
|
949
943
|
self.events = events
|
|
950
944
|
self.originator_id = originator_id
|
|
@@ -966,7 +960,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
966
960
|
|
|
967
961
|
def _trigger_event(
|
|
968
962
|
self,
|
|
969
|
-
logged_cls:
|
|
963
|
+
logged_cls: type[T] | None,
|
|
970
964
|
next_originator_version: int | None = None,
|
|
971
965
|
**kwargs: Any,
|
|
972
966
|
) -> T:
|
|
@@ -4,20 +4,14 @@ import inspect
|
|
|
4
4
|
import os
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from datetime import datetime, tzinfo
|
|
7
|
-
from functools import
|
|
7
|
+
from functools import cache
|
|
8
8
|
from types import FunctionType, WrapperDescriptorType
|
|
9
9
|
from typing import (
|
|
10
10
|
TYPE_CHECKING,
|
|
11
11
|
Any,
|
|
12
12
|
Callable,
|
|
13
|
-
Dict,
|
|
14
13
|
Generic,
|
|
15
|
-
Iterable,
|
|
16
|
-
List,
|
|
17
14
|
Protocol,
|
|
18
|
-
Sequence,
|
|
19
|
-
Tuple,
|
|
20
|
-
Type,
|
|
21
15
|
TypeVar,
|
|
22
16
|
Union,
|
|
23
17
|
cast,
|
|
@@ -29,6 +23,10 @@ from warnings import warn
|
|
|
29
23
|
|
|
30
24
|
from eventsourcing.utils import get_method_name, get_topic, resolve_topic
|
|
31
25
|
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Iterable, Sequence
|
|
28
|
+
|
|
29
|
+
|
|
32
30
|
TZINFO: tzinfo = resolve_topic(os.getenv("TZINFO_TOPIC", "datetime:timezone.utc"))
|
|
33
31
|
"""
|
|
34
32
|
A Python :py:obj:`tzinfo` object that defaults to UTC (:py:obj:`timezone.utc`). Used
|
|
@@ -294,7 +292,7 @@ class CanInitAggregate(CanMutateAggregate):
|
|
|
294
292
|
assert aggregate is None
|
|
295
293
|
|
|
296
294
|
# Resolve originator topic.
|
|
297
|
-
aggregate_class:
|
|
295
|
+
aggregate_class: type[TAggregate] = resolve_topic(self.originator_topic)
|
|
298
296
|
|
|
299
297
|
# Construct an aggregate object (a "shell" of the correct object type).
|
|
300
298
|
agg = aggregate_class.__new__(aggregate_class)
|
|
@@ -333,10 +331,10 @@ class MetaDomainEvent(type):
|
|
|
333
331
|
"""
|
|
334
332
|
|
|
335
333
|
def __new__(
|
|
336
|
-
cls, name: str, bases:
|
|
337
|
-
) ->
|
|
334
|
+
cls, name: str, bases: tuple[type[TDomainEvent], ...], cls_dict: dict[str, Any]
|
|
335
|
+
) -> type[TDomainEvent]:
|
|
338
336
|
event_cls = cast(
|
|
339
|
-
|
|
337
|
+
type[TDomainEvent], super().__new__(cls, name, bases, cls_dict)
|
|
340
338
|
)
|
|
341
339
|
event_cls = dataclass(frozen=True)(event_cls)
|
|
342
340
|
event_cls.__signature__ = inspect.signature(event_cls.__init__) # type: ignore
|
|
@@ -398,20 +396,20 @@ TLogEvent = TypeVar("TLogEvent", bound=DomainEventProtocol)
|
|
|
398
396
|
|
|
399
397
|
|
|
400
398
|
def _filter_kwargs_for_method_params(
|
|
401
|
-
kwargs:
|
|
402
|
-
) ->
|
|
399
|
+
kwargs: dict[str, Any], method: Callable[..., Any]
|
|
400
|
+
) -> dict[str, Any]:
|
|
403
401
|
names = _spec_filter_kwargs_for_method_params(method)
|
|
404
402
|
return {k: v for k, v in kwargs.items() if k in names}
|
|
405
403
|
|
|
406
404
|
|
|
407
|
-
@
|
|
405
|
+
@cache
|
|
408
406
|
def _spec_filter_kwargs_for_method_params(method: Callable[..., Any]) -> set[str]:
|
|
409
407
|
method_signature = inspect.signature(method)
|
|
410
408
|
return set(method_signature.parameters)
|
|
411
409
|
|
|
412
410
|
|
|
413
411
|
if TYPE_CHECKING:
|
|
414
|
-
EventSpecType = Union[str,
|
|
412
|
+
EventSpecType = Union[str, type[CanMutateAggregate]]
|
|
415
413
|
|
|
416
414
|
CommandMethod = Callable[..., None]
|
|
417
415
|
DecoratedObjType = Union[CommandMethod, property]
|
|
@@ -425,7 +423,7 @@ class CommandMethodDecorator:
|
|
|
425
423
|
decorated_obj: DecoratedObjType,
|
|
426
424
|
):
|
|
427
425
|
self.is_name_inferred_from_method = False
|
|
428
|
-
self.given_event_cls:
|
|
426
|
+
self.given_event_cls: type[CanMutateAggregate] | None = None
|
|
429
427
|
self.event_cls_name: str | None = None
|
|
430
428
|
self.decorated_property: property | None = None
|
|
431
429
|
self.is_property_setter = False
|
|
@@ -740,12 +738,12 @@ class BoundCommandMethodDecorator:
|
|
|
740
738
|
|
|
741
739
|
|
|
742
740
|
given_event_classes: set[type] = set()
|
|
743
|
-
decorated_methods:
|
|
744
|
-
aggregate_has_many_created_event_classes:
|
|
741
|
+
decorated_methods: dict[type, CommandMethod] = {}
|
|
742
|
+
aggregate_has_many_created_event_classes: dict[type, list[str]] = {}
|
|
745
743
|
|
|
746
744
|
|
|
747
|
-
decorated_event_classes:
|
|
748
|
-
CommandMethodDecorator,
|
|
745
|
+
decorated_event_classes: dict[
|
|
746
|
+
CommandMethodDecorator, type[MetaAggregate.DecoratedEvent]
|
|
749
747
|
] = {}
|
|
750
748
|
|
|
751
749
|
|
|
@@ -767,10 +765,10 @@ def _check_no_variable_params(method: FunctionType) -> None:
|
|
|
767
765
|
def _coerce_args_to_kwargs(
|
|
768
766
|
method: FunctionType | WrapperDescriptorType,
|
|
769
767
|
args: Iterable[Any],
|
|
770
|
-
kwargs:
|
|
768
|
+
kwargs: dict[str, Any],
|
|
771
769
|
*,
|
|
772
770
|
expects_id: bool = False,
|
|
773
|
-
) ->
|
|
771
|
+
) -> dict[str, Any]:
|
|
774
772
|
assert isinstance(method, (FunctionType, WrapperDescriptorType))
|
|
775
773
|
|
|
776
774
|
args = tuple(args)
|
|
@@ -789,14 +787,14 @@ def _coerce_args_to_kwargs(
|
|
|
789
787
|
return copy_kwargs
|
|
790
788
|
|
|
791
789
|
|
|
792
|
-
@
|
|
790
|
+
@cache
|
|
793
791
|
def _spec_coerce_args_to_kwargs(
|
|
794
792
|
method: FunctionType | WrapperDescriptorType,
|
|
795
793
|
len_args: int,
|
|
796
|
-
kwargs_keys:
|
|
794
|
+
kwargs_keys: tuple[str],
|
|
797
795
|
*,
|
|
798
796
|
expects_id: bool,
|
|
799
|
-
) ->
|
|
797
|
+
) -> tuple[tuple[tuple[int, str], ...], tuple[tuple[str, Any], ...]]:
|
|
800
798
|
method_signature = inspect.signature(method)
|
|
801
799
|
positional_names = []
|
|
802
800
|
keyword_defaults = {}
|
|
@@ -874,7 +872,7 @@ def _spec_coerce_args_to_kwargs(
|
|
|
874
872
|
return enumerated_args_names, keyword_defaults_items
|
|
875
873
|
|
|
876
874
|
|
|
877
|
-
def _raise_missing_names_type_error(missing_names:
|
|
875
|
+
def _raise_missing_names_type_error(missing_names: list[str], msg: str) -> None:
|
|
878
876
|
msg += missing_names[0]
|
|
879
877
|
if len(missing_names) == 2:
|
|
880
878
|
msg += f" and {missing_names[1]}"
|
|
@@ -920,7 +918,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
920
918
|
# Call the original method with event attribute values.
|
|
921
919
|
decorated_method(aggregate, **kwargs)
|
|
922
920
|
|
|
923
|
-
_created_event_class:
|
|
921
|
+
_created_event_class: type[CanInitAggregate]
|
|
924
922
|
|
|
925
923
|
def __new__(cls, *args: Any, **_: Any) -> MetaAggregate[Aggregate]:
|
|
926
924
|
"""
|
|
@@ -957,7 +955,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
957
955
|
|
|
958
956
|
# Identify or define a base event class for this aggregate.
|
|
959
957
|
base_event_name = "Event"
|
|
960
|
-
base_event_cls:
|
|
958
|
+
base_event_cls: type[CanMutateAggregate]
|
|
961
959
|
try:
|
|
962
960
|
base_event_cls = cls.__dict__[base_event_name]
|
|
963
961
|
except KeyError:
|
|
@@ -967,7 +965,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
967
965
|
setattr(cls, base_event_name, base_event_cls)
|
|
968
966
|
|
|
969
967
|
# Make sure all events defined on aggregate subclass the base event class.
|
|
970
|
-
created_event_classes:
|
|
968
|
+
created_event_classes: dict[str, type[CanInitAggregate]] = {}
|
|
971
969
|
for name, value in tuple(cls.__dict__.items()):
|
|
972
970
|
if name == base_event_name:
|
|
973
971
|
# Don't subclass the base event class again.
|
|
@@ -987,7 +985,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
987
985
|
created_event_classes[name] = value
|
|
988
986
|
|
|
989
987
|
# Disallow using both '_created_event_class' and 'created_event_name'.
|
|
990
|
-
created_event_class:
|
|
988
|
+
created_event_class: type[CanInitAggregate] | None = cls.__dict__.get(
|
|
991
989
|
"_created_event_class"
|
|
992
990
|
)
|
|
993
991
|
if created_event_class and created_event_name:
|
|
@@ -1015,7 +1013,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1015
1013
|
# Does the decorator specify a "created" event class?
|
|
1016
1014
|
if init_decorator.given_event_cls:
|
|
1017
1015
|
created_event_class = cast(
|
|
1018
|
-
|
|
1016
|
+
type[CanInitAggregate], init_decorator.given_event_cls
|
|
1019
1017
|
)
|
|
1020
1018
|
# Does the decorator specify a "created" event name?
|
|
1021
1019
|
elif init_decorator.event_cls_name:
|
|
@@ -1093,11 +1091,11 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1093
1091
|
# Define a "created" event class for this aggregate.
|
|
1094
1092
|
if issubclass(base_created_event_cls, base_event_cls):
|
|
1095
1093
|
# Don't subclass from base event class twice.
|
|
1096
|
-
bases:
|
|
1094
|
+
bases: tuple[type[CanMutateAggregate], ...] = (base_created_event_cls,)
|
|
1097
1095
|
else:
|
|
1098
1096
|
bases = (base_created_event_cls, base_event_cls)
|
|
1099
1097
|
created_event_class = cast(
|
|
1100
|
-
|
|
1098
|
+
type[CanInitAggregate],
|
|
1101
1099
|
cls._define_event_class(
|
|
1102
1100
|
created_event_name,
|
|
1103
1101
|
bases,
|
|
@@ -1160,7 +1158,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1160
1158
|
|
|
1161
1159
|
# Define event class as subclass of given class.
|
|
1162
1160
|
given_subclass = cast(
|
|
1163
|
-
|
|
1161
|
+
type[CanMutateAggregate],
|
|
1164
1162
|
getattr(cls, event_decorator.given_event_cls.__name__),
|
|
1165
1163
|
)
|
|
1166
1164
|
event_cls = cls._define_event_class(
|
|
@@ -1194,7 +1192,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1194
1192
|
|
|
1195
1193
|
# Remember which event class to trigger.
|
|
1196
1194
|
decorated_event_classes[event_decorator] = cast(
|
|
1197
|
-
|
|
1195
|
+
type[MetaAggregate.DecoratedEvent], event_cls
|
|
1198
1196
|
)
|
|
1199
1197
|
|
|
1200
1198
|
# Check any create_id method defined on this class is static or class method.
|
|
@@ -1208,7 +1206,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1208
1206
|
raise TypeError(msg)
|
|
1209
1207
|
|
|
1210
1208
|
# Get the parameters of the create_id method that will be used by this class.
|
|
1211
|
-
cls._create_id_param_names:
|
|
1209
|
+
cls._create_id_param_names: list[str] = []
|
|
1212
1210
|
for name, param in inspect.signature(cls.create_id).parameters.items():
|
|
1213
1211
|
if param.kind in [param.KEYWORD_ONLY, param.POSITIONAL_OR_KEYWORD]:
|
|
1214
1212
|
cls._create_id_param_names.append(name)
|
|
@@ -1230,9 +1228,9 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1230
1228
|
def _define_event_class(
|
|
1231
1229
|
cls,
|
|
1232
1230
|
name: str,
|
|
1233
|
-
bases:
|
|
1231
|
+
bases: tuple[type[CanMutateAggregate], ...],
|
|
1234
1232
|
apply_method: CommandMethod | None,
|
|
1235
|
-
) ->
|
|
1233
|
+
) -> type[CanMutateAggregate]:
|
|
1236
1234
|
# Define annotations for the event class (specs the init method).
|
|
1237
1235
|
annotations = {}
|
|
1238
1236
|
if apply_method is not None:
|
|
@@ -1256,7 +1254,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1256
1254
|
}
|
|
1257
1255
|
|
|
1258
1256
|
# Create the event class object.
|
|
1259
|
-
return cast(
|
|
1257
|
+
return cast(type[CanMutateAggregate], type(name, bases, event_cls_dict))
|
|
1260
1258
|
|
|
1261
1259
|
def __call__(
|
|
1262
1260
|
cls: MetaAggregate[TAggregate], *args: Any, **kwargs: Any
|
|
@@ -1286,7 +1284,7 @@ class MetaAggregate(type, Generic[TAggregate]):
|
|
|
1286
1284
|
|
|
1287
1285
|
def _create(
|
|
1288
1286
|
cls: MetaAggregate[TAggregate],
|
|
1289
|
-
event_class:
|
|
1287
|
+
event_class: type[CanInitAggregate],
|
|
1290
1288
|
**kwargs: Any,
|
|
1291
1289
|
) -> TAggregate:
|
|
1292
1290
|
raise NotImplementedError # pragma: no cover
|
|
@@ -1308,8 +1306,8 @@ class Aggregate(metaclass=MetaAggregate):
|
|
|
1308
1306
|
|
|
1309
1307
|
@classmethod
|
|
1310
1308
|
def _create(
|
|
1311
|
-
cls:
|
|
1312
|
-
event_class:
|
|
1309
|
+
cls: type[TAggregate],
|
|
1310
|
+
event_class: type[CanInitAggregate],
|
|
1313
1311
|
*,
|
|
1314
1312
|
id: UUID | None = None, # noqa: A002
|
|
1315
1313
|
**kwargs: Any,
|
|
@@ -1359,7 +1357,7 @@ class Aggregate(metaclass=MetaAggregate):
|
|
|
1359
1357
|
self._version = originator_version
|
|
1360
1358
|
self._created_on = timestamp
|
|
1361
1359
|
self._modified_on = timestamp
|
|
1362
|
-
self._pending_events:
|
|
1360
|
+
self._pending_events: list[CanMutateAggregate] = []
|
|
1363
1361
|
|
|
1364
1362
|
@property
|
|
1365
1363
|
def id(self) -> UUID:
|
|
@@ -1398,7 +1396,7 @@ class Aggregate(metaclass=MetaAggregate):
|
|
|
1398
1396
|
self._modified_on = modified_on
|
|
1399
1397
|
|
|
1400
1398
|
@property
|
|
1401
|
-
def pending_events(self) ->
|
|
1399
|
+
def pending_events(self) -> list[CanMutateAggregate]:
|
|
1402
1400
|
"""
|
|
1403
1401
|
A list of pending events.
|
|
1404
1402
|
"""
|
|
@@ -1423,7 +1421,7 @@ class Aggregate(metaclass=MetaAggregate):
|
|
|
1423
1421
|
|
|
1424
1422
|
def trigger_event(
|
|
1425
1423
|
self,
|
|
1426
|
-
event_class:
|
|
1424
|
+
event_class: type[CanMutateAggregate],
|
|
1427
1425
|
**kwargs: Any,
|
|
1428
1426
|
) -> None:
|
|
1429
1427
|
"""
|
|
@@ -1480,7 +1478,7 @@ def aggregate(
|
|
|
1480
1478
|
cls: Any | None = None,
|
|
1481
1479
|
*,
|
|
1482
1480
|
created_event_name: str = "",
|
|
1483
|
-
) ->
|
|
1481
|
+
) -> type[Aggregate] | Callable[[Any], type[Aggregate]]:
|
|
1484
1482
|
"""
|
|
1485
1483
|
Converts the class that was passed in to inherit from Aggregate.
|
|
1486
1484
|
|
|
@@ -1498,7 +1496,7 @@ def aggregate(
|
|
|
1498
1496
|
pass
|
|
1499
1497
|
"""
|
|
1500
1498
|
|
|
1501
|
-
def decorator(cls_: Any) ->
|
|
1499
|
+
def decorator(cls_: Any) -> type[Aggregate]:
|
|
1502
1500
|
if issubclass(cls_, Aggregate):
|
|
1503
1501
|
msg = f"{cls_.__qualname__} is already an Aggregate"
|
|
1504
1502
|
raise TypeError(msg)
|
|
@@ -1553,7 +1551,7 @@ class VersionError(OriginatorVersionError):
|
|
|
1553
1551
|
|
|
1554
1552
|
class SnapshotProtocol(DomainEventProtocol, Protocol):
|
|
1555
1553
|
@property
|
|
1556
|
-
def state(self) ->
|
|
1554
|
+
def state(self) -> dict[str, Any]:
|
|
1557
1555
|
"""
|
|
1558
1556
|
Snapshots have a read-only 'state'.
|
|
1559
1557
|
"""
|
|
@@ -1575,7 +1573,7 @@ class CanSnapshotAggregate(HasOriginatorIDVersion, CanCreateTimestamp):
|
|
|
1575
1573
|
|
|
1576
1574
|
@classmethod
|
|
1577
1575
|
def take(
|
|
1578
|
-
cls:
|
|
1576
|
+
cls: type[TCanSnapshotAggregate],
|
|
1579
1577
|
aggregate: MutableOrImmutableAggregate,
|
|
1580
1578
|
) -> TCanSnapshotAggregate:
|
|
1581
1579
|
"""
|
|
@@ -1601,7 +1599,7 @@ class CanSnapshotAggregate(HasOriginatorIDVersion, CanCreateTimestamp):
|
|
|
1601
1599
|
"""
|
|
1602
1600
|
Reconstructs the snapshotted :class:`Aggregate` object.
|
|
1603
1601
|
"""
|
|
1604
|
-
cls = cast(
|
|
1602
|
+
cls = cast(type[Aggregate], resolve_topic(self.topic))
|
|
1605
1603
|
aggregate_state = dict(self.state)
|
|
1606
1604
|
from_version = aggregate_state.pop("class_version", 1)
|
|
1607
1605
|
class_version = getattr(cls, "class_version", 1)
|
|
@@ -1634,4 +1632,4 @@ class Snapshot(CanSnapshotAggregate, DomainEvent):
|
|
|
1634
1632
|
"""
|
|
1635
1633
|
|
|
1636
1634
|
topic: str
|
|
1637
|
-
state:
|
|
1635
|
+
state: dict[str, Any]
|
|
@@ -3,12 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from base64 import b64decode, b64encode
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import TYPE_CHECKING, Generic
|
|
7
7
|
from uuid import UUID
|
|
8
8
|
|
|
9
9
|
from eventsourcing.application import NotificationLog, Section, TApplication
|
|
10
10
|
from eventsourcing.persistence import Notification
|
|
11
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Sequence
|
|
14
|
+
|
|
12
15
|
|
|
13
16
|
class NotificationLogInterface(ABC):
|
|
14
17
|
"""
|
|
@@ -142,7 +145,7 @@ class NotificationLogJSONClient(NotificationLog):
|
|
|
142
145
|
topics: Sequence[str] = (),
|
|
143
146
|
*,
|
|
144
147
|
inclusive_of_start: bool = True,
|
|
145
|
-
) ->
|
|
148
|
+
) -> list[Notification]:
|
|
146
149
|
"""
|
|
147
150
|
Returns a selection of
|
|
148
151
|
:class:`~eventsourcing.persistence.Notification` objects
|