Skip to content

Commit f2d0cc7

Browse files
committed
Handle CTI load failures and track failed files
Treat failed CTI loads as a mapping of CTI->error and record failures when initializing the shared Harvester. gentl_discovery.py now logs exceptions, captures failed_files as a dict, and raises a runtime error if no CTI producers were successfully loaded; it also attaches loaded_files and failed_files to raised exceptions and attempts to reset the harvester. gentl_backend.py updated to consume failed_files as a dict (using .items()) when building reporting data. Added a helper to reset the harvester and propagate context for callers.
1 parent 95c8647 commit f2d0cc7

2 files changed

Lines changed: 33 additions & 7 deletions

File tree

dlclivegui/cameras/backends/gentl_backend.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,11 +565,13 @@ def _acquire_shared_harvester(self, loaded: list[str]) -> list:
565565
self._harvester = self._shared_entry.harvester
566566

567567
actual_loaded = list(getattr(self._shared_entry, "loaded_files", loaded))
568-
actual_failed = list(getattr(self._shared_entry, "failed_files", []))
568+
actual_failed = dict(getattr(self._shared_entry, "failed_files", {}))
569569

570570
ns["cti_files_loaded"] = actual_loaded
571571
if actual_failed:
572-
ns["cti_files_failed"] = [{"cti": str(cti), "error": str(error)} for cti, error in actual_failed]
572+
ns["cti_files_failed"] = [
573+
{"cti": str(cti), "error": str(error)} for cti, error in actual_failed.items()
574+
]
573575

574576
with self._shared_entry.lock:
575577
infos = list(self._harvester.device_info_list or [])
@@ -583,11 +585,11 @@ def _acquire_shared_harvester(self, loaded: list[str]) -> list:
583585

584586
except Exception as exc:
585587
exc_loaded = list(getattr(exc, "loaded_files", []))
586-
exc_failed = list(getattr(exc, "failed_files", []))
588+
exc_failed = dict(getattr(exc, "failed_files", {}))
587589

588590
if exc_loaded or exc_failed:
589591
ns["cti_files_loaded"] = [str(p) for p in exc_loaded]
590-
ns["cti_files_failed"] = [{"cti": str(cti), "error": str(error)} for cti, error in exc_failed]
592+
ns["cti_files_failed"] = [{"cti": str(cti), "error": str(error)} for cti, error in exc_failed.items()]
591593

592594
if self._shared_entry is not None:
593595
try:

dlclivegui/cameras/backends/utils/gentl_discovery.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from __future__ import annotations
66

77
import glob
8+
import logging
89
import os
910
import threading
1011
from collections.abc import Iterable, Sequence
@@ -24,6 +25,8 @@ class GenTLDiscoveryPolicy(Enum):
2425
except Exception: # pragma: no cover - optional dependency
2526
Harvester = None # type: ignore
2627

28+
logger = logging.getLogger(__name__)
29+
2730

2831
class SharedHarvesterEntry:
2932
"""
@@ -41,13 +44,34 @@ def __init__(self, cti_files: list[str]):
4144
self.refcount = 0
4245
self.harvester = Harvester()
4346
self.loaded_files: list[str] = []
47+
self.failed_files: dict[str, str] = {}
4448

4549
for cti in self.key:
46-
self.harvester.add_file(cti)
47-
self.loaded_files.append(cti)
50+
try:
51+
self.harvester.add_file(cti)
52+
self.loaded_files.append(cti)
53+
except Exception as e:
54+
logger.exception(f"Failed to load CTI file: {cti}. Skipping.")
55+
self.failed_files[cti] = str(e)
56+
57+
if not self.loaded_files:
58+
e = RuntimeError("No GenTL producer (.cti) could be loaded by shared Harvester.")
59+
self._raise_and_reset_harvester(e)
4860

4961
# Initial device enumeration.
50-
self.harvester.update()
62+
try:
63+
self.harvester.update()
64+
except Exception as e:
65+
self._raise_and_reset_harvester(e)
66+
67+
def _raise_and_reset_harvester(self, exc: Exception) -> None:
68+
exc.loaded_files = self.loaded_files[:]
69+
exc.failed_files = dict(self.failed_files)
70+
try:
71+
self.harvester.reset()
72+
except Exception:
73+
pass
74+
raise exc
5175

5276

5377
class SharedHarvesterPool:

0 commit comments

Comments
 (0)