eventsourcing 9.4.0a7__tar.gz → 9.4.0b1__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.0a7 → eventsourcing-9.4.0b1}/PKG-INFO +2 -2
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/application.py +22 -30
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/cipher.py +3 -1
- eventsourcing-9.4.0b1/eventsourcing/dispatch.py +79 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/domain.py +373 -360
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/interface.py +1 -1
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/persistence.py +26 -28
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/popo.py +5 -1
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/postgres.py +174 -127
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/projection.py +82 -26
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/sqlite.py +5 -1
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/system.py +14 -9
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/tests/application.py +57 -49
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/tests/domain.py +8 -6
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/tests/persistence.py +170 -143
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/tests/postgres_utils.py +12 -9
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/utils.py +27 -17
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/pyproject.toml +13 -3
- eventsourcing-9.4.0a7/eventsourcing/dispatch.py +0 -38
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/AUTHORS +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/LICENSE +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/README.md +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/__init__.py +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/compressor.py +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/cryptography.py +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/py.typed +0 -0
- {eventsourcing-9.4.0a7 → eventsourcing-9.4.0b1}/eventsourcing/tests/__init__.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: eventsourcing
|
|
3
|
-
Version: 9.4.
|
|
3
|
+
Version: 9.4.0b1
|
|
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
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
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Intended Audience :: Education
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import os
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from collections.abc import Iterable, Iterator, Sequence
|
|
@@ -26,6 +27,7 @@ from eventsourcing.domain import (
|
|
|
26
27
|
DomainEventProtocol,
|
|
27
28
|
EventSourcingError,
|
|
28
29
|
MutableOrImmutableAggregate,
|
|
30
|
+
SDomainEvent,
|
|
29
31
|
Snapshot,
|
|
30
32
|
SnapshotProtocol,
|
|
31
33
|
TDomainEvent,
|
|
@@ -43,7 +45,6 @@ from eventsourcing.persistence import (
|
|
|
43
45
|
Notification,
|
|
44
46
|
Recording,
|
|
45
47
|
Tracking,
|
|
46
|
-
TrackingRecorder,
|
|
47
48
|
Transcoder,
|
|
48
49
|
UUIDAsHex,
|
|
49
50
|
)
|
|
@@ -95,10 +96,9 @@ class Cache(Generic[S, T]):
|
|
|
95
96
|
return self.cache.pop(key)
|
|
96
97
|
return self.cache[key]
|
|
97
98
|
|
|
98
|
-
def put(self, key: S, value: T) ->
|
|
99
|
+
def put(self, key: S, value: T | None) -> None:
|
|
99
100
|
if value is not None:
|
|
100
101
|
self.cache[key] = value
|
|
101
|
-
return None
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
class LRUCache(Cache[S, T]):
|
|
@@ -153,7 +153,7 @@ class LRUCache(Cache[S, T]):
|
|
|
153
153
|
return result
|
|
154
154
|
raise KeyError
|
|
155
155
|
|
|
156
|
-
def put(self, key: S, value: T) -> Any | None:
|
|
156
|
+
def put(self, key: S, value: T | None) -> Any | None:
|
|
157
157
|
evicted_key = None
|
|
158
158
|
evicted_value = None
|
|
159
159
|
with self.lock:
|
|
@@ -354,19 +354,18 @@ class Repository:
|
|
|
354
354
|
return aggregate
|
|
355
355
|
|
|
356
356
|
def _use_fastforward_lock(self, aggregate_id: UUID) -> Lock:
|
|
357
|
+
lock: Lock | None = None
|
|
357
358
|
with self._fastforward_locks_lock:
|
|
358
|
-
|
|
359
|
+
num_users = 0
|
|
360
|
+
with contextlib.suppress(KeyError):
|
|
359
361
|
lock, num_users = self._fastforward_locks_inuse[aggregate_id]
|
|
360
|
-
|
|
361
|
-
|
|
362
|
+
if lock is None:
|
|
363
|
+
with contextlib.suppress(KeyError):
|
|
362
364
|
lock = self._fastforward_locks_cache.get(aggregate_id, evict=True)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
finally:
|
|
368
|
-
num_users += 1
|
|
369
|
-
self._fastforward_locks_inuse[aggregate_id] = (lock, num_users)
|
|
365
|
+
if lock is None:
|
|
366
|
+
lock = Lock()
|
|
367
|
+
num_users += 1
|
|
368
|
+
self._fastforward_locks_inuse[aggregate_id] = (lock, num_users)
|
|
370
369
|
return lock
|
|
371
370
|
|
|
372
371
|
def _disuse_fastforward_lock(self, aggregate_id: UUID) -> None:
|
|
@@ -612,12 +611,10 @@ class Application:
|
|
|
612
611
|
name = "Application"
|
|
613
612
|
env: ClassVar[dict[str, str]] = {}
|
|
614
613
|
is_snapshotting_enabled: bool = False
|
|
615
|
-
snapshotting_intervals: ClassVar[
|
|
616
|
-
dict[type[MutableOrImmutableAggregate], int] | None
|
|
617
|
-
] = None
|
|
614
|
+
snapshotting_intervals: ClassVar[dict[type[MutableOrImmutableAggregate], int]] = {}
|
|
618
615
|
snapshotting_projectors: ClassVar[
|
|
619
|
-
dict[type[MutableOrImmutableAggregate], ProjectorFunction[Any, Any]]
|
|
620
|
-
] =
|
|
616
|
+
dict[type[MutableOrImmutableAggregate], ProjectorFunction[Any, Any]]
|
|
617
|
+
] = {}
|
|
621
618
|
snapshot_class: type[SnapshotProtocol] = Snapshot
|
|
622
619
|
log_section_size = 10
|
|
623
620
|
notify_topics: Sequence[str] = []
|
|
@@ -690,9 +687,7 @@ class Application:
|
|
|
690
687
|
_env.update(env)
|
|
691
688
|
return Environment(name, _env)
|
|
692
689
|
|
|
693
|
-
def construct_factory(
|
|
694
|
-
self, env: Environment
|
|
695
|
-
) -> InfrastructureFactory[TrackingRecorder]:
|
|
690
|
+
def construct_factory(self, env: Environment) -> InfrastructureFactory:
|
|
696
691
|
"""
|
|
697
692
|
Constructs an :class:`~eventsourcing.persistence.InfrastructureFactory`
|
|
698
693
|
for use by the application.
|
|
@@ -821,12 +816,9 @@ class Application:
|
|
|
821
816
|
continue
|
|
822
817
|
interval = self.snapshotting_intervals.get(type(aggregate))
|
|
823
818
|
if interval is not None and event.originator_version % interval == 0:
|
|
824
|
-
|
|
825
|
-
self.snapshotting_projectors
|
|
826
|
-
and type(aggregate) in self.snapshotting_projectors
|
|
827
|
-
):
|
|
819
|
+
try:
|
|
828
820
|
projector_func = self.snapshotting_projectors[type(aggregate)]
|
|
829
|
-
|
|
821
|
+
except KeyError:
|
|
830
822
|
projector_func = project_aggregate
|
|
831
823
|
if projector_func is project_aggregate and not isinstance(
|
|
832
824
|
event, CanMutateProtocol
|
|
@@ -951,10 +943,10 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
951
943
|
|
|
952
944
|
def _trigger_event(
|
|
953
945
|
self,
|
|
954
|
-
logged_cls: type[
|
|
946
|
+
logged_cls: type[SDomainEvent],
|
|
955
947
|
next_originator_version: int | None = None,
|
|
956
948
|
**kwargs: Any,
|
|
957
|
-
) ->
|
|
949
|
+
) -> SDomainEvent:
|
|
958
950
|
"""
|
|
959
951
|
Constructs and returns a new log event.
|
|
960
952
|
"""
|
|
@@ -965,7 +957,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
|
|
|
965
957
|
else:
|
|
966
958
|
next_originator_version = last_logged.originator_version + 1
|
|
967
959
|
|
|
968
|
-
return logged_cls(
|
|
960
|
+
return logged_cls(
|
|
969
961
|
originator_id=self.originator_id,
|
|
970
962
|
originator_version=next_originator_version,
|
|
971
963
|
timestamp=datetime_now_with_tzinfo(),
|
|
@@ -5,7 +5,9 @@ from base64 import b64decode, b64encode
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from Crypto.Cipher import AES
|
|
8
|
-
from Crypto.Cipher._mode_gcm import
|
|
8
|
+
from Crypto.Cipher._mode_gcm import (
|
|
9
|
+
GcmMode, # pyright: ignore [reportPrivateImportUsage]
|
|
10
|
+
)
|
|
9
11
|
from Crypto.Cipher.AES import key_size
|
|
10
12
|
|
|
11
13
|
from eventsourcing.persistence import Cipher
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, cast, overload
|
|
5
|
+
|
|
6
|
+
_T = TypeVar("_T")
|
|
7
|
+
_S = TypeVar("_S")
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
|
|
11
|
+
class _singledispatchmethod(functools.singledispatchmethod[_T]): # noqa: N801
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
else:
|
|
15
|
+
|
|
16
|
+
class _singledispatchmethod( # noqa: N801
|
|
17
|
+
functools.singledispatchmethod, Generic[_T]
|
|
18
|
+
):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class singledispatchmethod(_singledispatchmethod[_T]): # noqa: N801
|
|
23
|
+
def __init__(self, func: Callable[..., _T]) -> None:
|
|
24
|
+
super().__init__(func)
|
|
25
|
+
self.deferred_registrations: list[
|
|
26
|
+
tuple[type[Any] | Callable[..., _T], Callable[..., _T] | None]
|
|
27
|
+
] = []
|
|
28
|
+
|
|
29
|
+
@overload
|
|
30
|
+
def register(
|
|
31
|
+
self, cls: type[Any], method: None = None
|
|
32
|
+
) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... # pragma: no cover
|
|
33
|
+
@overload
|
|
34
|
+
def register(
|
|
35
|
+
self, cls: Callable[..., _T], method: None = None
|
|
36
|
+
) -> Callable[..., _T]: ... # pragma: no cover
|
|
37
|
+
|
|
38
|
+
@overload
|
|
39
|
+
def register(
|
|
40
|
+
self, cls: type[Any], method: Callable[..., _T]
|
|
41
|
+
) -> Callable[..., _T]: ... # pragma: no cover
|
|
42
|
+
|
|
43
|
+
def register(
|
|
44
|
+
self,
|
|
45
|
+
cls: type[Any] | Callable[..., _T],
|
|
46
|
+
method: Callable[..., _T] | None = None,
|
|
47
|
+
) -> Callable[[Callable[..., _T]], Callable[..., _T]] | Callable[..., _T]:
|
|
48
|
+
"""generic_method.register(cls, func) -> func
|
|
49
|
+
|
|
50
|
+
Registers a new implementation for the given *cls* on a *generic_method*.
|
|
51
|
+
"""
|
|
52
|
+
if isinstance(cls, (classmethod, staticmethod)):
|
|
53
|
+
first_annotation = {}
|
|
54
|
+
for k, v in cls.__func__.__annotations__.items():
|
|
55
|
+
first_annotation[k] = v
|
|
56
|
+
break
|
|
57
|
+
cls.__annotations__ = first_annotation
|
|
58
|
+
|
|
59
|
+
# for globals in typing.get_type_hints() in Python 3.8 and 3.9
|
|
60
|
+
if not hasattr(cls, "__wrapped__"):
|
|
61
|
+
cls.__dict__["__wrapped__"] = cls.__func__
|
|
62
|
+
# cls.__wrapped__ = cls.__func__
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
return self.dispatcher.register(cast(type[Any], cls), func=method)
|
|
66
|
+
except NameError:
|
|
67
|
+
self.deferred_registrations.append(
|
|
68
|
+
(cls, method) # pyright: ignore [reportArgumentType]
|
|
69
|
+
)
|
|
70
|
+
# TODO: Fix this....
|
|
71
|
+
return method or cls # pyright: ignore [reportReturnType]
|
|
72
|
+
|
|
73
|
+
def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]:
|
|
74
|
+
for registered_cls, registered_method in self.deferred_registrations:
|
|
75
|
+
self.dispatcher.register(
|
|
76
|
+
cast(type[Any], registered_cls), func=registered_method
|
|
77
|
+
)
|
|
78
|
+
self.deferred_registrations = []
|
|
79
|
+
return super().__get__(obj, cls=cls)
|