eventsourcing 9.4.0b1__py3-none-any.whl → 9.4.0b3__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.

@@ -31,9 +31,7 @@ if TYPE_CHECKING:
31
31
 
32
32
 
33
33
  class Transcoding(ABC):
34
- """
35
- Abstract base class for custom transcodings.
36
- """
34
+ """Abstract base class for custom transcodings."""
37
35
 
38
36
  type: type
39
37
  name: str
@@ -48,9 +46,7 @@ class Transcoding(ABC):
48
46
 
49
47
 
50
48
  class Transcoder(ABC):
51
- """
52
- Abstract base class for transcoders.
53
- """
49
+ """Abstract base class for transcoders."""
54
50
 
55
51
  @abstractmethod
56
52
  def encode(self, obj: Any) -> bytes:
@@ -61,10 +57,12 @@ class Transcoder(ABC):
61
57
  """Decodes obj from bytes."""
62
58
 
63
59
 
60
+ class TranscodingNotRegisteredError(EventSourcingError, TypeError):
61
+ """Raised when a transcoding isn't registered with JSONTranscoder."""
62
+
63
+
64
64
  class JSONTranscoder(Transcoder):
65
- """
66
- Extensible transcoder that uses the Python :mod:`json` module.
67
- """
65
+ """Extensible transcoder that uses the Python :mod:`json` module."""
68
66
 
69
67
  def __init__(self) -> None:
70
68
  self.types: dict[type, Transcoding] = {}
@@ -77,22 +75,16 @@ class JSONTranscoder(Transcoder):
77
75
  self.decoder = json.JSONDecoder(object_hook=self._decode_obj)
78
76
 
79
77
  def register(self, transcoding: Transcoding) -> None:
80
- """
81
- Registers given transcoding with the transcoder.
82
- """
78
+ """Registers given transcoding with the transcoder."""
83
79
  self.types[transcoding.type] = transcoding
84
80
  self.names[transcoding.name] = transcoding
85
81
 
86
82
  def encode(self, obj: Any) -> bytes:
87
- """
88
- Encodes given object as a bytes array.
89
- """
83
+ """Encodes given object as a bytes array."""
90
84
  return self.encoder.encode(obj).encode("utf8")
91
85
 
92
86
  def decode(self, data: bytes) -> Any:
93
- """
94
- Decodes bytes array as previously encoded object.
95
- """
87
+ """Decodes bytes array as previously encoded object."""
96
88
  return self.decoder.decode(data.decode("utf8"))
97
89
 
98
90
  def _encode_obj(self, o: Any) -> dict[str, Any]:
@@ -104,7 +96,7 @@ class JSONTranscoder(Transcoder):
104
96
  "serializable. Please define and register "
105
97
  "a custom transcoding for this type."
106
98
  )
107
- raise TypeError(msg) from None
99
+ raise TranscodingNotRegisteredError(msg) from None
108
100
  else:
