python-event-sourcery 0.4.0a3__tar.gz → 0.4.0a4__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.
Files changed (43) hide show
  1. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/PKG-INFO +1 -1
  2. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/aggregate/repository.py +1 -1
  3. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/event/dto.py +21 -0
  4. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/event/registry.py +7 -0
  5. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/event_store.py +81 -14
  6. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/factory.py +2 -0
  7. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/in_memory.py +2 -0
  8. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/pyproject.toml +2 -1
  9. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/LICENSE +0 -0
  10. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/__init__.py +0 -0
  11. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/aggregate/__init__.py +0 -0
  12. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/aggregate/aggregate.py +0 -0
  13. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/__init__.py +0 -0
  14. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/dispatcher.py +0 -0
  15. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/event/__init__.py +0 -0
  16. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/event/serde.py +0 -0
  17. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/exceptions.py +0 -0
  18. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/interfaces.py +0 -0
  19. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/outbox.py +0 -0
  20. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/stream_id.py +0 -0
  21. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/subscription.py +0 -0
  22. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/event_store/versioning.py +0 -0
  23. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/py.typed +0 -0
  24. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/read_model/__init__.py +0 -0
  25. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/read_model/cursors_dao.py +0 -0
  26. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery/read_model/projector.py +0 -0
  27. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/__init__.py +0 -0
  28. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/dto.py +0 -0
  29. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/event_store.py +0 -0
  30. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/outbox.py +0 -0
  31. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/py.typed +0 -0
  32. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/stream.py +0 -0
  33. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_esdb/subscription.py +0 -0
  34. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/__init__.py +0 -0
  35. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/cursors_dao.py +0 -0
  36. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/dto.py +0 -0
  37. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/event_store.py +0 -0
  38. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/guid.py +0 -0
  39. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/jsonb.py +0 -0
  40. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/models.py +0 -0
  41. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/outbox.py +0 -0
  42. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/py.typed +0 -0
  43. {python_event_sourcery-0.4.0a3 → python_event_sourcery-0.4.0a4}/event_sourcery_sqlalchemy/subscription.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-event-sourcery
3
- Version: 0.4.0a3
3
+ Version: 0.4.0a4
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: Your Name
@@ -49,7 +49,7 @@ class Repository(Generic[TAggregate]):
49
49
  if not events:
50
50
  return
51
51
 
