eventsourcing 9.3.5__py3-none-any.whl → 9.4.0__py3-none-any.whl

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.

@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import os
4
5
  from abc import ABC, abstractmethod
6
+ from collections.abc import Iterable, Iterator, Sequence
5
7
  from copy import deepcopy
6
8
  from dataclasses import dataclass
7
9
  from itertools import chain
@@ -11,22 +13,13 @@ from typing import (
11
13
  Any,
12
14
  Callable,
13
15
  ClassVar,
14
- Dict,
15
16
  Generic,
16
- Iterable,
17
- Iterator,
18
- List,
19
17
  Optional,
20
- Sequence,
21
- Tuple,
22
- Type,
23
18
  TypeVar,
24
19
  cast,
25
20
  )
26
21
  from warnings import warn
27
22
 
28
- from typing_extensions import deprecated
29
-
30
23
  from eventsourcing.domain import (
31
24
  Aggregate,
32
25
  CanMutateProtocol,
@@ -34,12 +27,12 @@ from eventsourcing.domain import (
34
27
  DomainEventProtocol,
35
28
  EventSourcingError,
36
29
  MutableOrImmutableAggregate,
37
- ProgrammingError,
30
+ SDomainEvent,
38
31
  Snapshot,
39
32
  SnapshotProtocol,
40
33
  TDomainEvent,
41
34
  TMutableOrImmutableAggregate,
42
- create_utc_datetime_now,
35
+ datetime_now_with_tzinfo,
43
36
  )
44
37
  from eventsourcing.persistence import (
45
38
  ApplicationRecorder,
@@ -47,6 +40,7 @@ from eventsourcing.persistence import (
47
40
  DecimalAsStr,
48
41
  EventStore,
49
42
  InfrastructureFactory,
43
+ JSONTranscoder,
50
44
  Mapper,
51
45
  Notification,
52
46
  Recording,
@@ -56,7 +50,7 @@ from eventsourcing.persistence import (
56
50
  )
57
51
  from eventsourcing.utils import Environment, EnvType, strtobool
58
52
 
59
- if TYPE_CHECKING: # pragma: nocover
53
+ if TYPE_CHECKING:
60
54
  from uuid import UUID
61
55
 
62
56
  ProjectorFunction = Callable[
@@ -70,12 +64,15 @@ MutatorFunction = Callable[
70
64
  ]
71
65
 
72
66
 
67
+ class ProgrammingError(Exception):
68
+ pass
69
+
70
+
73
71
  def project_aggregate(
74
72
  aggregate: TMutableOrImmutableAggregate | None,
75
73
  domain_events: Iterable[DomainEventProtocol],
76
74
  ) -> TMutableOrImmutableAggregate | None:
77
- """
78
- Projector function for aggregate projections, which works
75
+ """Projector function for aggregate projections, which works
79
76
  by successively calling aggregate mutator function mutate()
80
77
  on each of the given list of domain events in turn.
81
78
  """
@@ -91,22 +88,20 @@ T = TypeVar("T")
91
88
 
92
89
  class Cache(Generic[S, T]):
93
90
  def __init__(self) -> None:
94
- self.cache: Dict[S, Any] = {}
91
+ self.cache: dict[S, Any] = {}
95
92
 
96
93
  def get(self, key: S, *, evict: bool = False) -> T:
97
94
  if evict:
98
95
  return self.cache.pop(key)
99
96
  return self.cache[key]
100
97
 
101
- def put(self, key: S, value: T) -> T | None:
98
+ def put(self, key: S, value: T | None) -> None:
102
99
  if value is not None:
103
100
  self.cache[key] = value
104
- return None
105
101
 
106
102
 
107
103
  class LRUCache(Cache[S, T]):
108
- """
109
- Size limited caching that tracks accesses by recency.
104
+ """Size limited caching that tracks accesses by recency.
110
105
 
111
106
  This is basically copied from functools.lru_cache. But
112
107
  we need to know when there was a cache hit, so we can
@@ -122,7 +117,7 @@ class LRUCache(Cache[S, T]):
122
117
  self.maxsize = maxsize
123
118
  self.full = False
124
119
  self.lock = Lock() # because linkedlist updates aren't threadsafe
125
- self.root: List[Any] = [] # root of the circular doubly linked list
120
+ self.root: list[Any] = [] # root of the circular doubly linked list
126
121
  self.clear()
127
122
 
128
123
  def clear(self) -> None:
@@ -156,7 +151,7 @@ class LRUCache(Cache[S, T]):
156
151
  return result
157
152
  raise KeyError
158
153
 
159
- def put(self, key: S, value: T) -> Any | None:
154
+ def put(self, key: S, value: T | None) -> Any | None:
160
155
  evicted_key = None
161
156
  evicted_value = None
162
157
  with self.lock:
@@ -208,7 +203,8 @@ class Repository:
208
203
  """Reconstructs aggregates from events in an
209
204
  :class:`~eventsourcing.persistence.EventStore`,
210
205
  possibly using snapshot store to avoid replaying
211
- all events."""
206
+ all events.
207
+ """
212
208
 
213
209
  FASTFORWARD_LOCKS_CACHE_MAXSIZE = 50
214
210
 
@@ -222,8 +218,7 @@ class Repository:
222
218
  fastforward_skipping: bool = False,
223
219
  deepcopy_from_cache: bool = True,
224
220
  ):
225
- """
226
- Initialises repository with given event store (an
221
+ """Initialises repository with given event store (an
227
222
  :class:`~eventsourcing.persistence.EventStore` for aggregate
228
223
  :class:`~eventsourcing.domain.AggregateEvent` objects)
229
224
  and optionally a snapshot store (an
@@ -248,7 +243,7 @@ class Repository:
248
243
  self._fastforward_locks_cache: LRUCache[UUID, Lock] = LRUCache(
249
244
  maxsize=self.FASTFORWARD_LOCKS_CACHE_MAXSIZE
250
245
  )
251
- self._fastforward_locks_inuse: Dict[UUID, Tuple[Lock, int]] = {}
246
+ self._fastforward_locks_inuse: dict[UUID, tuple[Lock, int]] = {}
252
247
 
253
248
  def get(
254
249
  self,
@@ -261,15 +256,14 @@ class Repository:
261
256
  fastforward_skipping: bool = False,
262
257
  deepcopy_from_cache: bool = True,
263
258
  ) -> TMutableOrImmutableAggregate:
264
- """
265
- Reconstructs an :class:`~eventsourcing.domain.Aggregate` for a
259
+ """Reconstructs an :class:`~eventsourcing.domain.Aggregate` for a
266
260
  given ID from stored events, optionally at a particular version.
267
261
  """
268
262
  if self.cache and version is None:
269
263
  try:
270
264
  # Look for aggregate in the cache.
271
265
  aggregate = cast(
272
- TMutableOrImmutableAggregate, self.cache.get(aggregate_id)
266
+ "TMutableOrImmutableAggregate", self.cache.get(aggregate_id)
273
267
  )
274
268
  except KeyError:
275
269
  # Reconstruct aggregate from stored events.
@@ -290,7 +284,11 @@ class Repository:
290
284
  originator_id=aggregate_id, gt=aggregate.version
291
285
  )
292
286
  _aggregate = projector_func(
293
- aggregate, cast(Iterable[TDomainEvent], new_events)
287
+ aggregate,
288
+ cast(
289
+ "Iterable[TDomainEvent]",
290
+ new_events,
291
+ ),
294
292
  )
295
293
  if _aggregate is None:
296
294
  raise AggregateNotFoundError(aggregate_id)
@@ -345,8 +343,8 @@ class Repository:
345
343
  aggregate = projector_func(
346
344
  initial,
347
345
  chain(
348
- cast(Iterable[TDomainEvent], snapshots),
349
- cast(Iterable[TDomainEvent], aggregate_events),
346
+ cast("Iterable[TDomainEvent]", snapshots),
347
+ cast("Iterable[TDomainEvent]", aggregate_events),
350
348
  ),
351
349
  )
352
350
 
@@ -357,19 +355,18 @@ class Repository:
357
355
  return aggregate
358
356
 
359
357
  def _use_fastforward_lock(self, aggregate_id: UUID) -> Lock:
358
+ lock: Lock | None = None
360
359
  with self._fastforward_locks_lock:
361
- try:
360
+ num_users = 0
361
+ with contextlib.suppress(KeyError):
362
362
  lock, num_users = self._fastforward_locks_inuse[aggregate_id]
363
- except KeyError:
364
- try:
363
+ if lock is None:
364
+ with contextlib.suppress(KeyError):
365
365
  lock = self._fastforward_locks_cache.get(aggregate_id, evict=True)
366
- except KeyError:
367
- lock = Lock()
368
- finally:
369
- num_users = 0
370
- finally:
371
- num_users += 1
372
- self._fastforward_locks_inuse[aggregate_id] = (lock, num_users)
366
+ if lock is None:
367
+ lock = Lock()
368
+ num_users += 1
369
+ self._fastforward_locks_inuse[aggregate_id] = (lock, num_users)
373
370
  return lock
374
371
 
375
372
  def _disuse_fastforward_lock(self, aggregate_id: UUID) -> None:
@@ -383,9 +380,7 @@ class Repository:
383
380
  self._fastforward_locks_inuse[aggregate_id] = (lock_, num_users)
384
381
 
385
382
  def __contains__(self, item: UUID) -> bool:
386
- """
387
- Tests to see if an aggregate exists in the repository.
388
- """
383
+ """Tests to see if an aggregate exists in the repository."""
389
384
  try:
390
385
  self.get(aggregate_id=item)
391
386
  except AggregateNotFoundError:
@@ -396,8 +391,7 @@ class Repository:
396
391
 
397
392
  @dataclass(frozen=True)
398
393
  class Section:
399
- """
400
- Frozen dataclass that represents a section from a :class:`NotificationLog`.
394
+ """Frozen dataclass that represents a section from a :class:`NotificationLog`.
401
395
  The :data:`items` attribute contains a list of
402
396
  :class:`~eventsourcing.persistence.Notification` objects.
403
397
  The :data:`id` attribute is the section ID, two integers
@@ -410,24 +404,21 @@ class Section:
410
404
  Constructor arguments:
411
405
 
412
406
  :param Optional[str] id: section ID of this section e.g. "1,10"
413
- :param List[Notification] items: a list of event notifications
407
+ :param list[Notification] items: a list of event notifications
414
408
  :param Optional[str] next_id: section ID of the following section
415
409
  """
416
410
 
417
411
  id: str | None
418
- items: List[Notification]
412
+ items: list[Notification]
419
413
  next_id: str | None
420
414
 
421
415
 
422
416
  class NotificationLog(ABC):
423
- """
424
- Abstract base class for notification logs.
425
- """
417
+ """Abstract base class for notification logs."""
426
418
 
427
419
  @abstractmethod
428
420
  def __getitem__(self, section_id: str) -> Section:
429
- """
430
- Returns a :class:`Section` of
421
+ """Returns a :class:`Section` of
431
422
  :class:`~eventsourcing.persistence.Notification` objects
432
423
  from the notification log.
433
424
  """
@@ -435,21 +426,21 @@ class NotificationLog(ABC):
435
426
  @abstractmethod
436
427
  def select(
437
428
  self,
438
- start: int,
429
+ start: int | None,
439
430
  limit: int,
440
431
  stop: int | None = None,
441
432
  topics: Sequence[str] = (),
442
- ) -> List[Notification]:
443
- """
444
- Returns a selection of
433
+ *,
434
+ inclusive_of_start: bool = True,
435
+ ) -> list[Notification]:
436
+ """Returns a selection of
445
437
  :class:`~eventsourcing.persistence.Notification` objects
446
438
  from the notification log.
447
439
  """
448
440
 
449
441
 
450
442
  class LocalNotificationLog(NotificationLog):
451
- """
452
- Notification log that presents sections of event notifications
443
+ """Notification log that presents sections of event notifications
453
444
  retrieved from an :class:`~eventsourcing.persistence.ApplicationRecorder`.
454
445
  """
455
446
 
@@ -460,8 +451,7 @@ class LocalNotificationLog(NotificationLog):
460
451
  recorder: ApplicationRecorder,
461
452
  section_size: int = DEFAULT_SECTION_SIZE,
462
453
  ):
463
- """
464
- Initialises a local notification object with given
454
+ """Initialises a local notification object with given
465
455
  :class:`~eventsourcing.persistence.ApplicationRecorder`
466
456
  and an optional section size.
467
457
 
@@ -476,8 +466,7 @@ class LocalNotificationLog(NotificationLog):
476
466
  self.section_size = section_size
477
467
 
478
468
  def __getitem__(self, requested_section_id: str) -> Section:
479
- """
480
- Returns a :class:`Section` of event notifications
469
+ """Returns a :class:`Section` of event notifications
481
470
  based on the requested section ID. The section ID of
482
471
  the returned section will describe the event
483
472
  notifications that are actually contained in
@@ -523,13 +512,14 @@ class LocalNotificationLog(NotificationLog):
523
512
 
524
513
  def select(
525
514
  self,
526
- start: int,
515
+ start: int | None,
527
516
  limit: int,
528
517
  stop: int | None = None,
529
518
  topics: Sequence[str] = (),
530
- ) -> List[Notification]:
531
- """
532
- Returns a selection of
519
+ *,
520
+ inclusive_of_start: bool = True,
521
+ ) -> list[Notification]:
522
+ """Returns a selection of
533
523
  :class:`~eventsourcing.persistence.Notification` objects
534
524
  from the notification log.
535
525
  """
@@ -539,7 +529,11 @@ class LocalNotificationLog(NotificationLog):
539
529
  )
540
530
  raise ValueError(msg)
541
531
  return self.recorder.select_notifications(
542
- start=start, limit=limit, stop=stop, topics=topics
532
+ start=start,
533
+ limit=limit,
534
+ stop=stop,
535
+ topics=topics,
536
+ inclusive_of_start=inclusive_of_start,
543
537
  )
544
538
 
545
539
  @staticmethod
@@ -548,30 +542,25 @@ class LocalNotificationLog(NotificationLog):
548
542
 
549
543
 
550
544
  class ProcessingEvent:
551
- """
552
- Keeps together a :class:`~eventsourcing.persistence.Tracking`
545
+ """Keeps together a :class:`~eventsourcing.persistence.Tracking`
553
546
  object, which represents the position of a domain event notification
554
547
  in the notification log of a particular application, and the
555
548
  new domain events that result from processing that notification.
556
549
  """
557
550
 
558
551
  def __init__(self, tracking: Tracking | None = None):
559
- """
560
- Initialises the process event with the given tracking object.
561
- """
552
+ """Initialises the process event with the given tracking object."""
562
553
  self.tracking = tracking
563
- self.events: List[DomainEventProtocol] = []
564
- self.aggregates: Dict[UUID, MutableOrImmutableAggregate] = {}
565
- self.saved_kwargs: Dict[Any, Any] = {}
554
+ self.events: list[DomainEventProtocol] = []
555
+ self.aggregates: dict[UUID, MutableOrImmutableAggregate] = {}
556
+ self.saved_kwargs: dict[Any, Any] = {}
566
557
 
567
558
  def collect_events(
568
559
  self,
569
560
  *objs: MutableOrImmutableAggregate | DomainEventProtocol | None,
570
561
  **kwargs: Any,
571
562
  ) -> None:
572
- """
573
- Collects pending domain events from the given aggregate.
574
- """
563
+ """Collects pending domain events from the given aggregate."""
575
564
  for obj in objs:
576
565
  if obj is None:
577
566
  continue
@@ -600,20 +589,16 @@ class ProcessingEvent:
600
589
 
601
590
 
602
591
  class Application:
603
- """
604
- Base class for event-sourced applications.
605
- """
592
+ """Base class for event-sourced applications."""
606
593
 
607
594
  name = "Application"
608
- env: ClassVar[Dict[str, str]] = {}
595
+ env: ClassVar[dict[str, str]] = {}
609
596
  is_snapshotting_enabled: bool = False
610
- snapshotting_intervals: ClassVar[
611
- Dict[Type[MutableOrImmutableAggregate], int] | None
612
- ] = None
597
+ snapshotting_intervals: ClassVar[dict[type[MutableOrImmutableAggregate], int]] = {}
613
598
  snapshotting_projectors: ClassVar[
614
- Dict[Type[MutableOrImmutableAggregate], ProjectorFunction[Any, Any]] | None
615
- ] = None
616
- snapshot_class: Type[SnapshotProtocol] = Snapshot
599
+ dict[type[MutableOrImmutableAggregate], ProjectorFunction[Any, Any]]
600
+ ] = {}
601
+ snapshot_class: type[SnapshotProtocol] = Snapshot
617
602
  log_section_size = 10
618
603
  notify_topics: Sequence[str] = []
619
604
 
@@ -627,8 +612,7 @@ class Application:
627
612
  cls.name = cls.__name__
628
613
 
629
614
  def __init__(self, env: EnvType | None = None) -> None:
630
- """
631
- Initialises an application with an
615
+ """Initialises an application with an
632
616
  :class:`~eventsourcing.persistence.InfrastructureFactory`,
633
617
  a :class:`~eventsourcing.persistence.Mapper`,
634
618
  an :class:`~eventsourcing.persistence.ApplicationRecorder`,
@@ -650,15 +634,12 @@ class Application:
650
634
 
651
635
  @property
652
636
  def repository(self) -> Repository:
653
- """
654
- An application's repository reconstructs aggregates from stored events.
655
- """
637
+ """An application's repository reconstructs aggregates from stored events."""
656
638
  return self._repository
657
639
 
658
640
  @property
659
641
  def notification_log(self) -> LocalNotificationLog:
660
- """
661
- An application's notification log presents all the aggregate events
642
+ """An application's notification log presents all the aggregate events
662
643
  of an application in the order they were recorded as a sequence of event
663
644
  notifications.
664
645
  """
@@ -674,9 +655,7 @@ class Application:
674
655
  return self._notification_log
675
656
 
676
657
  def construct_env(self, name: str, env: EnvType | None = None) -> Environment:
677
- """
678
- Constructs environment from which application will be configured.
679
- """
658
+ """Constructs environment from which application will be configured."""
680
659
  _env = dict(type(self).env)
681
660
  if type(self).is_snapshotting_enabled or type(self).snapshotting_intervals:
682
661
  _env["IS_SNAPSHOTTING_ENABLED"] = "y"
@@ -686,31 +665,28 @@ class Application:
686
665
  return Environment(name, _env)
687
666
 
688
667
  def construct_factory(self, env: Environment) -> InfrastructureFactory:
689
- """
690
- Constructs an :class:`~eventsourcing.persistence.InfrastructureFactory`
668
+ """Constructs an :class:`~eventsourcing.persistence.InfrastructureFactory`
691
669
  for use by the application.
692
670
  """
693
671
  return InfrastructureFactory.construct(env)
694
672
 
695
673
  def construct_mapper(self) -> Mapper:
696
- """
697
- Constructs a :class:`~eventsourcing.persistence.Mapper`
674
+ """Constructs a :class:`~eventsourcing.persistence.Mapper`
698
675
  for use by the application.
699
676
  """
700
677
  return self.factory.mapper(transcoder=self.construct_transcoder())
701
678
 
702
679
  def construct_transcoder(self) -> Transcoder:
703
- """
704
- Constructs a :class:`~eventsourcing.persistence.Transcoder`
680
+ """Constructs a :class:`~eventsourcing.persistence.Transcoder`
705
681
  for use by the application.
706
682
  """
707
683
  transcoder = self.factory.transcoder()
708
- self.register_transcodings(transcoder)
684
+ if isinstance(transcoder, JSONTranscoder):
685
+ self.register_transcodings(transcoder)
709
686
  return transcoder
710
687
 
711
- def register_transcodings(self, transcoder: Transcoder) -> None:
712
- """
713
- Registers :class:`~eventsourcing.persistence.Transcoding`
688
+ def register_transcodings(self, transcoder: JSONTranscoder) -> None:
689
+ """Registers :class:`~eventsourcing.persistence.Transcoding`
714
690
  objects on given :class:`~eventsourcing.persistence.JSONTranscoder`.
715
691
  """
716
692
  transcoder.register(UUIDAsHex())
@@ -718,15 +694,13 @@ class Application:
718
694
  transcoder.register(DatetimeAsISO())
719
695
 
720
696
  def construct_recorder(self) -> ApplicationRecorder:
721
- """
722
- Constructs an :class:`~eventsourcing.persistence.ApplicationRecorder`
697
+ """Constructs an :class:`~eventsourcing.persistence.ApplicationRecorder`
723
698
  for use by the application.
724
699
  """
725
700
  return self.factory.application_recorder()
726
701
 
727
702
  def construct_event_store(self) -> EventStore:
728
- """
729
- Constructs an :class:`~eventsourcing.persistence.EventStore`
703
+ """Constructs an :class:`~eventsourcing.persistence.EventStore`
730
704
  for use by the application to store and retrieve aggregate
731
705
  :class:`~eventsourcing.domain.AggregateEvent` objects.
732
706
  """
@@ -736,8 +710,7 @@ class Application:
736
710
  )
737
711
 
738
712
  def construct_snapshot_store(self) -> EventStore:
739
- """
740
- Constructs an :class:`~eventsourcing.persistence.EventStore`
713
+ """Constructs an :class:`~eventsourcing.persistence.EventStore`
741
714
  for use by the application to store and retrieve aggregate
742
715
  :class:`~eventsourcing.domain.Snapshot` objects.
743
716
  """
@@ -748,9 +721,7 @@ class Application:
748
721
  )
