Source code for uchrom.fea.registry

"""Feature registry helpers for ``ChromData.uns``."""

from __future__ import annotations

from copy import deepcopy
from datetime import datetime, timezone
import hashlib
from pathlib import Path
from typing import Any, Mapping


[docs] def append_feature_registry_entry( cdata, entry: Mapping[str, Any], *, key: str = "feature_registry", ) -> dict[str, Any]: """Append a provenance record to ``cdata.uns[key]``. The registry is stored as a list of JSON-like dicts so it round-trips through the existing ``uns`` serializer. """ record = deepcopy(dict(entry)) record.setdefault( "created_utc", datetime.now(timezone.utc).replace(microsecond=0).isoformat(), ) try: from uchrom import __version__ as uchrom_version except Exception: uchrom_version = "unknown" record.setdefault("uchrom_version", uchrom_version) if "features" in record and "n_features" not in record: try: record["n_features"] = len(record.get("features") or []) except Exception: pass if record.get("hash_source") and record.get("source_path") and "source_sha256" not in record: source_path = Path(str(record["source_path"])) if source_path.exists() and source_path.is_file(): record["source_sha256"] = file_sha256(source_path) if record.get("hash_source") and record.get("source_paths") and "source_sha256s" not in record: hashes = {} for raw in record.get("source_paths") or []: source_path = Path(str(raw)) if source_path.exists() and source_path.is_file(): hashes[str(raw)] = file_sha256(source_path) if hashes: record["source_sha256s"] = hashes record.pop("hash_source", None) existing = cdata.uns.get(key, []) if existing is None: records = [] elif isinstance(existing, list): records = list(existing) elif isinstance(existing, tuple): records = list(existing) else: records = [existing] records.append(record) cdata.uns[key] = records return record
[docs] def file_sha256(path: str | Path, *, chunk_size: int = 1024 * 1024) -> str: """Return the SHA-256 digest for a local source file.""" digest = hashlib.sha256() with Path(path).open("rb") as handle: for chunk in iter(lambda: handle.read(chunk_size), b""): digest.update(chunk) return digest.hexdigest()
[docs] def table_provenance(table, *, value_columns=None) -> dict[str, Any]: """Small JSON-friendly summary for an enrichment output table.""" value_columns = list(value_columns or []) out = {"n_rows": int(len(table)), "value_columns": value_columns} finite_counts = {} for column in value_columns: if column not in table: continue try: finite_counts[str(column)] = int(table[column].notna().sum()) except Exception: pass if finite_counts: out["non_null_counts"] = finite_counts return out
__all__ = ["append_feature_registry_entry", "file_sha256", "table_provenance"]