109
101
  return {
110
102
  "_type_": transcoding.name,
@@ -124,14 +116,14 @@ class JSONTranscoder(Transcoder):
124
116
  return d
125
117
  else:
126
118
  try:
127
- transcoding = self.names[cast(str, _type_)]
119
+ transcoding = self.names[cast("str", _type_)]
128
120
  except KeyError as e:
129
121
  msg = (
130
- f"Data serialized with name '{cast(str, _type_)}' is not "
122
+ f"Data serialized with name '{cast('str', _type_)}' is not "
131
123
  "deserializable. Please register a "
132
124
  "custom transcoding for this type."
133
125
  )
134
- raise TypeError(msg) from e
126
+ raise TranscodingNotRegisteredError(msg) from e
135
127
  else:
136
128
  return transcoding.decode(_data_)
137
129
  else:
@@ -139,9 +131,7 @@ class JSONTranscoder(Transcoder):
139
131
 
140
132
 
141
133
  class UUIDAsHex(Transcoding):
142
- """
143
- Transcoding that represents :class:`UUID` objects as hex values.
144
- """
134
+ """Transcoding that represents :class:`UUID` objects as hex values."""
145
135
 
146
136
  type = UUID
147
137
  name = "uuid_hex"
@@ -155,9 +145,7 @@ class UUIDAsHex(Transcoding):
155
145
 
156
146
 
157
147
  class DecimalAsStr(Transcoding):
158
- """
159
- Transcoding that represents :class:`Decimal` objects as strings.
160
- """
148
+ """Transcoding that represents :class:`Decimal` objects as strings."""
161
149
 
162
150
  type = Decimal
163
151
  name = "decimal_str"
@@ -170,9 +158,7 @@ class DecimalAsStr(Transcoding):
170
158
 
171
159
 
172
160
  class DatetimeAsISO(Transcoding):
173
- """
174
- Transcoding that represents :class:`datetime` objects as ISO strings.
175
- """
161
+ """Transcoding that represents :class:`datetime` objects as ISO strings."""
176
162
 
177
163
  type = datetime
178
164
  name = "datetime_iso"
@@ -187,8 +173,7 @@ class DatetimeAsISO(Transcoding):
187
173
 
188
174
  @dataclass(frozen=True)
189
175
  class StoredEvent:
190
- """
191
- Frozen dataclass that represents :class:`~eventsourcing.domain.DomainEvent`
176
+ """Frozen dataclass that represents :class:`~eventsourcing.domain.DomainEvent`
192
177
  objects, such as aggregate :class:`~eventsourcing.domain.Aggregate.Event`
193
178
  objects and :class:`~eventsourcing.domain.Snapshot` objects.
194
179
  """
@@ -204,50 +189,39 @@ class StoredEvent:
204
189
 
205
190
 
206
191
  class Compressor(ABC):
207
- """
208
- Base class for compressors.
209
- """
192
+ """Base class for compressors."""
210
193
 
211
194
  @abstractmethod
212
195
  def compress(self, data: bytes) -> bytes:
213
- """
214
- Compress bytes.
215
- """
196
+ """Compress bytes."""
216
197
 
217
198
  @abstractmethod
218
199
  def decompress(self, data: bytes) -> bytes:
219
- """
220
- Decompress bytes.
221
- """
200
+ """Decompress bytes."""
222
201
 
223
202
 
224
203
  class Cipher(ABC):
225
- """
226
- Base class for ciphers.
227
- """
204
+ """Base class for ciphers."""
228
205
 
229
206
  @abstractmethod
230
207
  def __init__(self, environment: Environment):
231
- """
232
- Initialises cipher with given environment.
233
- """
208
+ """Initialises cipher with given environment."""
234
209
 
235
210
  @abstractmethod
236
211
  def encrypt(self, plaintext: bytes) -> bytes:
237
- """
238
- Return ciphertext for given plaintext.
239
- """
212
+ """Return ciphertext for given plaintext."""
240
213
 
241
214
  @abstractmethod
242
215
  def decrypt(self, ciphertext: bytes) -> bytes:
243
- """
244
- Return plaintext for given ciphertext.
245
- """
216
+ """Return plaintext for given ciphertext."""
217
+
218
+
219
+ class MapperDeserialisationError(EventSourcingError, ValueError):
220
+ """Raised when deserialization fails in a Mapper."""
246
221
 
247
222
 
248
223
  class Mapper:
249
- """
250
- Converts between domain event objects and :class:`StoredEvent` objects.
224
+ """Converts between domain event objects and :class:`StoredEvent` objects.
251
225
 
252
226
  Uses a :class:`Transcoder`, and optionally a cryptographic cipher and compressor.
253
227
  """
@@ -263,9 +237,7 @@ class Mapper:
263
237
  self.cipher = cipher
264
238
 
265
239
  def to_stored_event(self, domain_event: DomainEventProtocol) -> StoredEvent:
266
- """
267
- Converts the given domain event to a :class:`StoredEvent` object.
268
- """
240
+ """Converts the given domain event to a :class:`StoredEvent` object."""
269
241
  topic = get_topic(domain_event.__class__)
270
242
  event_state = domain_event.__dict__.copy()
271
243
  originator_id = event_state.pop("originator_id")
@@ -286,15 +258,23 @@ class Mapper:
286
258
  )
287
259
 
288
260
  def to_domain_event(self, stored_event: StoredEvent) -> DomainEventProtocol:
