Source code for elasticai.creator.hw_function_id

import logging
from collections.abc import Callable, Iterable, Iterator
from hashlib import blake2b, file_digest
from pathlib import Path


[docs] class HwFunctionIdUpdater: """Hash all files in `build_dir` except for `target_file` and uses `replace_id_fn` to write the result to `target_file`. The id will stay the same, if none of the files, except for the target file changed. You can use the optional `file_filter` to exclude additional files from the id computation. Example: ```python generate_my_source_files(build_dir) def replace_id(code: Iterable[str], id: bytes) -> Iterator[str]: for line in code: if line == "my_id_placeholder": yield f"my_id = {id.hex()};" else: yield line def filter_non_vhd(file): return str(file).endswith(".vhd") id_updater = HwFunctionIdUpdate(build_dir, "id_package.vhd", replace_id, filter_non_vhd) id_update.compute_id() id_updater.write_id() print(id_updater.id) ``` """ def __init__( self, build_dir: Path, target_file: str | Path, replace_id_fn: Callable[[Iterable[str], bytes], Iterator[str]], file_filter: Callable[[Path], bool] = lambda f: True, ): self._target_file = Path(target_file) self._build_dir = build_dir self._id = bytes() self._file_filter = file_filter self._replace_id_fn = replace_id_fn def _files_from_build_dir(self) -> Iterator[Path]: for f in self._build_dir.glob("**/*"): if f.is_file() and self._file_filter(f): yield f def _is_not_target_file(self, file: Path) -> bool: return not file.samefile(self._target_file) def _collect_files_to_hash(self) -> Iterator[Path]: return filter(self._is_not_target_file, self._files_from_build_dir())
[docs] def compute_id(self) -> None: logger = logging.getLogger(__name__) hash = _HwFunctionIdHash() for vhd_file in self._collect_files_to_hash(): logger.debug(f"hashing {vhd_file.as_uri}") hash.update(vhd_file) digest = hash.digest() logger.debug(f"raw_digest is {digest.hex()}") self._id = digest
[docs] def write_id(self) -> None: with open(self._target_file, "r") as f: code: Iterable[str] = f.readlines() code = self._replace_id_fn(code, self.id) with open(self._target_file, "w") as f: for line in code: f.write(line) f.write("\n")
@property def id(self) -> bytes: return self._id
class _HwFunctionIdHash: SIZE: int = 16 def __init__(self): self._digests = [] def _hash(self): return blake2b(digest_size=self.SIZE) def update(self, lines: Iterable[str] | Path) -> None: h = self._hash() if isinstance(lines, Path): with open(lines, "rb") as f: self._digests.append(file_digest(f, self._hash).digest()) else: for line in lines: h.update(line.encode()) self._digests.append(h.digest()) def digest(self) -> bytes: h = self._hash() for d in sorted(self._digests): h.update(d) return h.digest()