Source code for cutan.characters.drawn

"""What the character factory wrote, as it wrote it: the in-memory log behind its record of drawn bytes.

The machine's record of bytes the factory drew (an#269,
``an.library.registry.record_generated``) verifies the factory's stamps in the
asset library. It must hold the digests of bytes the factory itself produced,
never of whatever sits on disk when it finishes: a file swapped in while
``new_character`` runs is not the factory's drawing. So every file the factory
writes goes through :func:`write_text` / :func:`write_bytes`, which write it AND,
while a :func:`drawing` is open in this context, log the SHA-256 of the exact
bytes written — computed in memory at write time.

>>> import tempfile, pathlib
>>> with tempfile.TemporaryDirectory() as d, drawing() as log:
...     p = pathlib.Path(d) / "a.svg"
...     write_text(p, "<svg/>")
...     log[str(p.resolve())] == hashlib.sha256(b"<svg/>").hexdigest()
True
"""

from __future__ import annotations

import hashlib
import os
from collections.abc import Iterator
from contextlib import contextmanager
from contextvars import ContextVar
from pathlib import Path

__all__ = ["drawing", "write_bytes", "write_derived_text", "write_text"]

_LOG: ContextVar[dict[str, str] | None] = ContextVar("an_factory_drawn", default=None)


[docs] @contextmanager def drawing() -> Iterator[dict[str, str]]: """Log ``{resolved path: sha256}`` of every file written through this module, in this context. Re-entrant: inside an open drawing (``add_gaze`` called by ``new_character``) the same log is shared, so the outer call sees every byte the inner one wrote. """ held = _LOG.get() if held is not None: yield held return log: dict[str, str] = {} token = _LOG.set(log) try: yield log finally: _LOG.reset(token)
[docs] def write_bytes(path: str | os.PathLike, data: bytes) -> None: """Write ``data`` to ``path``, logging its digest if a :func:`drawing` is open.""" path = Path(path) path.write_bytes(data) log = _LOG.get() if log is not None: log[str(path.resolve())] = hashlib.sha256(data).hexdigest()
[docs] def write_text(path: str | os.PathLike, text: str, *, encoding: str = "utf-8") -> None: """Write ``text`` as ``Path.write_text`` does (newlines as the platform writes them), logged.""" write_bytes(path, text.replace("\n", os.linesep).encode(encoding))
[docs] def write_derived_text( path: str | os.PathLike, source: bytes, text: str, *, encoding: str = "utf-8" ) -> None: """Write ``text``, derived from ``source`` — the bytes just read back from ``path``. Logged only if ``source`` is exactly what this drawing itself wrote there: a file swapped in between the factory's write and its re-read (a rescale) is rewritten, but not as the factory's drawing (review-288 round 2). >>> import tempfile, pathlib >>> with tempfile.TemporaryDirectory() as d, drawing() as log: ... p = pathlib.Path(d) / "a.svg" ... write_text(p, "<svg/>") ... p.write_bytes(b"<svg>swapped</svg>") # not the factory's write ... write_derived_text(p, p.read_bytes(), "<svg>scaled</svg>") ... str(p.resolve()) in log 18 False """ path = Path(path) log = _LOG.get() key = str(path.resolve()) if log is None or log.get(key) == hashlib.sha256(source).hexdigest(): write_text(path, text, encoding=encoding) return path.write_bytes(text.replace("\n", os.linesep).encode(encoding)) log.pop(key, None)