749
722
 
750
723
  def construct_repository(self) -> Repository:
751
- """
752
- Constructs a :class:`Repository` for use by the application.
753
- """
724
+ """Constructs a :class:`Repository` for use by the application."""
754
725
  cache_maxsize_envvar = self.env.get(self.AGGREGATE_CACHE_MAXSIZE)
755
726
  cache_maxsize = int(cache_maxsize_envvar) if cache_maxsize_envvar else None
756
727
  return Repository(
@@ -767,18 +738,15 @@ class Application:
767
738
  )
768
739
 
769
740
  def construct_notification_log(self) -> LocalNotificationLog:
770
- """
771
- Constructs a :class:`LocalNotificationLog` for use by the application.
772
- """
741
+ """Constructs a :class:`LocalNotificationLog` for use by the application."""
773
742
  return LocalNotificationLog(self.recorder, section_size=self.log_section_size)
774
743
 
775
744
  def save(
776
745
  self,
777
746
  *objs: MutableOrImmutableAggregate | DomainEventProtocol | None,
778
747
  **kwargs: Any,
779
- ) -> List[Recording]:
780
- """
781
- Collects pending events from given aggregates and
748
+ ) -> list[Recording]:
749
+ """Collects pending events from given aggregates and
782
750
  puts them in the application's event store.
