eventsourcing 9.2.21__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.
- eventsourcing/__init__.py +1 -1
- eventsourcing/application.py +137 -132
- eventsourcing/cipher.py +17 -12
- eventsourcing/compressor.py +2 -0
- eventsourcing/dispatch.py +30 -56
- eventsourcing/domain.py +221 -227
- 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 +114 -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 +180 -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 +110 -0
- eventsourcing/examples/searchablecontent/test_recorder.py +68 -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 +94 -0
- eventsourcing/examples/test_invoice.py +176 -0
- eventsourcing/examples/test_parking_lot.py +206 -0
- eventsourcing/interface.py +4 -2
- eventsourcing/persistence.py +88 -82
- eventsourcing/popo.py +32 -31
- eventsourcing/postgres.py +388 -593
- eventsourcing/sqlite.py +100 -102
- eventsourcing/system.py +66 -71
- 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 +1180 -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 +52 -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 +1119 -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 +284 -0
- eventsourcing/tests/utils_tests/__init__.py +0 -0
- eventsourcing/tests/utils_tests/test_utils.py +226 -0
- eventsourcing/utils.py +49 -50
- {eventsourcing-9.2.21.dist-info → eventsourcing-9.3.0.dist-info}/METADATA +30 -33
- eventsourcing-9.3.0.dist-info/RECORD +145 -0
- {eventsourcing-9.2.21.dist-info → eventsourcing-9.3.0.dist-info}/WHEEL +1 -2
- eventsourcing-9.2.21.dist-info/RECORD +0 -25
- eventsourcing-9.2.21.dist-info/top_level.txt +0 -1
- {eventsourcing-9.2.21.dist-info → eventsourcing-9.3.0.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.2.21.dist-info → eventsourcing-9.3.0.dist-info}/LICENSE +0 -0
eventsourcing/sqlite.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import sqlite3
|
|
2
4
|
from contextlib import contextmanager
|
|
3
|
-
from
|
|
4
|
-
from types import TracebackType
|
|
5
|
-
from typing import Any, Iterator, List, Optional, Sequence, Type, Union
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Iterator, List, Sequence, Type
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
8
|
from eventsourcing.persistence import (
|
|
9
9
|
AggregateRecorder,
|
|
10
10
|
ApplicationRecorder,
|
|
11
|
-
Connection
|
|
11
|
+
Connection,
|
|
12
12
|
ConnectionPool,
|
|
13
|
-
Cursor
|
|
13
|
+
Cursor,
|
|
14
14
|
DatabaseError,
|
|
15
15
|
DataError,
|
|
16
16
|
InfrastructureFactory,
|
|
@@ -28,17 +28,20 @@ from eventsourcing.persistence import (
|
|
|
28
28
|
)
|
|
29
29
|
from eventsourcing.utils import Environment, strtobool
|
|
30
30
|
|
|
31
|
+
if TYPE_CHECKING: # pragma: nocover
|
|
32
|
+
from types import TracebackType
|
|
33
|
+
|
|
31
34
|
SQLITE3_DEFAULT_LOCK_TIMEOUT = 5
|
|
32
35
|
|
|
33
36
|
|
|
34
|
-
class SQLiteCursor(
|
|
35
|
-
def __init__(self, sqlite_cursor: Cursor):
|
|
37
|
+
class SQLiteCursor(Cursor):
|
|
38
|
+
def __init__(self, sqlite_cursor: sqlite3.Cursor):
|
|
36
39
|
self.sqlite_cursor = sqlite_cursor
|
|
37
40
|
|
|
38
|
-
def __enter__(self
|
|
41
|
+
def __enter__(self) -> sqlite3.Cursor:
|
|
39
42
|
return self.sqlite_cursor
|
|
40
43
|
|
|
41
|
-
def __exit__(self, *args:
|
|
44
|
+
def __exit__(self, *args: object, **kwargs: Any) -> None:
|
|
42
45
|
self.sqlite_cursor.close()
|
|
43
46
|
|
|
44
47
|
def execute(self, *args: Any, **kwargs: Any) -> None:
|
|
@@ -58,18 +61,16 @@ class SQLiteCursor(BaseCursor):
|
|
|
58
61
|
return self.sqlite_cursor.lastrowid
|
|
59
62
|
|
|
60
63
|
|
|
61
|
-
class SQLiteConnection(
|
|
62
|
-
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):
|
|
63
66
|
super().__init__(max_age=max_age)
|
|
64
67
|
self._sqlite_conn = sqlite_conn
|
|
65
68
|
|
|
66
69
|
@contextmanager
|
|
67
|
-
def transaction(self, commit: bool) -> Iterator[SQLiteCursor]:
|
|
68
|
-
# Context managed transaction.
|
|
69
|
-
with SQLiteTransaction(self, commit) as curs:
|
|
70
|
-
|
|
71
|
-
with curs:
|
|
72
|
-
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
|
|
73
74
|
|
|
74
75
|
def cursor(self) -> SQLiteCursor:
|
|
75
76
|
return SQLiteCursor(self._sqlite_conn.cursor())
|
|
@@ -86,7 +87,7 @@ class SQLiteConnection(BaseConnection[SQLiteCursor]):
|
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
class SQLiteTransaction:
|
|
89
|
-
def __init__(self, connection: SQLiteConnection, commit: bool = False):
|
|
90
|
+
def __init__(self, connection: SQLiteConnection, *, commit: bool = False):
|
|
90
91
|
self.connection = connection
|
|
91
92
|
self.commit = commit
|
|
92
93
|
|
|
@@ -99,9 +100,9 @@ class SQLiteTransaction:
|
|
|
99
100
|
|
|
100
101
|
def __exit__(
|
|
101
102
|
self,
|
|
102
|
-
exc_type: Type[BaseException],
|
|
103
|
-
exc_val: BaseException,
|
|
104
|
-
exc_tb: TracebackType,
|
|
103
|
+
exc_type: Type[BaseException] | None,
|
|
104
|
+
exc_val: BaseException | None,
|
|
105
|
+
exc_tb: TracebackType | None,
|
|
105
106
|
) -> None:
|
|
106
107
|
try:
|
|
107
108
|
if exc_val:
|
|
@@ -109,7 +110,7 @@ class SQLiteTransaction:
|
|
|
109
110
|
# if an exception occurs.
|
|
110
111
|
self.connection.rollback()
|
|
111
112
|
raise exc_val
|
|
112
|
-
|
|
113
|
+
if not self.commit:
|
|
113
114
|
self.connection.rollback()
|
|
114
115
|
else:
|
|
115
116
|
self.connection.commit()
|
|
@@ -136,12 +137,13 @@ class SQLiteTransaction:
|
|
|
136
137
|
class SQLiteConnectionPool(ConnectionPool[SQLiteConnection]):
|
|
137
138
|
def __init__(
|
|
138
139
|
self,
|
|
140
|
+
*,
|
|
139
141
|
db_name: str,
|
|
140
|
-
lock_timeout:
|
|
142
|
+
lock_timeout: int | None = None,
|
|
141
143
|
pool_size: int = 5,
|
|
142
144
|
max_overflow: int = 10,
|
|
143
145
|
pool_timeout: float = 5.0,
|
|
144
|
-
max_age:
|
|
146
|
+
max_age: float | None = None,
|
|
145
147
|
pre_ping: bool = False,
|
|
146
148
|
):
|
|
147
149
|
self.db_name = db_name
|
|
@@ -174,20 +176,19 @@ class SQLiteConnectionPool(ConnectionPool[SQLiteConnection]):
|
|
|
174
176
|
timeout=self.lock_timeout or SQLITE3_DEFAULT_LOCK_TIMEOUT,
|
|
175
177
|
)
|
|
176
178
|
except (sqlite3.Error, TypeError) as e:
|
|
177
|
-
raise InterfaceError(e)
|
|
179
|
+
raise InterfaceError(e) from e
|
|
178
180
|
|
|
179
181
|
# Use WAL (write-ahead log) mode if file-based database.
|
|
180
|
-
if not self.is_sqlite_memory_mode:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
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
|
|
191
192
|
|
|
192
193
|
# Set the row factory.
|
|
193
194
|
c.row_factory = sqlite3.Row
|
|
@@ -200,11 +201,12 @@ class SQLiteDatastore:
|
|
|
200
201
|
def __init__(
|
|
201
202
|
self,
|
|
202
203
|
db_name: str,
|
|
203
|
-
|
|
204
|
+
*,
|
|
205
|
+
lock_timeout: int | None = None,
|
|
204
206
|
pool_size: int = 5,
|
|
205
207
|
max_overflow: int = 10,
|
|
206
208
|
pool_timeout: float = 5.0,
|
|
207
|
-
max_age:
|
|
209
|
+
max_age: float | None = None,
|
|
208
210
|
pre_ping: bool = False,
|
|
209
211
|
):
|
|
210
212
|
self.pool = SQLiteConnectionPool(
|
|
@@ -218,13 +220,13 @@ class SQLiteDatastore:
|
|
|
218
220
|
)
|
|
219
221
|
|
|
220
222
|
@contextmanager
|
|
221
|
-
def transaction(self, commit: bool) -> Iterator[SQLiteCursor]:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
|
225
227
|
|
|
226
228
|
@contextmanager
|
|
227
|
-
def get_connection(self, commit: bool) -> Iterator[SQLiteConnection]:
|
|
229
|
+
def get_connection(self, *, commit: bool) -> Iterator[SQLiteConnection]:
|
|
228
230
|
# Using reader-writer interlocking is necessary for in-memory databases,
|
|
229
231
|
# but also speeds up (and provides "fairness") to file-based databases.
|
|
230
232
|
conn = self.pool.get_connection(is_writer=commit)
|
|
@@ -254,7 +256,7 @@ class SQLiteAggregateRecorder(AggregateRecorder):
|
|
|
254
256
|
f"INSERT INTO {self.events_table_name} VALUES (?,?,?,?)"
|
|
255
257
|
)
|
|
256
258
|
self.select_events_statement = (
|
|
257
|
-
"SELECT *
|
|
259
|
+
f"SELECT * FROM {self.events_table_name} WHERE originator_id=? "
|
|
258
260
|
)
|
|
259
261
|
|
|
260
262
|
def construct_create_table_statements(self) -> List[str]:
|
|
@@ -275,11 +277,10 @@ class SQLiteAggregateRecorder(AggregateRecorder):
|
|
|
275
277
|
with self.datastore.transaction(commit=True) as c:
|
|
276
278
|
for statement in self.create_table_statements:
|
|
277
279
|
c.execute(statement)
|
|
278
|
-
pass # for Coverage 5.5 bug with CPython 3.10.0rc1
|
|
279
280
|
|
|
280
281
|
def insert_events(
|
|
281
282
|
self, stored_events: List[StoredEvent], **kwargs: Any
|
|
282
|
-
) ->
|
|
283
|
+
) -> Sequence[int] | None:
|
|
283
284
|
with self.datastore.transaction(commit=True) as c:
|
|
284
285
|
return self._insert_events(c, stored_events, **kwargs)
|
|
285
286
|
|
|
@@ -287,28 +288,28 @@ class SQLiteAggregateRecorder(AggregateRecorder):
|
|
|
287
288
|
self,
|
|
288
289
|
c: SQLiteCursor,
|
|
289
290
|
stored_events: List[StoredEvent],
|
|
290
|
-
**
|
|
291
|
-
) ->
|
|
292
|
-
params = [
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
stored_event.topic,
|
|
299
|
-
stored_event.state,
|
|
300
|
-
)
|
|
291
|
+
**_: Any,
|
|
292
|
+
) -> Sequence[int] | None:
|
|
293
|
+
params = [
|
|
294
|
+
(
|
|
295
|
+
s.originator_id.hex,
|
|
296
|
+
s.originator_version,
|
|
297
|
+
s.topic,
|
|
298
|
+
s.state,
|
|
301
299
|
)
|
|
300
|
+
for s in stored_events
|
|
301
|
+
]
|
|
302
302
|
c.executemany(self.insert_events_statement, params)
|
|
303
303
|
return None
|
|
304
304
|
|
|
305
305
|
def select_events(
|
|
306
306
|
self,
|
|
307
307
|
originator_id: UUID,
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
*,
|
|
309
|
+
gt: int | None = None,
|
|
310
|
+
lte: int | None = None,
|
|
310
311
|
desc: bool = False,
|
|
311
|
-
limit:
|
|
312
|
+
limit: int | None = None,
|
|
312
313
|
) -> List[StoredEvent]:
|
|
313
314
|
statement = self.select_events_statement
|
|
314
315
|
params: List[Any] = [originator_id.hex]
|
|
@@ -326,20 +327,17 @@ class SQLiteAggregateRecorder(AggregateRecorder):
|
|
|
326
327
|
if limit is not None:
|
|
327
328
|
statement += "LIMIT ? "
|
|
328
329
|
params.append(limit)
|
|
329
|
-
stored_events = []
|
|
330
330
|
with self.datastore.transaction(commit=False) as c:
|
|
331
331
|
c.execute(statement, params)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
state=row["state"],
|
|
339
|
-
)
|
|
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"],
|
|
340
338
|
)
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
for row in c.fetchall()
|
|
340
|
+
]
|
|
343
341
|
|
|
344
342
|
|
|
345
343
|
class SQLiteApplicationRecorder(
|
|
@@ -373,8 +371,8 @@ class SQLiteApplicationRecorder(
|
|
|
373
371
|
self,
|
|
374
372
|
c: SQLiteCursor,
|
|
375
373
|
stored_events: List[StoredEvent],
|
|
376
|
-
**
|
|
377
|
-
) ->
|
|
374
|
+
**_: Any,
|
|
375
|
+
) -> Sequence[int] | None:
|
|
378
376
|
returning = []
|
|
379
377
|
for stored_event in stored_events:
|
|
380
378
|
c.execute(
|
|
@@ -393,17 +391,15 @@ class SQLiteApplicationRecorder(
|
|
|
393
391
|
self,
|
|
394
392
|
start: int,
|
|
395
393
|
limit: int,
|
|
396
|
-
stop:
|
|
394
|
+
stop: int | None = None,
|
|
397
395
|
topics: Sequence[str] = (),
|
|
398
396
|
) -> List[Notification]:
|
|
399
397
|
"""
|
|
400
398
|
Returns a list of event notifications
|
|
401
399
|
from 'start', limited by 'limit'.
|
|
402
400
|
"""
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
params: List[Union[int, str]] = [start]
|
|
406
|
-
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>=? "
|
|
407
403
|
|
|
408
404
|
if stop is not None:
|
|
409
405
|
params.append(stop)
|
|
@@ -418,19 +414,16 @@ class SQLiteApplicationRecorder(
|
|
|
418
414
|
|
|
419
415
|
with self.datastore.transaction(commit=False) as c:
|
|
420
416
|
c.execute(statement, params)
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
topic=row["topic"],
|
|
429
|
-
state=row["state"],
|
|
430
|
-
)
|
|
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"],
|
|
431
424
|
)
|
|
432
|
-
|
|
433
|
-
|
|
425
|
+
for row in c.fetchall()
|
|
426
|
+
]
|
|
434
427
|
|
|
435
428
|
def max_notification_id(self) -> int:
|
|
436
429
|
"""
|
|
@@ -479,8 +472,7 @@ class SQLiteProcessRecorder(
|
|
|
479
472
|
params = [application_name]
|
|
480
473
|
with self.datastore.transaction(commit=False) as c:
|
|
481
474
|
c.execute(self.select_max_tracking_id_statement, params)
|
|
482
|
-
|
|
483
|
-
return max_id
|
|
475
|
+
return c.fetchone()[0] or 0
|
|
484
476
|
|
|
485
477
|
def has_tracking_id(self, application_name: str, notification_id: int) -> bool:
|
|
486
478
|
params = [application_name, notification_id]
|
|
@@ -493,9 +485,9 @@ class SQLiteProcessRecorder(
|
|
|
493
485
|
c: SQLiteCursor,
|
|
494
486
|
stored_events: List[StoredEvent],
|
|
495
487
|
**kwargs: Any,
|
|
496
|
-
) ->
|
|
488
|
+
) -> Sequence[int] | None:
|
|
497
489
|
returning = super()._insert_events(c, stored_events, **kwargs)
|
|
498
|
-
tracking:
|
|
490
|
+
tracking: Tracking | None = kwargs.get("tracking", None)
|
|
499
491
|
if tracking is not None:
|
|
500
492
|
c.execute(
|
|
501
493
|
self.insert_tracking_statement,
|
|
@@ -512,37 +504,43 @@ class Factory(InfrastructureFactory):
|
|
|
512
504
|
SQLITE_LOCK_TIMEOUT = "SQLITE_LOCK_TIMEOUT"
|
|
513
505
|
CREATE_TABLE = "CREATE_TABLE"
|
|
514
506
|
|
|
507
|
+
aggregate_recorder_class = SQLiteAggregateRecorder
|
|
508
|
+
application_recorder_class = SQLiteApplicationRecorder
|
|
509
|
+
process_recorder_class = SQLiteProcessRecorder
|
|
510
|
+
|
|
515
511
|
def __init__(self, env: Environment):
|
|
516
512
|
super().__init__(env)
|
|
517
513
|
db_name = self.env.get(self.SQLITE_DBNAME)
|
|
518
514
|
if not db_name:
|
|
519
|
-
|
|
515
|
+
msg = (
|
|
520
516
|
"SQLite database name not found "
|
|
521
517
|
"in environment with keys: "
|
|
522
518
|
f"{', '.join(self.env.create_keys(self.SQLITE_DBNAME))}"
|
|
523
519
|
)
|
|
520
|
+
raise OSError(msg)
|
|
524
521
|
|
|
525
522
|
lock_timeout_str = (
|
|
526
523
|
self.env.get(self.SQLITE_LOCK_TIMEOUT) or ""
|
|
527
524
|
).strip() or None
|
|
528
525
|
|
|
529
|
-
lock_timeout:
|
|
526
|
+
lock_timeout: int | None = None
|
|
530
527
|
if lock_timeout_str is not None:
|
|
531
528
|
try:
|
|
532
529
|
lock_timeout = int(lock_timeout_str)
|
|
533
530
|
except ValueError:
|
|
534
|
-
|
|
535
|
-
|
|
531
|
+
msg = (
|
|
532
|
+
"SQLite environment value for key "
|
|
536
533
|
f"'{self.SQLITE_LOCK_TIMEOUT}' is invalid. "
|
|
537
|
-
|
|
534
|
+
"If set, an int or empty string is expected: "
|
|
538
535
|
f"'{lock_timeout_str}'"
|
|
539
536
|
)
|
|
537
|
+
raise OSError(msg) from None
|
|
540
538
|
|
|
541
539
|
self.datastore = SQLiteDatastore(db_name=db_name, lock_timeout=lock_timeout)
|
|
542
540
|
|
|
543
541
|
def aggregate_recorder(self, purpose: str = "events") -> AggregateRecorder:
|
|
544
542
|
events_table_name = "stored_" + purpose
|
|
545
|
-
recorder =
|
|
543
|
+
recorder = self.aggregate_recorder_class(
|
|
546
544
|
datastore=self.datastore,
|
|
547
545
|
events_table_name=events_table_name,
|
|
548
546
|
)
|
|
@@ -551,13 +549,13 @@ class Factory(InfrastructureFactory):
|
|
|
551
549
|
return recorder
|
|
552
550
|
|
|
553
551
|
def application_recorder(self) -> ApplicationRecorder:
|
|
554
|
-
recorder =
|
|
552
|
+
recorder = self.application_recorder_class(datastore=self.datastore)
|
|
555
553
|
if self.env_create_table():
|
|
556
554
|
recorder.create_table()
|
|
557
555
|
return recorder
|
|
558
556
|
|
|
559
557
|
def process_recorder(self) -> ProcessRecorder:
|
|
560
|
-
recorder =
|
|
558
|
+
recorder = self.process_recorder_class(datastore=self.datastore)
|
|
561
559
|
if self.env_create_table():
|
|
562
560
|
recorder.create_table()
|
|
563
561
|
return recorder
|