289
- """
290
- Converts the given :class:`StoredEvent` to a domain event object.
291
- """
261
+ """Converts the given :class:`StoredEvent` to a domain event object."""
292
262
  stored_state = stored_event.state
293
- if self.cipher:
294
- stored_state = self.cipher.decrypt(stored_state)
295
- if self.compressor:
296
- stored_state = self.compressor.decompress(stored_state)
297
- event_state: dict[str, Any] = self.transcoder.decode(stored_state)
263
+ try:
264
+ if self.cipher:
265
+ stored_state = self.cipher.decrypt(stored_state)
266
+ if self.compressor:
267
+ stored_state = self.compressor.decompress(stored_state)
268
+ event_state: dict[str, Any] = self.transcoder.decode(stored_state)
269
+ except Exception as e:
270
+ msg = (
271
+ f"Failed to deserialise state of stored event with "
272
+ f"topic '{stored_event.topic}', "
273
+ f"originator_id '{stored_event.originator_id}' and "
274
+ f"originator_version {stored_event.originator_version}: {e}"
275
+ )
276
+ raise MapperDeserialisationError(msg) from e
277
+
298
278
  event_state["originator_id"] = stored_event.originator_id
299
279
  event_state["originator_version"] = stored_event.originator_version
300
280
  cls = resolve_topic(stored_event.topic)
@@ -310,42 +290,34 @@ class Mapper:
310
290
 
311
291
 
312
292
  class RecordConflictError(EventSourcingError):
313
- """
314
- Legacy exception, replaced with IntegrityError.
315
- """
293
+ """Legacy exception, replaced with IntegrityError."""
316
294
 
317
295
 
318
296
  class PersistenceError(EventSourcingError):
319
- """
320
- The base class of the other exceptions in this module.
297
+ """The base class of the other exceptions in this module.
321
298
 
322
299
  Exception class names follow https://www.python.org/dev/peps/pep-0249/#exceptions
323
300
  """
324
301
 
325
302
 
326
303
  class InterfaceError(PersistenceError):
327
- """
328
- Exception raised for errors that are related to the database
304
+ """Exception raised for errors that are related to the database
329
305
  interface rather than the database itself.
330
306
  """
331
307
 
332
308
 
333
309
  class DatabaseError(PersistenceError):
334
- """
335
- Exception raised for errors that are related to the database.
336
- """
310
+ """Exception raised for errors that are related to the database."""
337
311
 
338
312
 
339
313
  class DataError(DatabaseError):
340
- """
341
- Exception raised for errors that are due to problems with the
314
+ """Exception raised for errors that are due to problems with the
342
315
  processed data like division by zero, numeric value out of range, etc.
343
316
  """
344
317
 
345
318
 
346
319
  class OperationalError(DatabaseError):
347
- """
348
- Exception raised for errors that are related to the database's
320
+ """Exception raised for errors that are related to the database's
349
321
  operation and not necessarily under the control of the programmer,
350
322
  e.g. an unexpected disconnect occurs, the data source name is not
351
323
  found, a transaction could not be processed, a memory allocation
@@ -354,31 +326,27 @@ class OperationalError(DatabaseError):
354
326
 
355
327
 
356
328
  class IntegrityError(DatabaseError, RecordConflictError):
357
- """
358
- Exception raised when the relational integrity of the
329
+ """Exception raised when the relational integrity of the
359
330
  database is affected, e.g. a foreign key check fails.
360
331
  """
361
332
 
362
333
 
363
334
  class InternalError(DatabaseError):
364
- """
365
- Exception raised when the database encounters an internal
335
+ """Exception raised when the database encounters an internal
366
336
  error, e.g. the cursor is not valid anymore, the transaction
367
337
  is out of sync, etc.
368
338
  """
369
339
 
370
340
 
371
341
  class ProgrammingError(DatabaseError):
372
- """
373
- Exception raised for database programming errors, e.g. table
342
+ """Exception raised for database programming errors, e.g. table
374
343
  not found or already exists, syntax error in the SQL statement,
375
344
  wrong number of parameters specified, etc.
376
345
  """
377
346
 
378
347
 
379
348
  class NotSupportedError(DatabaseError):
380
- """
381
- Exception raised in case a method or database API was used
349
+ """Exception raised in case a method or database API was used
382
350
  which is not supported by the database, e.g. calling the
