eventsourcing 9.2.22__py3-none-any.whl → 9.3.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.

Files changed (144) hide show
  1. eventsourcing/__init__.py +1 -1
  2. eventsourcing/application.py +116 -135
  3. eventsourcing/cipher.py +15 -12
  4. eventsourcing/dispatch.py +31 -91
  5. eventsourcing/domain.py +220 -226
  6. eventsourcing/examples/__init__.py +0 -0
  7. eventsourcing/examples/aggregate1/__init__.py +0 -0
  8. eventsourcing/examples/aggregate1/application.py +27 -0
  9. eventsourcing/examples/aggregate1/domainmodel.py +16 -0
  10. eventsourcing/examples/aggregate1/test_application.py +37 -0
  11. eventsourcing/examples/aggregate2/__init__.py +0 -0
  12. eventsourcing/examples/aggregate2/application.py +27 -0
  13. eventsourcing/examples/aggregate2/domainmodel.py +22 -0
  14. eventsourcing/examples/aggregate2/test_application.py +37 -0
  15. eventsourcing/examples/aggregate3/__init__.py +0 -0
  16. eventsourcing/examples/aggregate3/application.py +27 -0
  17. eventsourcing/examples/aggregate3/domainmodel.py +38 -0
  18. eventsourcing/examples/aggregate3/test_application.py +37 -0
  19. eventsourcing/examples/aggregate4/__init__.py +0 -0
  20. eventsourcing/examples/aggregate4/application.py +27 -0
  21. eventsourcing/examples/aggregate4/domainmodel.py +114 -0
  22. eventsourcing/examples/aggregate4/test_application.py +38 -0
  23. eventsourcing/examples/aggregate5/__init__.py +0 -0
  24. eventsourcing/examples/aggregate5/application.py +27 -0
  25. eventsourcing/examples/aggregate5/domainmodel.py +131 -0
  26. eventsourcing/examples/aggregate5/test_application.py +38 -0
  27. eventsourcing/examples/aggregate6/__init__.py +0 -0
  28. eventsourcing/examples/aggregate6/application.py +30 -0
  29. eventsourcing/examples/aggregate6/domainmodel.py +123 -0
  30. eventsourcing/examples/aggregate6/test_application.py +38 -0
  31. eventsourcing/examples/aggregate6a/__init__.py +0 -0
  32. eventsourcing/examples/aggregate6a/application.py +40 -0
  33. eventsourcing/examples/aggregate6a/domainmodel.py +149 -0
  34. eventsourcing/examples/aggregate6a/test_application.py +45 -0
  35. eventsourcing/examples/aggregate7/__init__.py +0 -0
  36. eventsourcing/examples/aggregate7/application.py +48 -0
  37. eventsourcing/examples/aggregate7/domainmodel.py +144 -0
  38. eventsourcing/examples/aggregate7/persistence.py +57 -0
  39. eventsourcing/examples/aggregate7/test_application.py +38 -0
  40. eventsourcing/examples/aggregate7/test_compression_and_encryption.py +45 -0
  41. eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +67 -0
  42. eventsourcing/examples/aggregate7a/__init__.py +0 -0
  43. eventsourcing/examples/aggregate7a/application.py +56 -0
  44. eventsourcing/examples/aggregate7a/domainmodel.py +170 -0
  45. eventsourcing/examples/aggregate7a/test_application.py +46 -0
  46. eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +45 -0
  47. eventsourcing/examples/aggregate8/__init__.py +0 -0
  48. eventsourcing/examples/aggregate8/application.py +47 -0
  49. eventsourcing/examples/aggregate8/domainmodel.py +65 -0
  50. eventsourcing/examples/aggregate8/persistence.py +57 -0
  51. eventsourcing/examples/aggregate8/test_application.py +37 -0
  52. eventsourcing/examples/aggregate8/test_compression_and_encryption.py +44 -0
  53. eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +38 -0
  54. eventsourcing/examples/bankaccounts/__init__.py +0 -0
  55. eventsourcing/examples/bankaccounts/application.py +70 -0
  56. eventsourcing/examples/bankaccounts/domainmodel.py +56 -0
  57. eventsourcing/examples/bankaccounts/test.py +173 -0
  58. eventsourcing/examples/cargoshipping/__init__.py +0 -0
  59. eventsourcing/examples/cargoshipping/application.py +126 -0
  60. eventsourcing/examples/cargoshipping/domainmodel.py +330 -0
  61. eventsourcing/examples/cargoshipping/interface.py +143 -0
  62. eventsourcing/examples/cargoshipping/test.py +231 -0
  63. eventsourcing/examples/contentmanagement/__init__.py +0 -0
  64. eventsourcing/examples/contentmanagement/application.py +118 -0
  65. eventsourcing/examples/contentmanagement/domainmodel.py +69 -0
  66. eventsourcing/examples/contentmanagement/test.py +180 -0
  67. eventsourcing/examples/contentmanagement/utils.py +26 -0
  68. eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
  69. eventsourcing/examples/contentmanagementsystem/application.py +54 -0
  70. eventsourcing/examples/contentmanagementsystem/postgres.py +17 -0
  71. eventsourcing/examples/contentmanagementsystem/sqlite.py +17 -0
  72. eventsourcing/examples/contentmanagementsystem/system.py +14 -0
  73. eventsourcing/examples/contentmanagementsystem/test_system.py +180 -0
  74. eventsourcing/examples/searchablecontent/__init__.py +0 -0
  75. eventsourcing/examples/searchablecontent/application.py +45 -0
  76. eventsourcing/examples/searchablecontent/persistence.py +23 -0
  77. eventsourcing/examples/searchablecontent/postgres.py +118 -0
  78. eventsourcing/examples/searchablecontent/sqlite.py +136 -0
  79. eventsourcing/examples/searchablecontent/test_application.py +110 -0
  80. eventsourcing/examples/searchablecontent/test_recorder.py +68 -0
  81. eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
  82. eventsourcing/examples/searchabletimestamps/application.py +32 -0
  83. eventsourcing/examples/searchabletimestamps/persistence.py +20 -0
  84. eventsourcing/examples/searchabletimestamps/postgres.py +110 -0
  85. eventsourcing/examples/searchabletimestamps/sqlite.py +99 -0
  86. eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +94 -0
  87. eventsourcing/examples/test_invoice.py +176 -0
  88. eventsourcing/examples/test_parking_lot.py +206 -0
  89. eventsourcing/interface.py +2 -2
  90. eventsourcing/persistence.py +85 -81
  91. eventsourcing/popo.py +30 -31
  92. eventsourcing/postgres.py +379 -590
  93. eventsourcing/sqlite.py +91 -99
  94. eventsourcing/system.py +52 -57
  95. eventsourcing/tests/application.py +20 -32
  96. eventsourcing/tests/application_tests/__init__.py +0 -0
  97. eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +55 -0
  98. eventsourcing/tests/application_tests/test_application_with_popo.py +22 -0
  99. eventsourcing/tests/application_tests/test_application_with_postgres.py +75 -0
  100. eventsourcing/tests/application_tests/test_application_with_sqlite.py +72 -0
  101. eventsourcing/tests/application_tests/test_cache.py +134 -0
  102. eventsourcing/tests/application_tests/test_event_sourced_log.py +162 -0
  103. eventsourcing/tests/application_tests/test_notificationlog.py +232 -0
  104. eventsourcing/tests/application_tests/test_notificationlogreader.py +126 -0
  105. eventsourcing/tests/application_tests/test_processapplication.py +110 -0
  106. eventsourcing/tests/application_tests/test_processingpolicy.py +109 -0
  107. eventsourcing/tests/application_tests/test_repository.py +504 -0
  108. eventsourcing/tests/application_tests/test_snapshotting.py +68 -0
  109. eventsourcing/tests/application_tests/test_upcasting.py +459 -0
  110. eventsourcing/tests/docs_tests/__init__.py +0 -0
  111. eventsourcing/tests/docs_tests/test_docs.py +293 -0
  112. eventsourcing/tests/domain.py +1 -1
  113. eventsourcing/tests/domain_tests/__init__.py +0 -0
  114. eventsourcing/tests/domain_tests/test_aggregate.py +1180 -0
  115. eventsourcing/tests/domain_tests/test_aggregate_decorators.py +1604 -0
  116. eventsourcing/tests/domain_tests/test_domainevent.py +80 -0
  117. eventsourcing/tests/interface_tests/__init__.py +0 -0
  118. eventsourcing/tests/interface_tests/test_remotenotificationlog.py +258 -0
  119. eventsourcing/tests/persistence.py +52 -50
  120. eventsourcing/tests/persistence_tests/__init__.py +0 -0
  121. eventsourcing/tests/persistence_tests/test_aes.py +93 -0
  122. eventsourcing/tests/persistence_tests/test_connection_pool.py +722 -0
  123. eventsourcing/tests/persistence_tests/test_eventstore.py +72 -0
  124. eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +21 -0
  125. eventsourcing/tests/persistence_tests/test_mapper.py +113 -0
  126. eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +69 -0
  127. eventsourcing/tests/persistence_tests/test_popo.py +124 -0
  128. eventsourcing/tests/persistence_tests/test_postgres.py +1119 -0
  129. eventsourcing/tests/persistence_tests/test_sqlite.py +348 -0
  130. eventsourcing/tests/persistence_tests/test_transcoder.py +44 -0
  131. eventsourcing/tests/postgres_utils.py +7 -7
  132. eventsourcing/tests/system_tests/__init__.py +0 -0
  133. eventsourcing/tests/system_tests/test_runner.py +935 -0
  134. eventsourcing/tests/system_tests/test_system.py +284 -0
  135. eventsourcing/tests/utils_tests/__init__.py +0 -0
  136. eventsourcing/tests/utils_tests/test_utils.py +226 -0
  137. eventsourcing/utils.py +47 -50
  138. {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/METADATA +29 -79
  139. eventsourcing-9.3.0.dist-info/RECORD +145 -0
  140. {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/WHEEL +1 -2
  141. eventsourcing-9.2.22.dist-info/RECORD +0 -25
  142. eventsourcing-9.2.22.dist-info/top_level.txt +0 -1
  143. {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/AUTHORS +0 -0
  144. {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0.dist-info}/LICENSE +0 -0
eventsourcing/sqlite.py CHANGED
@@ -2,17 +2,15 @@ from __future__ import annotations
2
2
 
3
3
  import sqlite3
4
4
  from contextlib import contextmanager
5
- from sqlite3 import Connection, Cursor
6
- from types import TracebackType
7
- from typing import Any, Iterator, List, Optional, Sequence, Type, Union
5
+ from typing import TYPE_CHECKING, Any, Iterator, List, Sequence, Type
8
6
  from uuid import UUID
9
7
 
10
8
  from eventsourcing.persistence import (
11
9
  AggregateRecorder,
12
10
  ApplicationRecorder,
13
- Connection as BaseConnection,
11
+ Connection,
14
12
  ConnectionPool,
15
- Cursor as BaseCursor,
13
+ Cursor,
16
14
  DatabaseError,
17
15
  DataError,
18
16
  InfrastructureFactory,
@@ -30,17 +28,20 @@ from eventsourcing.persistence import (
30
28
  )
31
29
  from eventsourcing.utils import Environment, strtobool
32
30
 
31
+ if TYPE_CHECKING: # pragma: nocover
32
+ from types import TracebackType
33
+
33
34
  SQLITE3_DEFAULT_LOCK_TIMEOUT = 5
34
35
 
35
36
 
36
- class SQLiteCursor(BaseCursor):
37
- def __init__(self, sqlite_cursor: Cursor):
37
+ class SQLiteCursor(Cursor):
38
+ def __init__(self, sqlite_cursor: sqlite3.Cursor):
38
39
  self.sqlite_cursor = sqlite_cursor
39
40
 
40
- def __enter__(self, *args: Any, **kwargs: Any) -> Cursor:
41
+ def __enter__(self) -> sqlite3.Cursor:
41
42
  return self.sqlite_cursor
42
43
 
43
- def __exit__(self, *args: Any, **kwargs: Any) -> None:
44
+ def __exit__(self, *args: object, **kwargs: Any) -> None:
44
45
  self.sqlite_cursor.close()
45
46
 
46
47
  def execute(self, *args: Any, **kwargs: Any) -> None:
@@ -60,18 +61,16 @@ class SQLiteCursor(BaseCursor):
60
61
  return self.sqlite_cursor.lastrowid
61
62
 
62
63
 
63
- class SQLiteConnection(BaseConnection[SQLiteCursor]):
64
- def __init__(self, sqlite_conn: Connection, max_age: Optional[float]):
64
+ class SQLiteConnection(Connection[SQLiteCursor]):
65
+ def __init__(self, sqlite_conn: sqlite3.Connection, max_age: float | None):
65
66
  super().__init__(max_age=max_age)
66
67
  self._sqlite_conn = sqlite_conn
67
68
 
68
69
  @contextmanager
69
- def transaction(self, commit: bool) -> Iterator[SQLiteCursor]:
70
- # Context managed transaction.
71
- with SQLiteTransaction(self, commit) as curs:
72
- # Context managed cursor.
73
- with curs:
74
- yield curs
70
+ def transaction(self, *, commit: bool) -> Iterator[SQLiteCursor]:
71
+ # Context managed cursor, and context managed transaction.
72
+ with SQLiteTransaction(self, commit=commit) as curs, curs:
73
+ yield curs
75
74
 
76
75
  def cursor(self) -> SQLiteCursor:
77
76
  return SQLiteCursor(self._sqlite_conn.cursor())
@@ -88,7 +87,7 @@ class SQLiteConnection(BaseConnection[SQLiteCursor]):
88
87
 
89
88
 
90
89
  class SQLiteTransaction:
91
- def __init__(self, connection: SQLiteConnection, commit: bool = False):
90
+ def __init__(self, connection: SQLiteConnection, *, commit: bool = False):
92
91
  self.connection = connection
93
92
  self.commit = commit
94
93
 
@@ -101,9 +100,9 @@ class SQLiteTransaction:
101
100
 
102
101
  def __exit__(
103
102
  self,
104
- exc_type: Type[BaseException],
105
- exc_val: BaseException,
106
- exc_tb: TracebackType,
103
+ exc_type: Type[BaseException] | None,
104
+ exc_val: BaseException | None,
105
+ exc_tb: TracebackType | None,
107
106
  ) -> None:
108
107
  try:
109
108
  if exc_val:
@@ -111,7 +110,7 @@ class SQLiteTransaction:
111
110
  # if an exception occurs.
112
111
  self.connection.rollback()
113
112
  raise exc_val
114
- elif not self.commit:
113
+ if not self.commit:
115
114
  self.connection.rollback()
116
115
  else:
117
116
  self.connection.commit()
@@ -138,12 +137,13 @@ class SQLiteTransaction:
138
137
  class SQLiteConnectionPool(ConnectionPool[SQLiteConnection]):
139
138
  def __init__(
140
139
  self,
140
+ *,
141
141
  db_name: str,
142
- lock_timeout: Optional[int] = None,
142
+ lock_timeout: int | None = None,
143
143
  pool_size: int = 5,
144
144
  max_overflow: int = 10,
145
145
  pool_timeout: float = 5.0,
146
- max_age: Optional[float] = None,
146
+ max_age: float | None = None,
147
147
  pre_ping: bool = False,
148
148
  ):
149
149
  self.db_name = db_name
@@ -176,20 +176,19 @@ class SQLiteConnectionPool(ConnectionPool[SQLiteConnection]):
176
176
  timeout=self.lock_timeout or SQLITE3_DEFAULT_LOCK_TIMEOUT,
177
177
  )
178
178
  except (sqlite3.Error, TypeError) as e:
179
- raise InterfaceError(e)
179
+ raise InterfaceError(e) from e
180
180
 
181
181
  # Use WAL (write-ahead log) mode if file-based database.
182
- if not self.is_sqlite_memory_mode:
183
- if not self.is_journal_mode_wal:
184
- cursor = c.cursor()
185
- cursor.execute("PRAGMA journal_mode;")
186
- mode = cursor.fetchone()[0]
187
- if mode.lower() == "wal":
188
- self.is_journal_mode_wal = True
189
- else:
190
- cursor.execute("PRAGMA journal_mode=WAL;")
191
- self.is_journal_mode_wal = True
192
- self.journal_mode_was_changed_to_wal = True
182
+ if not self.is_sqlite_memory_mode and not self.is_journal_mode_wal:
183
+ cursor = c.cursor()
184
+ cursor.execute("PRAGMA journal_mode;")
185
+ mode = cursor.fetchone()[0]
186
+ if mode.lower() == "wal":
187
+ self.is_journal_mode_wal = True
188
+ else:
189
+ cursor.execute("PRAGMA journal_mode=WAL;")
190
+ self.is_journal_mode_wal = True
191
+ self.journal_mode_was_changed_to_wal = True
193
192
 
194
193
  # Set the row factory.
195
194
  c.row_factory = sqlite3.Row
@@ -202,11 +201,12 @@ class SQLiteDatastore:
202
201
  def __init__(
203
202
  self,
204
203
  db_name: str,
205
- lock_timeout: Optional[int] = None,
204
+ *,
205
+ lock_timeout: int | None = None,
206
206
  pool_size: int = 5,
207
207
  max_overflow: int = 10,
208
208
  pool_timeout: float = 5.0,
209
- max_age: Optional[float] = None,
209
+ max_age: float | None = None,
210
210
  pre_ping: bool = False,
211
211
  ):
212
212
  self.pool = SQLiteConnectionPool(
@@ -220,13 +220,13 @@ class SQLiteDatastore:
220
220
  )
221
221
 
222
222
  @contextmanager
223
- def transaction(self, commit: bool) -> Iterator[SQLiteCursor]:
224
- with self.get_connection(commit=commit) as conn:
225
- with conn.transaction(commit) as curs:
226
- yield curs
223
+ def transaction(self, *, commit: bool) -> Iterator[SQLiteCursor]:
224
+ connection = self.get_connection(commit=commit)
225
+ with connection as conn, conn.transaction(commit=commit) as curs:
226
+ yield curs
227
227
 
228
228
  @contextmanager
229
- def get_connection(self, commit: bool) -> Iterator[SQLiteConnection]:
229
+ def get_connection(self, *, commit: bool) -> Iterator[SQLiteConnection]:
230
230
  # Using reader-writer interlocking is necessary for in-memory databases,
231
231
  # but also speeds up (and provides "fairness") to file-based databases.
232
232
  conn = self.pool.get_connection(is_writer=commit)
@@ -256,7 +256,7 @@ class SQLiteAggregateRecorder(AggregateRecorder):
256
256
  f"INSERT INTO {self.events_table_name} VALUES (?,?,?,?)"
257
257
  )
258
258
  self.select_events_statement = (
259
- "SELECT * " f"FROM {self.events_table_name} " "WHERE originator_id=? "
259
+ f"SELECT * FROM {self.events_table_name} WHERE originator_id=? "
260
260
  )
261
261
 
262
262
  def construct_create_table_statements(self) -> List[str]:
@@ -277,11 +277,10 @@ class SQLiteAggregateRecorder(AggregateRecorder):
277
277
  with self.datastore.transaction(commit=True) as c:
278
278
  for statement in self.create_table_statements:
279
279
  c.execute(statement)
280
- pass # for Coverage 5.5 bug with CPython 3.10.0rc1
281
280
 
282
281
  def insert_events(
283
282
  self, stored_events: List[StoredEvent], **kwargs: Any
284
- ) -> Optional[Sequence[int]]:
283
+ ) -> Sequence[int] | None:
285
284
  with self.datastore.transaction(commit=True) as c:
286
285
  return self._insert_events(c, stored_events, **kwargs)
287
286
 
@@ -289,28 +288,28 @@ class SQLiteAggregateRecorder(AggregateRecorder):
289
288
  self,
290
289
  c: SQLiteCursor,
291
290
  stored_events: List[StoredEvent],
292
- **kwargs: Any,
293
- ) -> Optional[Sequence[int]]:
294
- params = []
295
- for stored_event in stored_events:
296
- params.append(
297
- (
298
- stored_event.originator_id.hex,
299
- stored_event.originator_version,
300
- stored_event.topic,
301
- stored_event.state,
302
- )
291
+ **_: Any,
292
+ ) -> Sequence[int] | None:
293
+ params = [
294
+ (
295
+ s.originator_id.hex,
296
+ s.originator_version,
297
+ s.topic,
298
+ s.state,
303
299
  )
300
+ for s in stored_events
301
+ ]
304
302
  c.executemany(self.insert_events_statement, params)
