SqliteJournal
Journal that keeps one row per decision in a decisions table.
The primary key (session, track, step) makes a duplicated step on
one track impossible at the storage layer: a second writer on the same
track fails instead of silently interleaving two histories.
Functions
func__init__(self, connection, session) -> NoneCreate the table if missing and adopt session on connection.
The caller owns the connection: opening, closing and any pragmas
(journal_mode=WAL is a good idea) are outside this journal. The
protocol is declared later with bind.
› Source code
def __init__(
self,
connection: sqlite3.Connection,
session: SessionId,
) -> None:
"""Create the table if missing and adopt *session* on *connection*.
The caller owns the connection: opening, closing and any pragmas
(``journal_mode=WAL`` is a good idea) are outside this journal. The
protocol is declared later with :meth:`bind`.
Args:
connection: An open SQLite connection owned by the caller.
session: Which recorded session to read and extend.
"""
self._connection = connection
self._session = session
self._digest: Digest | None = None
connection.execute(SCHEMA)
connection.commit()paramconnectionsqlite3.ConnectionAn open SQLite connection owned by the caller.
paramsessionSessionIdWhich recorded session to read and extend.
Returns
Nonefuncbind(self, protocol_id) -> NoneDeclare the protocol this journal records; verify on reopen.
Rows for this session record the digest on append; an existing
journal must carry the same one, or the mismatch is refused here.
› Source code
def bind(self, protocol_id: ProtocolId) -> None:
"""Declare the protocol this journal records; verify on reopen.
Rows for this session record the digest on ``append``; an existing
journal must carry the same one, or the mismatch is refused here.
Args:
protocol_id: The identity of the protocol this session runs.
Raises:
ProtocolMismatch: if rows for this session were written under
another protocol.
"""
digest = Digest(protocol_id.value)
if self._digest is not None:
if self._digest != digest:
session_id = self._session.value
recorded_protocol = self._digest.value
current_protocol = digest.value
raise ProtocolMismatch(
f'session {session_id} was recorded under '
f'protocol {recorded_protocol}, but this session runs {current_protocol}',
)
return
self._digest = digest
self._reject_foreign_protocol()paramprotocol_idProtocolIdThe identity of the protocol this session runs.
Returns
Nonefuncscript(self) -> ScriptEverything decided so far, one word per track.
› Source code
def script(self) -> Script:
"""Everything decided so far, one word per track."""
rows = self._connection.execute(
'SELECT track, step, sender, receiver, label, codec, raw '
'FROM decisions WHERE session = ? ORDER BY track, step',
(self._session.value,),
).fetchall()
return Script.of(_decision_of(row) for row in rows)Returns
agentsparty.journal.types.Scriptfuncappend(self, decision) -> NoneInsert decision as one row and commit.
› Source code
def append(self, decision: Decision) -> None:
"""Insert *decision* as one row and commit.
Args:
decision: The alt to record.
Raises:
AssertionError: if the journal was not bound to a protocol first.
"""
digest = self._bound_digest()
track_text = _track_text(decision.step.track)
recorded = self._connection.execute(
'SELECT COUNT(*) FROM decisions WHERE session = ? AND track = ?',
(self._session.value, track_text),
).fetchone()[0]
track_path = decision.step.track.path
position = decision.step.position
next_position = recorded + 1
pre(
expr=position == recorded + 1,
message=f'append must extend track {track_path} step {next_position}, got {position}',
)
self._connection.execute(
'INSERT INTO decisions '
'(session, protocol, track, step, sender, receiver, label, codec, raw) '
'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
(
self._session.value,
digest.value,
track_text,
decision.step.position,
decision.sender.name,
decision.receiver.name,
str(decision.label),
decision.codec,
json.dumps(decision.raw),
),
)
self._connection.commit()paramdecisionDecisionThe alt to record.
Returns
None