python-event-sourcery 0.4.0a10__tar.gz → 0.4.0a13__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 (44) hide show
  1. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/PKG-INFO +4 -3
  2. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/__init__.py +2 -0
  3. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/event/dto.py +16 -8
  4. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/event/serde.py +6 -2
  5. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/dto.py +11 -6
  6. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/outbox.py +7 -4
  7. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/pyproject.toml +2 -2
  8. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/LICENSE +0 -0
  9. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/__init__.py +0 -0
  10. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/aggregate/__init__.py +0 -0
  11. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/aggregate/aggregate.py +0 -0
  12. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/aggregate/repository.py +0 -0
  13. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/dispatcher.py +0 -0
  14. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/event/__init__.py +0 -0
  15. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/event/registry.py +0 -0
  16. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/event_store.py +0 -0
  17. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/exceptions.py +0 -0
  18. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/factory.py +0 -0
  19. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/in_memory.py +0 -0
  20. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/interfaces.py +0 -0
  21. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/outbox.py +0 -0
  22. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/stream_id.py +0 -0
  23. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/subscription.py +0 -0
  24. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/tenant_id.py +0 -0
  25. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/event_store/versioning.py +0 -0
  26. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/py.typed +0 -0
  27. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/read_model/__init__.py +0 -0
  28. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/read_model/cursors_dao.py +0 -0
  29. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery/read_model/projector.py +0 -0
  30. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/__init__.py +0 -0
  31. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/event_store.py +0 -0
  32. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/outbox.py +0 -0
  33. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/py.typed +0 -0
  34. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/stream.py +0 -0
  35. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_esdb/subscription.py +0 -0
  36. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/__init__.py +0 -0
  37. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/cursors_dao.py +0 -0
  38. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/dto.py +0 -0
  39. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/event_store.py +0 -0
  40. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/guid.py +0 -0
  41. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/jsonb.py +0 -0
  42. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/models.py +0 -0
  43. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/py.typed +0 -0
  44. {python_event_sourcery-0.4.0a10 → python_event_sourcery-0.4.0a13}/event_sourcery_sqlalchemy/subscription.py +0 -0
@@ -1,16 +1,17 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: python-event-sourcery
3
- Version: 0.4.0a10
3
+ Version: 0.4.0a13
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: Your Name
7
7
  Author-email: you@example.com
8
- Requires-Python: >=3.10,<4.0
8
+ Requires-Python: >=3.10
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
14
15
  Provides-Extra: django
15
16
  Provides-Extra: esdb
16
17
  Provides-Extra: sqlalchemy