305
303
  return None
306
304
 
307
305
  def select_events(
308
306
  self,
309
307
  originator_id: UUID,
310
- gt: Optional[int] = None,
311
- lte: Optional[int] = None,
308
+ *,
309
+ gt: int | None = None,
310
+ lte: int | None = None,
312
311
  desc: bool = False,
313
- limit: Optional[int] = None,
312
+ limit: int | None = None,
314
313
  ) -> List[StoredEvent]:
315
314
  statement = self.select_events_statement
316
315
  params: List[Any] = [originator_id.hex]
@@ -328,20 +327,17 @@ class SQLiteAggregateRecorder(AggregateRecorder):
328
327
  if limit is not None:
329
328
  statement += "LIMIT ? "
330
329
  params.append(limit)
331
- stored_events = []
332
330
  with self.datastore.transaction(commit=False) as c:
333
331
  c.execute(statement, params)
334
- for row in c.fetchall():
335
- stored_events.append(
336
- StoredEvent(
337
- originator_id=UUID(row["originator_id"]),
338
- originator_version=row["originator_version"],
339
- topic=row["topic"],
340
- state=row["state"],
341
- )
332
+ return [
333
+ StoredEvent(
334
+ originator_id=UUID(row["originator_id"]),
335
+ originator_version=row["originator_version"],
336
+ topic=row["topic"],
337
+ state=row["state"],
342
338
  )
343
- pass # for Coverage 5.5 bug with CPython 3.10.0rc1
344
- return stored_events
339
+ for row in c.fetchall()
340
+ ]
345
341
 
346
342
 
347
343
  class SQLiteApplicationRecorder(
@@ -375,8 +371,8 @@ class SQLiteApplicationRecorder(
375
371
  self,
376
372
  c: SQLiteCursor,
377
373
  stored_events: List[StoredEvent],
378
- **kwargs: Any,
379
- ) -> Optional[Sequence[int]]:
374
+ **_: Any,
375
+ ) -> Sequence[int] | None:
380
376
  returning = []
381
377
  for stored_event in stored_events:
382
378
  c.execute(
@@ -395,17 +391,15 @@ class SQLiteApplicationRecorder(
395
391
  self,
396
392
  start: int,
397
393
  limit: int,
398
- stop: Optional[int] = None,
394
+ stop: int | None = None,
399
395
  topics: Sequence[str] = (),
400
396
  ) -> List[Notification]:
401
397
  """
402
398
  Returns a list of event notifications
403
399
  from 'start', limited by 'limit'.
404
400
  """
405
- notifications = []
406
-
407
- params: List[Union[int, str]] = [start]
408
- statement = f"SELECT rowid, * FROM {self.events_table_name} " "WHERE rowid>=? "
401
+ params: List[int | str] = [start]
402
+ statement = f"SELECT rowid, * FROM {self.events_table_name} WHERE rowid>=? "
409
403
 
410
404
  if stop is not None:
411
405
  params.append(stop)
@@ -420,19 +414,16 @@ class SQLiteApplicationRecorder(
420
414
 
421
415
  with self.datastore.transaction(commit=False) as c:
422
416
  c.execute(statement, params)
423
-
424
- for row in c.fetchall():
425
- notifications.append(
426
- Notification(
427
- id=row["rowid"],
428
- originator_id=UUID(row["originator_id"]),
429
- originator_version=row["originator_version"],
430
- topic=row["topic"],
431
- state=row["state"],
432
- )
417
+ return [
418
+ Notification(
419
+ id=row["rowid"],
420
+ originator_id=UUID(row["originator_id"]),
421
+ originator_version=row["originator_version"],
422
+ topic=row["topic"],
423
+ state=row["state"],
433
424
  )
434
- pass # for Coverage 5.5 bug with CPython 3.10.0rc1
435
- return notifications
425
+ for row in c.fetchall()
426
+ ]
436
427
 
437
428
  def max_notification_id(self) -> int:
438
429
  """
@@ -481,8 +472,7 @@ class SQLiteProcessRecorder(
481
472
  params = [application_name]
482
473
  with self.datastore.transaction(commit=False) as c:
483
474
  c.execute(self.select_max_tracking_id_statement, params)
484
- max_id = c.fetchone()[0] or 0
485
- return max_id
475
+ return c.fetchone()[0] or 0
486
476
 
487
477
  def has_tracking_id(self, application_name: str, notification_id: int) -> bool:
488
478
  params = [application_name, notification_id]
@@ -495,9 +485,9 @@ class SQLiteProcessRecorder(
495
485
  c: SQLiteCursor,
496
486
  stored_events: List[StoredEvent],
497
487
  **kwargs: Any,
498
- ) -> Optional[Sequence[int]]:
488
+ ) -> Sequence[int] | None:
499
489
  returning = super()._insert_events(c, stored_events, **kwargs)
500
- tracking: Optional[Tracking] = kwargs.get("tracking", None)
490
+ tracking: Tracking | None = kwargs.get("tracking", None)
501
491
  if tracking is not None:
502
492
  c.execute(
503
493
  self.insert_tracking_statement,
@@ -522,27 +512,29 @@ class Factory(InfrastructureFactory):
522
512
  super().__init__(env)
523
513
  db_name = self.env.get(self.SQLITE_DBNAME)
524
514
  if not db_name:
525
- raise EnvironmentError(
515
+ msg = (
526
516
  "SQLite database name not found "
527
517
  "in environment with keys: "
528
518
  f"{', '.join(self.env.create_keys(self.SQLITE_DBNAME))}"
529
519
  )
520
+ raise OSError(msg)
530
521
 
531
522
  lock_timeout_str = (
532
523
  self.env.get(self.SQLITE_LOCK_TIMEOUT) or ""
533
524
  ).strip() or None
534
525
 
535
- lock_timeout: Optional[int] = None
526
+ lock_timeout: int | None = None
536
527
  if lock_timeout_str is not None:
537
528
  try:
538
529
  lock_timeout = int(lock_timeout_str)
539
530
  except ValueError:
540
- raise EnvironmentError(
541
- f"SQLite environment value for key "
531
+ msg = (
532
+ "SQLite environment value for key "
542
533
  f"'{self.SQLITE_LOCK_TIMEOUT}' is invalid. "
543
- f"If set, an int or empty string is expected: "
534
+ "If set, an int or empty string is expected: "
544
535
  f"'{lock_timeout_str}'"
545
536
  )
537
+ raise OSError(msg) from None
546
538
 
547
539
  self.datastore = SQLiteDatastore(db_name=db_name, lock_timeout=lock_timeout)
548
540