383
351
  rollback() method on a connection that does not support
384
352
  transaction or has transactions turned off.
@@ -386,27 +354,21 @@ class NotSupportedError(DatabaseError):
386
354
 
387
355
 
388
356
  class WaitInterruptedError(PersistenceError):
389
- """
390
- Raised when waiting for a tracking record is interrupted.
391
- """
357
+ """Raised when waiting for a tracking record is interrupted."""
392
358
 
393
359
 
394
360
  class Recorder:
395
361
  pass
396
362
 
397
363
 
398
- class AggregateRecorder(ABC):
399
- """
400
- Abstract base class for inserting and selecting stored events.
401
- """
364
+ class AggregateRecorder(Recorder, ABC):
365
+ """Abstract base class for inserting and selecting stored events."""
402
366
 
403
367
  @abstractmethod
404
368
  def insert_events(
405
369
  self, stored_events: list[StoredEvent], **kwargs: Any
406
370
  ) -> Sequence[int] | None:
407
- """
408
- Writes stored events into database.
409
- """
371
+ """Writes stored events into database."""
410
372
 
411
373
  @abstractmethod
412
374
  def select_events(
@@ -418,24 +380,19 @@ class AggregateRecorder(ABC):
418
380
  desc: bool = False,
419
381
  limit: int | None = None,
420
382
  ) -> list[StoredEvent]:
421
- """
422
- Reads stored events from database.
423
- """
383
+ """Reads stored events from database."""
424
384
 
425
385
 
426
386
  @dataclass(frozen=True)
427
387
  class Notification(StoredEvent):
428
- """
429
- Frozen dataclass that represents domain event notifications.
430
- """
388
+ """Frozen dataclass that represents domain event notifications."""
431
389
 
432
390
  id: int
433
391
  """Position in an application sequence."""
434
392
 
435
393
 
436
394
  class ApplicationRecorder(AggregateRecorder):
437
- """
438
- Abstract base class for recording events in both aggregate
395
+ """Abstract base class for recording events in both aggregate
439
396
  and application sequences.
440
397
  """
441
398
 
@@ -449,8 +406,7 @@ class ApplicationRecorder(AggregateRecorder):
449
406
  *,
450
407
  inclusive_of_start: bool = True,
451
408
  ) -> list[Notification]:
452
- """
453
- Returns a list of Notification objects representing events from an
409
+ """Returns a list of Notification objects representing events from an
454
410
  application sequence. If `inclusive_of_start` is True (the default),
455
411
  the returned Notification objects will have IDs greater than or equal
456
412
  to `start` and less than or equal to `stop`. If `inclusive_of_start`
@@ -460,8 +416,7 @@ class ApplicationRecorder(AggregateRecorder):
460
416
 
461
417
  @abstractmethod
462
418
  def max_notification_id(self) -> int | None:
463
- """
464
- Returns the largest notification ID in an application sequence,
419
+ """Returns the largest notification ID in an application sequence,
465
420
  or None if no stored events have been recorded.
466
421
  """
467
422
 
@@ -469,8 +424,7 @@ class ApplicationRecorder(AggregateRecorder):
469
424
  def subscribe(
470
425
  self, gt: int | None = None, topics: Sequence[str] = ()
471
426
  ) -> Subscription[ApplicationRecorder]:
472
- """
473
- Returns an iterator of Notification objects representing events from an
427
+ """Returns an iterator of Notification objects representing events from an
474
428
  application sequence.
475
429
 
476
430
  The iterator will block after the last recorded event has been yielded, but
@@ -481,21 +435,17 @@ class ApplicationRecorder(AggregateRecorder):
481
435
 
482
436
 
483
437
  class TrackingRecorder(Recorder, ABC):
484
- """
485
- Abstract base class for recorders that record tracking
438
+ """Abstract base class for recorders that record tracking
486
439
  objects atomically with other state.
487
440
  """
488
441
 
489
442
  @abstractmethod
490
443
  def insert_tracking(self, tracking: Tracking) -> None:
491
- """
492
- Records a tracking object.
493
- """
444
+ """Records a tracking object."""
494
445
 
495
446
  @abstractmethod
496
447
  def max_tracking_id(self, application_name: str) -> int | None:
497
- """
498
- Returns the largest notification ID across all recorded tracking objects
448
+ """Returns the largest notification ID across all recorded tracking objects
499
449
  for the named application, or None if no tracking objects have been recorded.
500
450
  """
