# SPDX-License-Identifier: GPL-3.0-or-later # Haketilo proxy data and configuration (definition of fields of a class that # will implement HaketiloState). # # This file is part of Hydrilla&Haketilo. # # Copyright (C) 2022 Wojtek Kosior # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # I, Wojtek Kosior, thereby promise not to sue for violation of this # file's license. Although I request that you do not make use this code # in a proprietary program, I am not going to enforce this in court. """ This module defines fields that will later be part of a concrete HaketiloState subtype. """ # Enable using with Python 3.7. from __future__ import annotations import sqlite3 import threading import dataclasses as dc import typing as t from pathlib import Path from contextlib import contextmanager import sqlite3 from immutables import Map from ... import pattern_tree from .. import state from .. import policies PolicyTree = pattern_tree.PatternTree[policies.PolicyFactory] PayloadsData = t.Mapping[state.PayloadRef, state.PayloadData] # mypy needs to be corrected: # https://stackoverflow.com/questions/70999513/conflict-between-mix-ins-for-abstract-dataclasses/70999704#70999704 @dc.dataclass # type: ignore[misc] class HaketiloStateWithFields(state.HaketiloState): """....""" store_dir: Path connection: sqlite3.Connection current_cursor: t.Optional[sqlite3.Cursor] = None #settings: state.HaketiloGlobalSettings policy_tree: PolicyTree = PolicyTree() payloads_data: PayloadsData = dc.field(default_factory=dict) lock: threading.RLock = dc.field(default_factory=threading.RLock) @contextmanager def cursor(self, transaction: bool = False) \ -> t.Iterator[sqlite3.Cursor]: """....""" start_transaction = transaction and not self.connection.in_transaction with self.lock: if self.current_cursor is not None: yield self.current_cursor return try: self.current_cursor = self.connection.cursor() if start_transaction: self.current_cursor.execute('BEGIN TRANSACTION;') try: yield self.current_cursor if start_transaction: assert self.connection.in_transaction self.current_cursor.execute('COMMIT TRANSACTION;') except: if start_transaction: self.current_cursor.execute('ROLLBACK TRANSACTION;') raise finally: self.current_cursor = None def recompute_payloads(self, cursor: sqlite3.Cursor) -> None: """....""" ...