783
751
  """
784
752
  processing_event = ProcessingEvent()
@@ -789,10 +757,8 @@ class Application:
789
757
  self.notify(processing_event.events) # Deprecated.
790
758
  return recordings
791
759
 
792
- def _record(self, processing_event: ProcessingEvent) -> List[Recording]:
793
- """
794
- Records given process event in the application's recorder.
795
- """
760
+ def _record(self, processing_event: ProcessingEvent) -> list[Recording]:
761
+ """Records given process event in the application's recorder."""
796
762
  recordings = self.events.put(
797
763
  processing_event.events,
798
764
  tracking=processing_event.tracking,
@@ -813,12 +779,9 @@ class Application:
813
779
  continue
814
780
  interval = self.snapshotting_intervals.get(type(aggregate))
815
781
  if interval is not None and event.originator_version % interval == 0:
816
- if (
817
- self.snapshotting_projectors
818
- and type(aggregate) in self.snapshotting_projectors
819
- ):
782
+ try:
820
783
  projector_func = self.snapshotting_projectors[type(aggregate)]
821
- else:
784
+ except KeyError:
822
785
  projector_func = project_aggregate
823
786
  if projector_func is project_aggregate and not isinstance(
824
787
  event, CanMutateProtocol
@@ -849,8 +812,7 @@ class Application:
849
812
  TMutableOrImmutableAggregate, TDomainEvent
850
813
  ] = project_aggregate,
851
814
  ) -> None:
