Source code for zephon.debug.inspector

"""Public, single-process debug utility for inspecting dataset payloads."""

from __future__ import annotations

from collections.abc import Mapping
from typing import TYPE_CHECKING, Any

from zephon._internal.io.protocols import DatasetShardView as _DatasetShardView
from zephon._internal.io.protocols import RandomAccessShard as _RandomAccessShard
from zephon._internal.io.stores import (
    build_multi_dataset_store as _build_multi_dataset_store,
)
from zephon._internal.io.stores.registry import DatasetStoreRegistry as _DatasetStore
from zephon.io.options import StoreOptions
from zephon.types import SamplePayload

if TYPE_CHECKING:
    from zephon.io.dataset import Dataset


[docs] class DatasetInspector: """Inspect raw payloads from one dataset without a work source or pipeline. This utility is intended for interactive inspection and dataset validation. It does not choose sample order, add record metadata, prefetch, batch, or run workers. Training pipelines should continue to use :class:`zephon.Pipeline`. Use inspectors as context managers so opened shard resources are closed promptly. An inspector is single-process and not thread-safe. """ def __init__( self, dataset: Dataset, *, io_options: StoreOptions | Mapping[str, Any] | None = None, ) -> None: self._dataset = dataset self._options = StoreOptions.from_any(io_options) self._store: _DatasetStore | None = None self._view: _DatasetShardView | None = None self._shards: dict[int, _RandomAccessShard] = {} self._closed = False def __enter__(self) -> "DatasetInspector": self._ensure_open() return self def __exit__(self, exc_type: object, exc_value: object, traceback: object) -> None: del exc_type, exc_value, traceback self.close()
[docs] def read(self, shard_id: int, sample_index: int) -> SamplePayload: """Return one raw payload identified by its shard and local row index. Args: shard_id: Dataset-local shard identifier, as returned by :meth:`Dataset.ids`. sample_index: Zero-based row index inside that shard. Returns: The payload stored by the underlying format. Payloads are normally mappings, but binary formats may return arrays or other supported payload values. """ return self.read_many(shard_id, [sample_index])[0]
[docs] def read_many( self, shard_id: int, sample_indices: list[int] ) -> list[SamplePayload]: """Return raw payloads for local row indices from one shard. The returned list preserves the order of ``sample_indices``. Supplying an empty list performs no I/O and returns an empty list. """ self._ensure_open() if not sample_indices: return [] shard = self._shard(shard_id) indexed_indices = sorted( enumerate(int(index) for index in sample_indices), key=lambda item: item[1] ) got = shard.getsamples([index for _, index in indexed_indices]) rows = got[0] if isinstance(got, tuple) else got result: list[SamplePayload] = [None] * len(sample_indices) for (position, _), row in zip(indexed_indices, rows, strict=True): result[position] = row return result
[docs] def close(self) -> None: """Close every shard opened by this inspector. Calling ``close`` more than once is safe. A closed inspector cannot be used for further reads. """ if self._closed: return self._closed = True first_error: BaseException | None = None for shard in self._shards.values(): try: shard.close() except BaseException as exc: # noqa: BLE001 - close all opened shards. if first_error is None: first_error = exc self._shards.clear() self._view = None if self._store is not None: try: self._store.close() except BaseException as exc: # noqa: BLE001 - finish local teardown. if first_error is None: first_error = exc self._store = None if first_error is not None: raise first_error
def _ensure_open(self) -> None: if self._closed: raise RuntimeError("DatasetInspector is closed") def _shard(self, shard_id: int) -> _RandomAccessShard: normalized_id = int(shard_id) existing = self._shards.get(normalized_id) if existing is not None: return existing # Reuse the same catalog, resolver, format handler, and optional on-disk # cache as FetchOp. Catalog attachment uses the process's existing or # default directory; inspection must not reconfigure that global state. if self._view is None: store = _build_multi_dataset_store( {0: self._dataset}, options=self._options ) self._store = store self._view = store.for_dataset(0) shard, _ = self._view.open(normalized_id) self._shards[normalized_id] = shard return shard
__all__ = ["DatasetInspector"]