From a6b00ec52d7d2fbe586ba8a963ceca2a6b30c5da Mon Sep 17 00:00:00 2001 From: jcoludar <32150027+jcoludar@users.noreply.github.com> Date: Tue, 11 Aug 2026 17:49:41 +0200 Subject: [PATCH] fix(reader): raise instead of returning an empty dataset for a bad path Every probe in _load_data is an .exists() test with an empty-frame fallback, so ArrowReader(Path) reported 0 proteins / 0 projections / format_version 1 and raised nothing for: - a .parquetbundle FILE (the documented distribution format) - a directory that does not exist - a directory containing no ProtSpace tables The try/except only fires on a read error, and a path that does not exist is never read, so it never triggered. Add _validate_data_path(), called before the try so its ValueError is not re-wrapped. The bundle case names extract_bundle_to_dir() in the message, since that is what the existing callers already do. The check is "at least one core file" rather than "all three": directories carrying only selected_annotations.parquet are legitimate and test_bundle_version.py builds exactly those. --- .../src/protspace/utils/arrow_reader.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/apps/protspace/src/protspace/utils/arrow_reader.py b/apps/protspace/src/protspace/utils/arrow_reader.py index 14e13dfc1..4ccf3c104 100644 --- a/apps/protspace/src/protspace/utils/arrow_reader.py +++ b/apps/protspace/src/protspace/utils/arrow_reader.py @@ -60,8 +60,44 @@ def __init__(self, source: Path | dict): self._load_data() self._build_data_structure() + _CORE_FILENAMES = ( + "selected_annotations.parquet", + "projections_metadata.parquet", + "projections_data.parquet", + ) + + def _validate_data_path(self) -> None: + """Fail loudly when data_path cannot yield any ProtSpace table. + + Every probe in _load_data is an ``.exists()`` test with an empty-frame fallback, so a + .parquetbundle FILE, a missing directory, or an unrelated directory all produced a + reader reporting 0 proteins / 0 projections / format_version 1 and raised nothing. + + The check is "at least one core file", not "all three": extracted directories carrying + only selected_annotations.parquet are legitimate (see test_bundle_version.py). + """ + path = self.data_path + if path.is_file(): + if path.suffix.lower() == ".parquetbundle": + raise ValueError( + f"'{path}' is a .parquetbundle file; ArrowReader reads a directory of " + "parquet files. Extract it first with " + "protspace.data.io.bundle.extract_bundle_to_dir()." + ) + raise ValueError( + f"'{path}' is a file; ArrowReader expects a directory of .parquet files." + ) + if not path.is_dir(): + raise FileNotFoundError(f"No such directory: '{path}'") + if not any((path / name).exists() for name in self._CORE_FILENAMES): + raise ValueError( + f"Directory '{path}' contains none of {', '.join(self._CORE_FILENAMES)}." + ) + def _load_data(self): """Load data from Parquet files.""" + # Before the try: this ValueError must not be re-wrapped by the handler below. + self._validate_data_path() try: protein_annotations_path = self.data_path / "selected_annotations.parquet" projections_metadata_path = self.data_path / "projections_metadata.parquet"