852
- """
853
- Takes a snapshot of the recorded state of the aggregate,
815
+ """Takes a snapshot of the recorded state of the aggregate,
854
816
  and puts the snapshot in the snapshot store.
855
817
  """
856
818
  if self.snapshots is None:
@@ -869,9 +831,8 @@ class Application:
869
831
  snapshot = snapshot_class.take(aggregate)
870
832
  self.snapshots.put([snapshot])
871
833
 
872
- def notify(self, new_events: List[DomainEventProtocol]) -> None:
873
- """
874
- Deprecated.
834
+ def notify(self, new_events: list[DomainEventProtocol]) -> None:
835
+ """Deprecated.
875
836
 
876
837
  Called after new aggregate events have been saved. This
877
838
  method on this class doesn't actually do anything,
@@ -879,9 +840,8 @@ class Application:
879
840
  need to take action when new domain events have been saved.
880
841
  """
881
842
 
882
- def _notify(self, recordings: List[Recording]) -> None:
883
- """
884
- Called after new aggregate events have been saved. This
843
+ def _notify(self, recordings: list[Recording]) -> None:
844
+ """Called after new aggregate events have been saved. This
885
845
  method on this class doesn't actually do anything,
886
846
  but this method may be implemented by subclasses that
887
847
  need to take action when new domain events have been saved.