501
451
 
@@ -503,8 +453,7 @@ class TrackingRecorder(Recorder, ABC):
503
453
  def has_tracking_id(
504
454
  self, application_name: str, notification_id: int | None
505
455
  ) -> bool:
506
- """
507
- Returns True if a tracking object with the given application name
456
+ """Returns True if a tracking object with the given application name
508
457
  and notification ID has been recorded, and True if given notification_id is
509
458
  None, otherwise returns False.
510
459
  """
@@ -516,8 +465,7 @@ class TrackingRecorder(Recorder, ABC):
516
465
  timeout: float = 1.0,
517
466
  interrupt: Event | None = None,
518
467
  ) -> None:
519
- """
520
- Block until a tracking object with the given application name and a
468
+ """Block until a tracking object with the given application name and a
521
469
  notification ID greater than equal to the given value has been recorded.
522
470
 
523
471
  Polls max_tracking_id() with exponential backoff until the timeout
@@ -532,7 +480,8 @@ class TrackingRecorder(Recorder, ABC):
532
480
  Raises WaitInterruptError if the `interrupt` is set before `timeout` is reached.
533
481
  """
534
482
  deadline = monotonic() + timeout
535
- delay_ms = 1.0
483
+ sleep_interval_ms = 100.0
484
+ max_sleep_interval_ms = 800.0
536
485
  while True:
537
486
  max_tracking_id = self.max_tracking_id(application_name)
538
487
  if notification_id is None or (
@@ -540,11 +489,10 @@ class TrackingRecorder(Recorder, ABC):
540
489
  ):
541
490
  break
542
491
  if interrupt:
543
- if interrupt.wait(timeout=delay_ms / 1000):
492
+ if interrupt.wait(timeout=sleep_interval_ms / 1000):
544
493
  raise WaitInterruptedError
545
494
  else:
546
- sleep(delay_ms / 1000)
547
- delay_ms *= 2
495
+ sleep(sleep_interval_ms / 1000)
548
496
  remaining = deadline - monotonic()
549
497
  if remaining < 0:
550
498
  msg = (
@@ -552,7 +500,9 @@ class TrackingRecorder(Recorder, ABC):
552
500
  f"from application '{application_name}' to be processed"
553
501
  )
554
502
  raise TimeoutError(msg)
555
- delay_ms = min(delay_ms, remaining * 1000)
503
+ sleep_interval_ms = min(
504
+ sleep_interval_ms * 2, remaining * 1000, max_sleep_interval_ms
505
+ )
556
506
 
557
507
 
558
508
  class ProcessRecorder(TrackingRecorder, ApplicationRecorder, ABC):
@@ -570,9 +520,7 @@ class Recording:
570
520
 
571
521
 
572
522
  class EventStore:
573
- """
574
- Stores and retrieves domain events.
575
- """
523
+ """Stores and retrieves domain events."""
576
524
 
577
525
  def __init__(
578
526
  self,
@@ -585,9 +533,7 @@ class EventStore:
585
533
  def put(
586
534
  self, domain_events: Sequence[DomainEventProtocol], **kwargs: Any
587
535
  ) -> list[Recording]:
588
- """
589
- Stores domain events in aggregate sequence.
590
- """
536
+ """Stores domain events in aggregate sequence."""
591
537
  stored_events = list(map(self.mapper.to_stored_event, domain_events))
592
538
  recordings = []
593
539
  notification_ids = self.recorder.insert_events(stored_events, **kwargs)
@@ -617,9 +563,7 @@ class EventStore:
617
563
  desc: bool = False,
618
564
  limit: int | None = None,
619
565
  ) -> Iterator[DomainEventProtocol]:
620
- """
621
- Retrieves domain events from aggregate sequence.
622
- """
566
+ """Retrieves domain events from aggregate sequence."""
623
567
  return map(
624
568
  self.mapper.to_domain_event,
625
569
  self.recorder.select_events(
@@ -637,10 +581,12 @@ TTrackingRecorder = TypeVar(
637
581
  )
638
582
 
639
583
 
584
+ class InfrastructureFactoryError(EventSourcingError):
585
+ """Raised when an infrastructure factory cannot be created."""
586
+
587
+
640
588
  class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
641
- """
642
- Abstract base class for infrastructure factories.
643
- """
589
+ """Abstract base class for infrastructure factories."""
644
590
 
645
591
  PERSISTENCE_MODULE = "PERSISTENCE_MODULE"
646
592
  TRANSCODER_TOPIC = "TRANSCODER_TOPIC"
@@ -657,8 +603,7 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
657
603
  cls: type[InfrastructureFactory[TTrackingRecorder]],
658
604
  env: Environment | None = None,
659
605
  ) -> InfrastructureFactory[TTrackingRecorder]:
660
- """
661
- Constructs concrete infrastructure factory for given
606
+ """Constructs concrete infrastructure factory for given
662
607
  named application. Reads and resolves persistence
663
608
  topic from environment variable 'PERSISTENCE_MODULE'.
664
609
  """
@@ -690,7 +635,7 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
690
635
  f"'{topic}' from environment "
691
636
  f"variable '{cls.PERSISTENCE_MODULE}'"
692
637
  )
693
- raise OSError(msg) from e
638
+ raise InfrastructureFactoryError(msg) from e
694
639
 
695
640
  if isinstance(obj, ModuleType):
696
641
  # Find the factory in the module.
@@ -715,26 +660,25 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
715
660
  f"Found {len(factory_classes)} infrastructure factory classes in"
716
661
  f" '{topic}', expected 1."
717
662
  )
718
- raise AssertionError(msg)
663
+ raise InfrastructureFactoryError(msg)
719
664
  elif isinstance(obj, type) and issubclass(obj, InfrastructureFactory):
720
665
  factory_cls = obj
721
666
  else:
722
- msg = f"Not an infrastructure factory class or module: {topic}"
723
- raise AssertionError(msg)
667
+ msg = (
668
+ f"Topic '{topic}' didn't resolve to a persistence module "
669
+ f"or infrastructure factory class: {obj}"
670
+ )
671
+ raise InfrastructureFactoryError(msg)
724
672
  return factory_cls(env=env)
725
673
 
726
674
  def __init__(self, env: Environment):
727
- """
728
- Initialises infrastructure factory object with given application name.
729
- """
675
+ """Initialises infrastructure factory object with given application name."""
730
676
  self.env = env
731
677
 
732
678
  def transcoder(
733
679
  self,
734
680
  ) -> Transcoder:
735
- """
736
- Constructs a transcoder.
737
- """
681
+ """Constructs a transcoder."""
738
682
  transcoder_topic = self.env.get(self.TRANSCODER_TOPIC)
739
683
  if transcoder_topic:
740
684
  transcoder_class: type[Transcoder] = resolve_topic(transcoder_topic)
@@ -747,9 +691,7 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
747
691
  transcoder: Transcoder | None = None,
748
692
  mapper_class: type[Mapper] | None = None,
749
693
  ) -> Mapper:
750
- """
751
- Constructs a mapper.
752
- """
694
+ """Constructs a mapper."""
753
695
  if mapper_class is None:
754
696
  mapper_topic = self.env.get(self.MAPPER_TOPIC)
755
697
  mapper_class = resolve_topic(mapper_topic) if mapper_topic else Mapper
@@ -762,8 +704,7 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
762
704
  )
763
705
 
764
706
  def cipher(self) -> Cipher | None:
765
- """
766
- Reads environment variables 'CIPHER_TOPIC'
707
+ """Reads environment variables 'CIPHER_TOPIC'
767
708
  and 'CIPHER_KEY' to decide whether or not
768
709
  to construct a cipher.
769
710
  """
@@ -780,8 +721,7 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
780
721
  return cipher
781
722
 
782
723
  def compressor(self) -> Compressor | None:
783
- """
784
- Reads environment variable 'COMPRESSOR_TOPIC' to
724
+ """Reads environment variable 'COMPRESSOR_TOPIC' to
785
725
  decide whether or not to construct a compressor.
786
726
  """
787
727
  compressor: Compressor | None = None
@@ -801,9 +741,7 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
801
741
  mapper: Mapper | None = None,
802
742
  recorder: AggregateRecorder | None = None,
803
743
  ) -> EventStore:
804
- """
805
- Constructs an event store.
806
- """
744
+ """Constructs an event store."""
807
745
  return EventStore(
808
746
  mapper=mapper or self.mapper(),
809
747
  recorder=recorder or self.application_recorder(),
@@ -811,48 +749,36 @@ class InfrastructureFactory(ABC, Generic[TTrackingRecorder]):
811
749
 
812
750
  @abstractmethod
813
751
  def aggregate_recorder(self, purpose: str = "events") -> AggregateRecorder:
814
- """
815
- Constructs an aggregate recorder.
816
- """
752
+ """Constructs an aggregate recorder."""
817
753
 
818
754
  @abstractmethod
819
755
  def application_recorder(self) -> ApplicationRecorder:
820
- """
821
- Constructs an application recorder.
822
- """
756
+ """Constructs an application recorder."""
823
757
 
824
758
  @abstractmethod
825
759
  def tracking_recorder(
826
760
  self, tracking_recorder_class: type[TTrackingRecorder] | None = None
827
761
  ) -> TTrackingRecorder:
828
- """
829
- Constructs a tracking recorder.
830
- """
762
+ """Constructs a tracking recorder."""
831
763
 
832
764
  @abstractmethod
833
765
  def process_recorder(self) -> ProcessRecorder:
834
- """
835
- Constructs a process recorder.
836
- """
766
+ """Constructs a process recorder."""
837
767
 
838
768
  def is_snapshotting_enabled(self) -> bool:
839
- """
840
- Decides whether or not snapshotting is enabled by
769
+ """Decides whether or not snapshotting is enabled by
841
770
  reading environment variable 'IS_SNAPSHOTTING_ENABLED'.
842
771
  Snapshotting is not enabled by default.
843
772
  """
844
773
  return strtobool(self.env.get(self.IS_SNAPSHOTTING_ENABLED, "no"))
845
774
 
846
775
  def close(self) -> None:
847
- """
848
- Closes any database connections, and anything else that needs closing.
849
- """
776
+ """Closes any database connections, and anything else that needs closing."""
850
777
 
851
778
 
852
779
  @dataclass(frozen=True)
853
780
  class Tracking:
854
- """
855
- Frozen dataclass representing the position of a domain
781
+ """Frozen dataclass representing the position of a domain
856
782
  event :class:`Notification` in an application's notification log.
857
783
  """
858
784
 
@@ -939,20 +865,15 @@ TConnection = TypeVar("TConnection", bound=Connection[Any])
939
865
 
940
866
 
941
867
  class ConnectionPoolClosedError(EventSourcingError):
942
- """
943
- Raised when using a connection pool that is already closed.
944
- """
868
+ """Raised when using a connection pool that is already closed."""
945
869
 
946
870
 
947
871
  class ConnectionNotFromPoolError(EventSourcingError):
948
- """
949
- Raised when putting a connection in the wrong pool.
950
- """
872
+ """Raised when putting a connection in the wrong pool."""
951
873
 
952
874
 
953
875
  class ConnectionUnavailableError(OperationalError, TimeoutError):
954
- """
955
- Raised when a request to get a connection from a
876
+ """Raised when a request to get a connection from a
956
877
  connection pool times out.
957
878
  """
958
879
 
@@ -968,8 +889,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
968
889
  pre_ping: bool = False,
969
890
  mutually_exclusive_read_write: bool = False,
970
891
  ) -> None:
