eventsourcing 9.2.22__py3-none-any.whl → 9.3.0a1__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.
- eventsourcing/__init__.py +1 -1
- eventsourcing/application.py +106 -135
- eventsourcing/cipher.py +15 -12
- eventsourcing/dispatch.py +31 -91
- eventsourcing/domain.py +138 -143
- eventsourcing/examples/__init__.py +0 -0
- eventsourcing/examples/aggregate1/__init__.py +0 -0
- eventsourcing/examples/aggregate1/application.py +27 -0
- eventsourcing/examples/aggregate1/domainmodel.py +16 -0
- eventsourcing/examples/aggregate1/test_application.py +37 -0
- eventsourcing/examples/aggregate2/__init__.py +0 -0
- eventsourcing/examples/aggregate2/application.py +27 -0
- eventsourcing/examples/aggregate2/domainmodel.py +22 -0
- eventsourcing/examples/aggregate2/test_application.py +37 -0
- eventsourcing/examples/aggregate3/__init__.py +0 -0
- eventsourcing/examples/aggregate3/application.py +27 -0
- eventsourcing/examples/aggregate3/domainmodel.py +38 -0
- eventsourcing/examples/aggregate3/test_application.py +37 -0
- eventsourcing/examples/aggregate4/__init__.py +0 -0
- eventsourcing/examples/aggregate4/application.py +27 -0
- eventsourcing/examples/aggregate4/domainmodel.py +128 -0
- eventsourcing/examples/aggregate4/test_application.py +38 -0
- eventsourcing/examples/aggregate5/__init__.py +0 -0
- eventsourcing/examples/aggregate5/application.py +27 -0
- eventsourcing/examples/aggregate5/domainmodel.py +131 -0
- eventsourcing/examples/aggregate5/test_application.py +38 -0
- eventsourcing/examples/aggregate6/__init__.py +0 -0
- eventsourcing/examples/aggregate6/application.py +30 -0
- eventsourcing/examples/aggregate6/domainmodel.py +123 -0
- eventsourcing/examples/aggregate6/test_application.py +38 -0
- eventsourcing/examples/aggregate6a/__init__.py +0 -0
- eventsourcing/examples/aggregate6a/application.py +40 -0
- eventsourcing/examples/aggregate6a/domainmodel.py +149 -0
- eventsourcing/examples/aggregate6a/test_application.py +45 -0
- eventsourcing/examples/aggregate7/__init__.py +0 -0
- eventsourcing/examples/aggregate7/application.py +48 -0
- eventsourcing/examples/aggregate7/domainmodel.py +144 -0
- eventsourcing/examples/aggregate7/persistence.py +57 -0
- eventsourcing/examples/aggregate7/test_application.py +38 -0
- eventsourcing/examples/aggregate7/test_compression_and_encryption.py +45 -0
- eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +67 -0
- eventsourcing/examples/aggregate7a/__init__.py +0 -0
- eventsourcing/examples/aggregate7a/application.py +56 -0
- eventsourcing/examples/aggregate7a/domainmodel.py +170 -0
- eventsourcing/examples/aggregate7a/test_application.py +46 -0
- eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +45 -0
- eventsourcing/examples/aggregate8/__init__.py +0 -0
- eventsourcing/examples/aggregate8/application.py +47 -0
- eventsourcing/examples/aggregate8/domainmodel.py +65 -0
- eventsourcing/examples/aggregate8/persistence.py +57 -0
- eventsourcing/examples/aggregate8/test_application.py +37 -0
- eventsourcing/examples/aggregate8/test_compression_and_encryption.py +44 -0
- eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +38 -0
- eventsourcing/examples/bankaccounts/__init__.py +0 -0
- eventsourcing/examples/bankaccounts/application.py +70 -0
- eventsourcing/examples/bankaccounts/domainmodel.py +56 -0
- eventsourcing/examples/bankaccounts/test.py +173 -0
- eventsourcing/examples/cargoshipping/__init__.py +0 -0
- eventsourcing/examples/cargoshipping/application.py +126 -0
- eventsourcing/examples/cargoshipping/domainmodel.py +330 -0
- eventsourcing/examples/cargoshipping/interface.py +143 -0
- eventsourcing/examples/cargoshipping/test.py +231 -0
- eventsourcing/examples/contentmanagement/__init__.py +0 -0
- eventsourcing/examples/contentmanagement/application.py +118 -0
- eventsourcing/examples/contentmanagement/domainmodel.py +69 -0
- eventsourcing/examples/contentmanagement/test.py +180 -0
- eventsourcing/examples/contentmanagement/utils.py +26 -0
- eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
- eventsourcing/examples/contentmanagementsystem/application.py +54 -0
- eventsourcing/examples/contentmanagementsystem/postgres.py +17 -0
- eventsourcing/examples/contentmanagementsystem/sqlite.py +17 -0
- eventsourcing/examples/contentmanagementsystem/system.py +14 -0
- eventsourcing/examples/contentmanagementsystem/test_system.py +174 -0
- eventsourcing/examples/searchablecontent/__init__.py +0 -0
- eventsourcing/examples/searchablecontent/application.py +45 -0
- eventsourcing/examples/searchablecontent/persistence.py +23 -0
- eventsourcing/examples/searchablecontent/postgres.py +118 -0
- eventsourcing/examples/searchablecontent/sqlite.py +136 -0
- eventsourcing/examples/searchablecontent/test_application.py +111 -0
- eventsourcing/examples/searchablecontent/test_recorder.py +69 -0
- eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
- eventsourcing/examples/searchabletimestamps/application.py +32 -0
- eventsourcing/examples/searchabletimestamps/persistence.py +20 -0
- eventsourcing/examples/searchabletimestamps/postgres.py +110 -0
- eventsourcing/examples/searchabletimestamps/sqlite.py +99 -0
- eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +91 -0
- eventsourcing/examples/test_invoice.py +176 -0
- eventsourcing/examples/test_parking_lot.py +206 -0
- eventsourcing/interface.py +2 -2
- eventsourcing/persistence.py +85 -81
- eventsourcing/popo.py +30 -31
- eventsourcing/postgres.py +361 -578
- eventsourcing/sqlite.py +91 -99
- eventsourcing/system.py +42 -57
- eventsourcing/tests/application.py +20 -32
- eventsourcing/tests/application_tests/__init__.py +0 -0
- eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +55 -0
- eventsourcing/tests/application_tests/test_application_with_popo.py +22 -0
- eventsourcing/tests/application_tests/test_application_with_postgres.py +75 -0
- eventsourcing/tests/application_tests/test_application_with_sqlite.py +72 -0
- eventsourcing/tests/application_tests/test_cache.py +134 -0
- eventsourcing/tests/application_tests/test_event_sourced_log.py +162 -0
- eventsourcing/tests/application_tests/test_notificationlog.py +232 -0
- eventsourcing/tests/application_tests/test_notificationlogreader.py +126 -0
- eventsourcing/tests/application_tests/test_processapplication.py +110 -0
- eventsourcing/tests/application_tests/test_processingpolicy.py +109 -0
- eventsourcing/tests/application_tests/test_repository.py +504 -0
- eventsourcing/tests/application_tests/test_snapshotting.py +68 -0
- eventsourcing/tests/application_tests/test_upcasting.py +459 -0
- eventsourcing/tests/docs_tests/__init__.py +0 -0
- eventsourcing/tests/docs_tests/test_docs.py +293 -0
- eventsourcing/tests/domain.py +1 -1
- eventsourcing/tests/domain_tests/__init__.py +0 -0
- eventsourcing/tests/domain_tests/test_aggregate.py +1159 -0
- eventsourcing/tests/domain_tests/test_aggregate_decorators.py +1604 -0
- eventsourcing/tests/domain_tests/test_domainevent.py +80 -0
- eventsourcing/tests/interface_tests/__init__.py +0 -0
- eventsourcing/tests/interface_tests/test_remotenotificationlog.py +258 -0
- eventsourcing/tests/persistence.py +49 -50
- eventsourcing/tests/persistence_tests/__init__.py +0 -0
- eventsourcing/tests/persistence_tests/test_aes.py +93 -0
- eventsourcing/tests/persistence_tests/test_connection_pool.py +722 -0
- eventsourcing/tests/persistence_tests/test_eventstore.py +72 -0
- eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +21 -0
- eventsourcing/tests/persistence_tests/test_mapper.py +113 -0
- eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +69 -0
- eventsourcing/tests/persistence_tests/test_popo.py +124 -0
- eventsourcing/tests/persistence_tests/test_postgres.py +1121 -0
- eventsourcing/tests/persistence_tests/test_sqlite.py +348 -0
- eventsourcing/tests/persistence_tests/test_transcoder.py +44 -0
- eventsourcing/tests/postgres_utils.py +7 -7
- eventsourcing/tests/system_tests/__init__.py +0 -0
- eventsourcing/tests/system_tests/test_runner.py +935 -0
- eventsourcing/tests/system_tests/test_system.py +287 -0
- eventsourcing/tests/utils_tests/__init__.py +0 -0
- eventsourcing/tests/utils_tests/test_utils.py +226 -0
- eventsourcing/utils.py +47 -50
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0a1.dist-info}/METADATA +28 -80
- eventsourcing-9.3.0a1.dist-info/RECORD +144 -0
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0a1.dist-info}/WHEEL +1 -2
- eventsourcing-9.2.22.dist-info/AUTHORS +0 -10
- eventsourcing-9.2.22.dist-info/RECORD +0 -25
- eventsourcing-9.2.22.dist-info/top_level.txt +0 -1
- {eventsourcing-9.2.22.dist-info → eventsourcing-9.3.0a1.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
|
|
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
|
|
11
|
+
Connection,
|
|
14
12
|
ConnectionPool,
|
|
15
|
-
Cursor
|
|
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(
|
|
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
|
|
41
|
+
def __enter__(self) -> sqlite3.Cursor:
|
|
41
42
|
return self.sqlite_cursor
|
|
42
43
|
|
|
43
|
-
def __exit__(self, *args:
|
|
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(
|
|
64
|
-
def __init__(self, sqlite_conn: Connection, max_age:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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 *
|
|
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
|
-
) ->
|
|
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
|
-
**
|
|
293
|
-
) ->
|
|
294
|
-
params = [
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
311
|
-
|
|
308
|
+
*,
|
|
309
|
+
gt: int | None = None,
|
|
310
|
+
lte: int | None = None,
|
|
312
311
|
desc: bool = False,
|
|
313
|
-
limit:
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
344
|
-
|
|
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
|
-
**
|
|
379
|
-
) ->
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
435
|
-
|
|
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
|
-
|
|
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
|
-
) ->
|
|
488
|
+
) -> Sequence[int] | None:
|
|
499
489
|
returning = super()._insert_events(c, stored_events, **kwargs)
|
|
500
|
-
tracking:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
541
|
-
|
|
531
|
+
msg = (
|
|
532
|
+
"SQLite environment value for key "
|
|
542
533
|
f"'{self.SQLITE_LOCK_TIMEOUT}' is invalid. "
|
|
543
|
-
|
|
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
|
|