|
7 | 7 | - Lazy image reading with Dask support |
8 | 8 | - Video reading with OpenCV and optional PyAV fallback |
9 | 9 | - Superkeypoints diagram and JSON loading |
| 10 | +
|
| 11 | +NOTE: write_hdf could use some refactoring to split responsibilities more cleanly |
| 12 | +A potentially interesting direction could be to move toward a more schema-centric |
| 13 | +architecture, where objects own and guarantee their own validity, and writing |
| 14 | +is just a translation of already-valid objects to disk. This would also make |
| 15 | +validation more consistent across reads and writes. |
| 16 | +Config I/O is an especially good candidate for this, the logic is a bit spread right now |
| 17 | +(here, config_sync.py, and the sidecar JSON) |
10 | 18 | """ |
11 | 19 | # src/napari_deeplabcut/core/io.py |
12 | 20 |
|
|
17 | 25 | import logging |
18 | 26 | import os |
19 | 27 | from collections.abc import Callable |
| 28 | +from glob import has_magic |
20 | 29 | from importlib import resources |
21 | 30 | from pathlib import Path |
22 | 31 | from typing import Any |
|
61 | 70 | # ----------------------------------------------------------------------------- |
62 | 71 | # Supported formats (shared by image/video readers) |
63 | 72 | # ----------------------------------------------------------------------------- |
64 | | -_GLOB_MAGIC = set("*?[") |
65 | 73 | _SUPPORTED_SUFFIXES = {ext.lower() for ext in SUPPORTED_IMAGES} |
66 | 74 | DLC_CANONICAL_H5_KEY = "df_with_missing" # TODO use this key instead of str literal in all places |
67 | 75 | FALLBACK_H5_KEYS = ["keypoints"] |
68 | 76 |
|
69 | | - |
70 | | -def _has_glob_magic(name: str) -> bool: |
71 | | - return any(ch in name for ch in _GLOB_MAGIC) |
72 | | - |
73 | | - |
74 | 77 | # ============================================================================= |
75 | 78 | # CONFIG (YAML) |
76 | 79 | # ============================================================================= |
77 | 80 |
|
78 | 81 |
|
79 | 82 | def load_config(config_path: str): |
80 | 83 | # NOTE: intentionally minimal; callers own error handling |
81 | | - with open(config_path) as file: |
82 | | - return yaml.safe_load(file) |
| 84 | + try: |
| 85 | + with open(config_path) as file: |
| 86 | + return yaml.safe_load(file) |
| 87 | + except yaml.YAMLError as e: |
| 88 | + logger.warning(f"Invalid YAML in config file {config_path}: {e}") |
| 89 | + raise e |
83 | 90 |
|
84 | 91 |
|
85 | 92 | # Read config file and create keypoint layer metadata |
@@ -492,6 +499,11 @@ def writer(path: str, data: Any, attributes: dict) -> List[str] |
492 | 499 | guarantee_multiindex_rows(df_new) |
493 | 500 | guarantee_multiindex_rows(df_old) |
494 | 501 | except Exception: |
| 502 | + logger.warning( |
| 503 | + "Could not guarantee multiindex rows for new or existing data; " |
| 504 | + "attempting to harmonize indices for merge.", |
| 505 | + exc_info=True, |
| 506 | + ) |
495 | 507 | pass |
496 | 508 |
|
497 | 509 | df_new, df_old = harmonize_keypoint_row_index(df_new, df_old) |
@@ -639,7 +651,7 @@ def _expand_image_paths(path: str | Path | list[str | Path] | tuple[str | Path, |
639 | 651 | expanded.extend(files) |
640 | 652 | continue |
641 | 653 |
|
642 | | - if not _has_glob_magic(p.name): |
| 654 | + if not has_magic(p.name): |
643 | 655 | if p.is_file() and p.suffix.lower() in _SUPPORTED_SUFFIXES: |
644 | 656 | expanded.append(p) |
645 | 657 | continue |
|
0 commit comments