971
- """
972
- Initialises a new connection pool.
892
+ """Initialises a new connection pool.
973
893
 
974
894
  The 'pool_size' argument specifies the maximum number of connections
975
895
  that will be put into the pool when connections are returned. The
@@ -1020,9 +940,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1020
940
 
1021
941
  @property
1022
942
  def num_in_use(self) -> int:
1023
- """
1024
- Indicates the total number of connections currently in use.
1025
- """
943
+ """Indicates the total number of connections currently in use."""
1026
944
  with self._put_condition:
1027
945
  return self._num_in_use
1028
946
 
@@ -1032,9 +950,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1032
950
 
1033
951
  @property
1034
952
  def num_in_pool(self) -> int:
1035
- """
1036
- Indicates the number of connections currently in the pool.
1037
- """
953
+ """Indicates the number of connections currently in the pool."""
1038
954
  with self._put_condition:
1039
955
  return self._num_in_pool
1040
956
 
@@ -1053,8 +969,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1053
969
  def get_connection(
1054
970
  self, timeout: float | None = None, is_writer: bool | None = None
1055
971
  ) -> TConnection:
1056
- """
1057
- Issues connections, or raises ConnectionPoolExhausted error.
972
+ """Issues connections, or raises ConnectionPoolExhausted error.
1058
973
  Provides "fairness" on attempts to get connections, meaning that
