Fix: make ExportSink a real ABC per design spec

The implementation plan for Task 2 diverged from the design spec
(export-sink-architecture-design.md), leaving ExportSink as a plain
class with NotImplementedError bodies instead of the specified
AbstractContextManager subclass. Use abc.ABC + @abstractmethod so a
concrete sink missing a required method fails at instantiation
rather than at first call.
This commit is contained in:
stumpylog
2026-07-22 15:07:24 -07:00
parent 344414bb47
commit 7f58d16758
+11 -8
View File
@@ -1,7 +1,9 @@
from __future__ import annotations
import abc
import hashlib
import json
from contextlib import AbstractContextManager
from contextlib import contextmanager
from typing import TYPE_CHECKING
@@ -51,7 +53,7 @@ class StreamingManifestWriter:
self._file.write("\n]")
class ExportSink:
class ExportSink(AbstractContextManager, abc.ABC):
"""Destination for a document export.
The command declares export contents via three verbs; the sink decides how to
@@ -65,31 +67,32 @@ class ExportSink:
failed run leaves a complete-looking artifact.
"""
@abc.abstractmethod
def add_file(
self,
source: Path,
arcname: str,
*,
checksum: str | None = None,
) -> None:
raise NotImplementedError # pragma: no cover
) -> None: ...
def add_json(self, content: list | dict, arcname: str) -> None:
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def add_json(self, content: list | dict, arcname: str) -> None: ...
@abc.abstractmethod
def stream(self, arcname: str): # -> contextmanager yielding TextIO
raise NotImplementedError # pragma: no cover
...
def _open(self) -> None:
"""Hook called on context entry. Override as needed."""
@abc.abstractmethod
def _finalize(self) -> None:
"""Commit on clean exit."""
raise NotImplementedError # pragma: no cover
@abc.abstractmethod
def _abort(self) -> None:
"""Roll back on exception."""
raise NotImplementedError # pragma: no cover
def __enter__(self) -> ExportSink:
self._open()