52
- self._event_store.publish(
52
+ self._event_store.append(
53
53
  *events,
54
54
  stream_id=stream_id,
55
55
  expected_version=old_version,
@@ -27,6 +27,14 @@ class RecordedRaw(BaseModel):
27
27
 
28
28
 
29
29
  class Event(BaseModel, extra="forbid"):
30
+ """Base class for all events.
31
+
32
+ Example usage:
33
+ ```
34
+ class OrderCancelled(Event):
35
+ order_id: OrderId
36
+ ```
37
+ """
30
38
  __registry__: ClassVar = EventRegistry()
31
39
 
32
40
  def __init_subclass__(cls, **kwargs: Any) -> None:
@@ -42,6 +50,19 @@ class Context(BaseModel, extra="allow"):
42
50
 
43
51
 
44
52
  class Metadata(BaseModel, Generic[TEvent], extra="forbid"):
53
+ """Wrapper for events with all relevant metadata.
54
+
55
+ Returned from EventStore when loading events from a stream.
56
+
57
+ Example usage:
58
+ ```
59
+ class OrderCancelled(Event):
60
+ order_id: OrderId
61
+
62
+ event = OrderCancelled(order_id=OrderId("#123"))
63
+ metadata = Metadata.wrap(event, version=1)
64
+ ```
65
+ """
45
66
  event: TEvent
46
67
  version: int | None
47
68
  uuid: UUID = Field(default_factory=uuid4)
@@ -18,11 +18,18 @@ def event_name(cls: type) -> str:
18
18
 
19
19
 
20
20
  class EventRegistry:
21
+ """Keeps mappings between event types and their names.
22
+
23
+ Normally, there is no need to use it directly. If one needs to have multiple
24
+ registries or wants more granular control, they can pass an instance
25
+ of EventRegistry to BackendFactory."""
26
+
21
27
  def __init__(self) -> None:
22
28
  self._types_to_names: dict[type[Event], str] = {}
23
29
  self._names_to_types: dict[str, type[Event]] = {}
24
30
 
25
31
  def add(self, event: type["Event"]) -> type["Event"]:
32
+ """Add event subclass to the registry."""
26
33
  if event in self._types_to_names:
27
34
  raise DuplicatedEvent(f"Duplicated Event detected! {event}")
28
35
 
@@ -13,6 +13,8 @@ from event_sourcery.event_store.versioning import (
13
13
 
14
14
 
15
15
  class EventStore:
16
+ """API for working with events."""
17
+
16
18
  def __init__(self, storage_strategy: StorageStrategy, serde: Serde) -> None:
17
19
  self._storage_strategy = storage_strategy
18
20
  self._serde = serde
@@ -23,6 +25,24 @@ class EventStore:
23
25
  start: int | None = None,
24
26
  stop: int | None = None,
25
27
  ) -> Sequence[Metadata]:
28
+ """Loads events from a given stream.
29
+
30
+ Examples:
31
+ >>> load_stream(stream_id=StreamId(name="not_existing_stream"))
32
+ []
33
+ >>> load_stream(stream_id=StreamId(name="existing_stream"))
34
+ [Metadata(..., version=1), Metadata(...), Metadata(..., version=3)]
35
+ >>> load_stream(stream_id=StreamId(name="existing_stream"), start=2, stop=3)
36
+ [Metadata(..., version=2)]
37
+
38
+ Args:
39
+ stream_id: The stream identifier to load events from.
40
+ start: The stream version to start loading from (including).
41
+ stop: The stream version to stop loading at (excluding).
42
+
43
+ Returns:
44
+ A sequence of events or empty list if the stream doesn't exist.
45
+ """
26
46
  events = self._storage_strategy.fetch_events(stream_id, start=start, stop=stop)
27
47
  return self._deserialize_events(events)
28
48
 
@@ -34,6 +54,26 @@ class EventStore:
34
54
  stream_id: StreamId,
35
55
  expected_version: int | Versioning = 0,
36
56
  ) -> None:
57
+ """Appends events to a stream with a given ID.
58
+
59
+ Implements optimistic locking to ensure stream wasn't modified since last read.
60
+ To use it, pass the expected version of the stream.
61
+
62
+ Examples:
63
+ >>> append(Metadata(...), stream_id=StreamId())
64
+ None
65
+ >>> append(Metadata(...), stream_id=StreamId(), expected_version=1)
66
+ None
67
+
68
+ Args:
69
+ first: The first event to append (Metadata or Event).
70
+ *events: The rest of the events to append (same type as first argument).
71
+ stream_id: The stream identifier to append events to.
72
+ expected_version: The expected version of the stream
73
+
74
+ Returns:
75
+ None
76
+ """
37
77
  self._append(
38
78
  stream_id=stream_id,
39
79
  events=(first, *events),
@@ -71,20 +111,6 @@ class EventStore:
71
111
  ) -> Sequence[Metadata]:
72
112
  return [Metadata.wrap(event=event, version=None) for event in events]
73
113
 
74
- @singledispatchmethod
75
- def publish(
76
- self,
77
- first: Metadata,
78
- *events: Metadata,
79
- stream_id: StreamId,
80
- expected_version: int | Versioning = 0,
81
- ) -> None:
82
- self._append(
83
- stream_id=stream_id,
84
- events=(first, *events),
85
- expected_version=expected_version,
86
- )
87
-
88
114
  def _append(
89
115
  self,
90
116
  stream_id: StreamId,
@@ -108,9 +134,41 @@ class EventStore:
108
134
  )
109
135
 
110
136
  def delete_stream(self, stream_id: StreamId) -> None:
137
+ """Deletes a stream with a given ID.
138
+
139
+ If a stream does not exist, this method does nothing.
140
+
141
+ Examples:
142
+ >>> delete_stream(StreamId())
143
+ None
144
+ >>> delete_stream(StreamId(name="not_existing_stream"))
145
+ None
146
+
147
+ Args:
148
+ stream_id: The stream identifier to delete.
149
+
150
+ Returns:
151
+ None
152
+ """
111
153
  self._storage_strategy.delete_stream(stream_id)
112
154
 
113
155
  def save_snapshot(self, stream_id: StreamId, snapshot: Metadata) -> None:
156
+ """Saves a snapshot of the stream.
157
+
158
+ Examples:
159
+ >>> save_snapshot(StreamId(), Metadata(...))
160
+ None
161
+ >>> save_snapshot(StreamId(name="not_existing_stream"), Metadata(...))
162
+ None
163
+
164
+ Args:
165
+ stream_id: The stream identifier to save the snapshot.
166
+ snapshot: The snapshot to save.
167
+
168
+ Returns:
169
+ None
170
+ """
171
+
114
172
  serialized = self._serde.serialize(event=snapshot, stream_id=stream_id)
115
173
  self._storage_strategy.save_snapshot(serialized)
116
174
 
@@ -126,4 +184,13 @@ class EventStore:
126
184
 
127
185
  @property
128
186
  def position(self) -> Position | None:
187
+ """Returns the current position of the event store.
188
+
189
+ Examples:
190
+ >>> position
191
+ None # nothing was saved yet
192
+ >>> position
193
+ Position(15) # Some events were saved
194
+
195
+ """
129
196
  return self._storage_strategy.current_position
@@ -38,6 +38,8 @@ class TransactionalBackend(Backend):
38
38
 
39
39
 
40
40
  class BackendFactory(abc.ABC):
41
+ """Abstract base class to configure EventStore."""
42
+
41
43
  @abc.abstractmethod
42
44
  def build(self) -> Backend:
43
45
  pass
@@ -298,6 +298,8 @@ class Config(BaseModel):
298
298
 
299
299
  @dataclass(repr=False)
300
300
  class InMemoryBackendFactory(BackendFactory):
301
+ """Lightweight in-memory backend factory for testing and development."""
302
+
301
303
  serde = Serde(Event.__registry__)
302
304
 
303
305
  _config: Config = field(default_factory=Config)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-event-sourcery"
3
- version = "0.4.0a3"
3
+ version = "0.4.0a4"
4
4
  description = ""
5
5
  authors = ["Your Name <you@example.com>"]
6
6
  license = "MIT"
@@ -26,6 +26,7 @@ pytest-cov = "*"
26
26
  pytest-django = {version = "*"}
27
27
  mkdocs-material = "^8.3.9"
28
28
  psycopg2-binary = "^2.9.3"
29
+ mkdocstrings = {version = "*", extras = ["python"]}
29
30
 
30
31
  [tool.poetry.extras]
31
32
  sqlalchemy = ["SQLAlchemy"]