-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add caching of message verification results to parser
Ref. #442
- Loading branch information
Showing
6 changed files
with
203 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import hashlib | ||
import json | ||
import pathlib | ||
from typing import Dict | ||
|
||
from rflx import __version__ | ||
from rflx.model.message import AbstractMessage | ||
|
||
CACHE_DIR = pathlib.Path.home() / ".cache" / "RecordFlux" | ||
VERIFICATION_FILE = "verification.json" | ||
|
||
|
||
class Cache: | ||
def __init__(self, enabled: bool = True) -> None: | ||
self._enabled = enabled | ||
|
||
if not enabled: | ||
return | ||
|
||
self._verification: Dict[str, str] = {} | ||
|
||
self._initialize_cache() | ||
self._load_cache() | ||
|
||
def is_verified(self, message: AbstractMessage) -> bool: | ||
if not self._enabled: | ||
return False | ||
|
||
return message.full_name in self._verification and self._verification[ | ||
message.full_name | ||
] == self._message_hash(message) | ||
|
||
def add_verified(self, message: AbstractMessage) -> None: | ||
if not self._enabled: | ||
return | ||
|
||
message_hash = self._message_hash(message) | ||
if ( | ||
message.full_name not in self._verification | ||
or message_hash != self._verification[message.full_name] | ||
): | ||
self._verification[message.full_name] = message_hash | ||
self._write_cache() | ||
|
||
@staticmethod | ||
def _initialize_cache() -> None: | ||
if not CACHE_DIR.exists(): | ||
CACHE_DIR.mkdir() | ||
if not (CACHE_DIR / VERIFICATION_FILE).exists(): | ||
with open(CACHE_DIR / VERIFICATION_FILE, "w") as f: | ||
json.dump({}, f) | ||
|
||
def _load_cache(self) -> None: | ||
with open(CACHE_DIR / VERIFICATION_FILE) as f: | ||
self._verification = json.load(f) | ||
|
||
def _write_cache(self) -> None: | ||
with open(CACHE_DIR / VERIFICATION_FILE, "w") as f: | ||
json.dump(self._verification, f) | ||
|
||
@staticmethod | ||
def _message_hash(message: AbstractMessage) -> str: | ||
return hashlib.md5(f"{__version__}|{message}".encode("utf-8")).hexdigest() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from pathlib import Path | ||
|
||
from rflx.parser import cache | ||
from tests.models import TLV_MESSAGE | ||
|
||
|
||
def test_init(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path / "Test" | ||
cache.Cache() | ||
assert (tmp_path / "Test").is_dir() | ||
assert (tmp_path / "Test" / cache.VERIFICATION_FILE).is_file() | ||
|
||
|
||
def test_init_existing(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path | ||
with open(tmp_path / cache.VERIFICATION_FILE, "x") as f: | ||
f.write("{}") | ||
cache.Cache() | ||
assert tmp_path.is_dir() | ||
assert (tmp_path / cache.VERIFICATION_FILE).is_file() | ||
|
||
|
||
def test_init_disabled(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path / "Test" | ||
cache.Cache(enabled=False) | ||
assert not (tmp_path / "Test").is_dir() | ||
assert not (tmp_path / "Test" / cache.VERIFICATION_FILE).is_file() | ||
|
||
|
||
def test_verified(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path | ||
c = cache.Cache() | ||
assert not c.is_verified(TLV_MESSAGE) | ||
c.add_verified(TLV_MESSAGE) | ||
assert c.is_verified(TLV_MESSAGE) | ||
c.add_verified(TLV_MESSAGE) | ||
assert c.is_verified(TLV_MESSAGE) | ||
|
||
|
||
def test_verified_disabled(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path | ||
c = cache.Cache(enabled=False) | ||
assert not c.is_verified(TLV_MESSAGE) | ||
c.add_verified(TLV_MESSAGE) | ||
assert not c.is_verified(TLV_MESSAGE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
from rflx.error import RecordFluxError | ||
from rflx.parser import cache, parser | ||
from tests.models import INVALID_MESSAGE, VALID_MESSAGE | ||
|
||
TEST_DIR = Path("specs") | ||
SPEC_DIR = Path("specs") | ||
|
||
|
||
def test_create_model() -> None: | ||
p = parser.Parser() | ||
p.parse(SPEC_DIR / "tlv.rflx") | ||
p.create_model() | ||
|
||
|
||
def test_create_model_cached() -> None: | ||
p = parser.Parser(cached=True) | ||
p.parse(SPEC_DIR / "tlv.rflx") | ||
p.create_model() | ||
|
||
|
||
def test_create_proven_message(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path | ||
c = cache.Cache() | ||
assert parser.create_proven_message(VALID_MESSAGE, False, c) | ||
assert c.is_verified(VALID_MESSAGE) | ||
|
||
|
||
def test_create_proven_message_error(tmp_path: Path) -> None: | ||
cache.CACHE_DIR = tmp_path | ||
c = cache.Cache() | ||
with pytest.raises(RecordFluxError): | ||
parser.create_proven_message(INVALID_MESSAGE, False, c) | ||
assert not c.is_verified(INVALID_MESSAGE) |