@@ -4,6 +4,7 @@ __all__ = [
4
4
  "Dispatcher",
5
5
  "Entry",
6
6
  "Event",
7
+ "Context",
7
8
  "EventRegistry",
8
9
  "EventStore",
9
10
  "ExplicitVersioning",
@@ -29,6 +30,7 @@ __all__ = [
29
30
  from event_sourcery.event_store import exceptions, factory, interfaces, subscription
30
31
  from event_sourcery.event_store.dispatcher import Dispatcher, Listener
31
32
  from event_sourcery.event_store.event import (
33
+ Context,
32
34
  Entry,
33
35
  Event,
34
36
  EventRegistry,
@@ -1,27 +1,30 @@
1
- from datetime import datetime
1
+ import dataclasses
2
+ from datetime import datetime, timezone
2
3
  from typing import Any, ClassVar, Generic, TypeAlias, TypeVar
3
4
  from uuid import UUID, uuid4
4
5
 
5
- from pydantic import BaseModel, Field
6
+ from pydantic import BaseModel
6
7
 
7
8
  from event_sourcery.event_store.event.registry import EventRegistry
8
9
  from event_sourcery.event_store.stream_id import StreamId
9
10
  from event_sourcery.event_store.tenant_id import DEFAULT_TENANT, TenantId
10
11
 
11
12
 
13
+ @dataclasses.dataclass(frozen=True)
12
14
  class RawEvent(BaseModel):
13
15
  uuid: UUID
14
16
  stream_id: StreamId
15
17
  created_at: datetime
16
- version: int | None = None
17
18
  name: str
18
19
  data: dict
19
20
  context: dict
21
+ version: int | None = None
20
22
 
21
23
 
22
24
  Position: TypeAlias = int
23
25
 
24
26
 
27
+ @dataclasses.dataclass(frozen=True)
25
28
  class RecordedRaw(BaseModel):
26
29
  entry: RawEvent
27
30
  position: Position
@@ -52,7 +55,8 @@ class Context(BaseModel, extra="allow"):
52
55
  causation_id: UUID | None = None
53
56
 
54
57
 
55
- class WrappedEvent(BaseModel, Generic[TEvent], extra="forbid"):
58
+ @dataclasses.dataclass()
59
+ class WrappedEvent(Generic[TEvent]):
56
60
  """Wrapper for events with all relevant metadata.
57
61
 
58
62
  Returned from EventStore when loading events from a stream.
@@ -69,20 +73,24 @@ class WrappedEvent(BaseModel, Generic[TEvent], extra="forbid"):
69
73
 
70
74
  event: TEvent
71
75
  version: int | None
72
- uuid: UUID = Field(default_factory=uuid4)
73
- created_at: datetime = Field(default_factory=datetime.utcnow)
74
- context: Context = Field(default_factory=Context)
76
+ uuid: UUID = dataclasses.field(default_factory=uuid4)
77
+ created_at: datetime = dataclasses.field(
78
+ default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
79
+ )
80
+ context: Context = dataclasses.field(default_factory=Context)
75
81
 
76
82
  @classmethod
77
83
  def wrap(cls, event: TEvent, version: int | None) -> "WrappedEvent[TEvent]":
78
84
  return WrappedEvent[TEvent](event=event, version=version)
79
85
 
80
86
 
81
- class Entry(BaseModel):
87
+ @dataclasses.dataclass(frozen=True)
88
+ class Entry:
82
89
  wrapped_event: WrappedEvent
83
90
  stream_id: StreamId
84
91
 
85
92
 
93
+ @dataclasses.dataclass(frozen=True)
86
94
  class Recorded(Entry):
87
95
  position: Position
88
96
  tenant_id: TenantId = DEFAULT_TENANT
@@ -1,8 +1,10 @@
1
+ import dataclasses
1
2
  from collections.abc import Mapping, Sequence
2
3
  from dataclasses import dataclass
3
4
  from typing import cast
4
5
 
5
6
  from event_sourcery.event_store.event.dto import (
7
+ Context,
6
8
  Event,
7
9
  RawEvent,
8
10
  Recorded,
@@ -18,12 +20,14 @@ class Serde:
18
20
  registry: EventRegistry
19
21
 
20
22
  def deserialize(self, event: RawEvent) -> WrappedEvent:
21
- event_as_dict = dict(event)
23
+ event_as_dict = dataclasses.asdict(event)
22
24
  del event_as_dict["stream_id"]
23
25
  del event_as_dict["name"]
24
26
  data = cast(Mapping, event_as_dict.pop("data"))
27
+ context = event_as_dict.pop("context", {})
28
+ event_as_dict["context"] = Context(**context)
25
29
  event_type = self.registry.type_for_name(event.name)
26
- return WrappedEvent[event_type]( # type: ignore
30
+ return WrappedEvent[event_type]( # type: ignore[valid-type]
27
31
  **event_as_dict,
28
32
  event=event_type(**data),
29
33
  )
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from datetime import datetime
3
- from typing import Any
3
+ from typing import Any, Literal, cast
4
4
 
5
5
  from esdbclient import NewEvent, RecordedEvent
6
6
 
@@ -10,15 +10,22 @@ from event_sourcery_esdb import stream
10
10
  ES_PREFIX = "$es-"
11
11
 
12
12
 
13
- def raw_event(from_entry: RecordedEvent) -> RawEvent:
13
+ def raw_event(
14
+ from_entry: RecordedEvent, version: int | Literal["undefined"] = "undefined"
15
+ ) -> RawEvent:
14
16
  metadata = json.loads(from_entry.metadata.decode("utf-8"))
15
17
  created_at = datetime.fromisoformat(metadata.pop("created_at"))
16
18
  position = stream.Position(from_entry.stream_position)
19
+ if version == "undefined":
20
+ version = position.as_version()
21
+
22
+ version = cast(int, version)
23
+
17
24
  return RawEvent(
18
25
  uuid=from_entry.id,
19
26
  stream_id=stream.Name.from_stream_name(from_entry.stream_name).uuid,
20
27
  created_at=created_at,
21
- version=position.as_version(),
28
+ version=version,
22
29
  name=from_entry.type,
23
30
  data=json.loads(from_entry.data.decode("utf-8")),
24
31
  context={k: v for k, v in metadata.items() if not k.startswith(ES_PREFIX)},
@@ -28,9 +35,7 @@ def raw_event(from_entry: RecordedEvent) -> RawEvent:
28
35
  def snapshot(from_entry: RecordedEvent) -> RawEvent:
29
36
  metadata = json.loads(from_entry.metadata.decode("utf-8"))
30
37
  position = metadata[f"{ES_PREFIX}stream_position"]
31
- event = raw_event(from_entry)
32
- event.version = stream.Position(position).as_version()
33
- return event
38
+ return raw_event(from_entry, version=stream.Position(position).as_version())
34
39
 
35
40
 
36
41
  def new_entry(from_raw: RawEvent, **metadata: Any) -> NewEvent:
@@ -1,8 +1,9 @@
1
+ import dataclasses
1
2
  import logging
2
3
  from collections.abc import Generator, Iterator
3
4
  from contextlib import AbstractContextManager, contextmanager
4
5
  from dataclasses import dataclass
5
- from datetime import datetime
6
+ from datetime import datetime, timezone
6
7
  from typing import cast
7
8
  from uuid import UUID
8
9
 
@@ -31,15 +32,17 @@ class SqlAlchemyOutboxStorageStrategy(OutboxStorageStrategy):
31
32
  if not self._filterer(record.entry):
32
33
  continue
33
34
 
34
- as_dict = dict(record.entry)
35
+ stream_id = record.entry.stream_id
36
+ as_dict = dataclasses.asdict(record.entry)
37
+ as_dict.pop("stream_id")
35
38
  created_at = cast(datetime, as_dict["created_at"])
36
39
  as_dict["created_at"] = created_at.isoformat()
37
40
  as_dict["uuid"] = str(as_dict["uuid"])
38
- as_dict["stream_id"] = str(as_dict["stream_id"])
41
+ as_dict["stream_id"] = str(stream_id)
39
42
  as_dict["tenant_id"] = str(record.tenant_id)
40
43
  rows.append(
41
44
  {
42
- "created_at": datetime.utcnow(),
45
+ "created_at": datetime.now(timezone.utc).replace(tzinfo=None),
43
46
  "data": as_dict,
44
47
  "stream_name": record.entry.stream_id.name,
45
48
  "position": record.position,
@@ -1,9 +1,9 @@
1
1
  [project]
2
+ name = "python-event-sourcery"
2
3
  requires-python = ">=3.10"
3
4
 
4
5
  [tool.poetry]
5
- name = "python-event-sourcery"
6
- version = "0.4.0a10"
6
+ version = "0.4.0a13"
7
7
  description = ""
8
8
  authors = ["Your Name <you@example.com>"]
9
9
  license = "MIT"