@@ -895,23 +855,14 @@ class Application:
895
855
  TApplication = TypeVar("TApplication", bound=Application)
896
856
 
897
857
 
898
- @deprecated(
899
- "AggregateNotFound is deprecated, use AggregateNotFoundError instead", category=None
900
- )
901
- class AggregateNotFound(EventSourcingError): # noqa: N818
902
- pass
903
-
904
-
905
- class AggregateNotFoundError(AggregateNotFound):
906
- """
907
- Raised when an :class:`~eventsourcing.domain.Aggregate`
858
+ class AggregateNotFoundError(EventSourcingError):
859
+ """Raised when an :class:`~eventsourcing.domain.Aggregate`
908
860
  object is not found in a :class:`Repository`.
909
861
  """
910
862
 
911
863
 
912
864
  class EventSourcedLog(Generic[TDomainEvent]):
913
- """
914
- Constructs a sequence of domain events, like an aggregate.
865
+ """Constructs a sequence of domain events, like an aggregate.
915
866
  But unlike an aggregate the events can be triggered
916
867
  and selected for use in an application without
917
868
  reconstructing a current state from all the events.
@@ -928,7 +879,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
928
879
  self,
929
880
  events: EventStore,
930
881
  originator_id: UUID,
931
- logged_cls: Type[TDomainEvent], # TODO: Rename to 'event_class' in v10.
882
+ logged_cls: type[TDomainEvent], # TODO: Rename to 'event_class' in v10.
932
883
  ):
933
884
  self.events = events
934
885
  self.originator_id = originator_id
@@ -939,9 +890,7 @@ class EventSourcedLog(Generic[TDomainEvent]):
939
890
  next_originator_version: int | None = None,
940
891
  **kwargs: Any,
941
892
  ) -> TDomainEvent:
942
- """
943
- Constructs and returns a new log event.
944
- """
893
+ """Constructs and returns a new log event."""
945
894
  return self._trigger_event(
946
895
  logged_cls=self.logged_cls,
947
896
  next_originator_version=next_originator_version,
@@ -950,13 +899,11 @@ class EventSourcedLog(Generic[TDomainEvent]):
950
899
 
951
900
  def _trigger_event(
952
901
  self,
953
- logged_cls: Type[T] | None,
902
+ logged_cls: type[SDomainEvent],
954
903
  next_originator_version: int | None = None,
955
904
  **kwargs: Any,
956
- ) -> T:
957
- """
958
- Constructs and returns a new log event.
959
- """
905
+ ) -> SDomainEvent:
906
+ """Constructs and returns a new log event."""
960
907
  if next_originator_version is None:
961
908
  last_logged = self.get_last()
962
909
  if last_logged is None:
@@ -964,26 +911,22 @@ class EventSourcedLog(Generic[TDomainEvent]):
964
911
  else:
965
912
  next_originator_version = last_logged.originator_version + 1
966
913
 
967
- return logged_cls( # type: ignore
914
+ return logged_cls(
968
915
  originator_id=self.originator_id,
969
916
  originator_version=next_originator_version,
970
- timestamp=create_utc_datetime_now(),
917
+ timestamp=datetime_now_with_tzinfo(),
971
918
  **kwargs,
972
919
  )
973
920
 
974
921
  def get_first(self) -> TDomainEvent | None:
975
- """
976
- Selects the first logged event.
977
- """
922
+ """Selects the first logged event."""
978
923
  try:
979
924
  return next(self.get(limit=1))
980
925
  except StopIteration:
981
926
  return None
982
927
 
983
928
  def get_last(self) -> TDomainEvent | None:
984
- """
985
- Selects the last logged event.
986
- """
929
+ """Selects the last logged event."""
987
930
  try:
988
931
  return next(self.get(desc=True, limit=1))
989
932
  except StopIteration:
@@ -997,12 +940,11 @@ class EventSourcedLog(Generic[TDomainEvent]):
997
940
  desc: bool = False,
998
941
  limit: int | None = None,
999
942
  ) -> Iterator[TDomainEvent]:
1000
- """
1001
- Selects a range of logged events with limit,
943
+ """Selects a range of logged events with limit,
1002
944
  with ascending or descending order.
1003
945
  """
1004
946
  return cast(
1005
- Iterator[TDomainEvent],
947
+ "Iterator[TDomainEvent]",
1006
948
  self.events.get(
1007
949
  originator_id=self.originator_id,
1008
950
  gt=gt,