1059
974
  connections are issued in the same order as they are requested.
1060
975
 
@@ -1138,8 +1053,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1138
1053
  raise ConnectionUnavailableError(msg)
1139
1054
 
1140
1055
  def _get_connection(self, timeout: float = 0.0) -> TConnection:
1141
- """
1142
- Gets or creates connections from pool within given
1056
+ """Gets or creates connections from pool within given
1143
1057
  time, otherwise raises a "pool exhausted" error.
1144
1058
 
1145
1059
  Waits for connections to be returned if the pool
@@ -1206,8 +1120,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1206
1120
  return conn
1207
1121
 
1208
1122
  def put_connection(self, conn: TConnection) -> None:
1209
- """
1210
- Returns connections to the pool, or closes connection
1123
+ """Returns connections to the pool, or closes connection
1211
1124
  if the pool is full.
1212
1125
 
1213
1126
  Unlocks write lock after writer has returned, and
@@ -1216,7 +1129,6 @@ class ConnectionPool(ABC, Generic[TConnection]):
1216
1129
  Notifies waiters when connections have been returned,
1217
1130
  and when there are no longer any readers.
1218
1131
  """
1219
-
1220
1132
  # Start forgetting if this connection was for reading or writing.
1221
1133
  is_writer, conn.is_writer = conn.is_writer, None
1222
1134
 
@@ -1263,8 +1175,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1263
1175
 
1264
1176
  @abstractmethod
1265
1177
  def _create_connection(self) -> TConnection:
1266
- """
1267
- Create a new connection.
1178
+ """Create a new connection.
1268
1179
 
1269
1180
  Subclasses should implement this method by
1270
1181
  creating a database connection of the type
@@ -1272,9 +1183,7 @@ class ConnectionPool(ABC, Generic[TConnection]):
1272
1183
  """
1273
1184
 
1274
1185
  def close(self) -> None:
1275
- """
1276
- Close the connection pool.
1277
- """
1186
+ """Close the connection pool."""
1278
1187
  with self._put_condition:
1279
1188
  if self._closed:
1280
1189
  return
@@ -1329,9 +1238,7 @@ class Subscription(Iterator[Notification], Generic[TApplicationRecorder_co]):
1329
1238
  self.stop()
1330
1239
 
1331
1240
  def stop(self) -> None:
1332
- """
1333
- Stops the subscription.
1334
- """
1241
+ """Stops the subscription."""
1335
1242
  self._has_been_stopped = True
1336
1243
 
1337
1244
  def __iter__(self) -> Self:
@@ -1339,9 +1246,7 @@ class Subscription(Iterator[Notification], Generic[TApplicationRecorder_co]):
1339
1246
 
1340
1247
  @abstractmethod
1341
1248
  def __next__(self) -> Notification:
1342
- """
1343
- Returns the next Notification object in the application sequence.
1344
- """
1249
+ """Returns the next Notification object in the application sequence."""
1345
1250
 
1346
1251
 
1347
1252
  class ListenNotifySubscription(Subscription[TApplicationRecorder_co]):
@@ -1366,9 +1271,7 @@ class ListenNotifySubscription(Subscription[TApplicationRecorder_co]):
1366
1271
  self._pull_thread.join()
1367
1272
 
1368
1273
  def stop(self) -> None:
1369
- """
1370
- Stops the subscription.
1371
- """
1274
+ """Stops the subscription."""
1372
1275
  super().stop()
1373
1276
  self._notifications_queue.put([])
1374
1277
  self._has_been_notified.set()