diff --git a/openhcs/core/steps/function_step.py b/openhcs/core/steps/function_step.py index f59baf6e2..0ba2aba8e 100644 --- a/openhcs/core/steps/function_step.py +++ b/openhcs/core/steps/function_step.py @@ -513,8 +513,12 @@ def _process_single_pattern_group( ) if not matching_files: - logger.warning(f"No matching files for pattern group {pattern_repr} in {step_input_dir}") - return + raise ValueError( + f"No matching files found for pattern group {pattern_repr} in {step_input_dir}. " + f"This indicates either: (1) no image files exist in the directory, " + f"(2) files don't match the pattern, or (3) pattern parsing failed. " + f"Check that input files exist and match the expected naming convention." + ) logger.debug(f"🔥 PATTERN: Found {len(matching_files)} files: {[Path(f).name for f in matching_files]}") @@ -532,8 +536,12 @@ def _process_single_pattern_group( raw_slices = [] if not raw_slices: - logger.warning(f"No valid images loaded for pattern group {pattern_repr} in {step_input_dir}") - return + raise ValueError( + f"No valid images loaded for pattern group {pattern_repr} in {step_input_dir}. " + f"Found {len(matching_files)} matching files but failed to load any valid images. " + f"This indicates corrupted image files, unsupported formats, or I/O errors. " + f"Check file integrity and format compatibility." + ) # 🔍 DEBUG: Log stacking operation logger.debug(f"🔍 STACKING: {len(raw_slices)} slices → memory_type: {input_memory_type_from_plan}") @@ -840,14 +848,21 @@ def process(self, context: 'ProcessingContext') -> None: logger.info(f"🔥 STEP: Starting processing for '{step_name}' well {well_id} (group_by={group_by.name}, variable_components={[vc.name for vc in variable_components]})") - if well_id in patterns_by_well: - if isinstance(patterns_by_well[well_id], dict): - # Grouped patterns (when group_by is set) - for comp_val, pattern_list in patterns_by_well[well_id].items(): - logger.debug(f"🔥 STEP: Component '{comp_val}' has {len(pattern_list)} patterns: {pattern_list}") - else: - # Ungrouped patterns (when group_by is None) - logger.debug(f"🔥 STEP: Found {len(patterns_by_well[well_id])} ungrouped patterns: {patterns_by_well[well_id]}") + if well_id not in patterns_by_well: + raise ValueError( + f"No patterns detected for well '{well_id}' in step '{step_name}' (ID: {step_id}). " + f"This indicates either: (1) no image files found for this well, " + f"(2) image files don't match the expected naming pattern, or " + f"(3) pattern detection failed. Check input directory: {step_input_dir}" + ) + + if isinstance(patterns_by_well[well_id], dict): + # Grouped patterns (when group_by is set) + for comp_val, pattern_list in patterns_by_well[well_id].items(): + logger.debug(f"🔥 STEP: Component '{comp_val}' has {len(pattern_list)} patterns: {pattern_list}") + else: + # Ungrouped patterns (when group_by is None) + logger.debug(f"🔥 STEP: Found {len(patterns_by_well[well_id])} ungrouped patterns: {patterns_by_well[well_id]}") if func_from_plan is None: raise ValueError(f"Step plan missing 'func' for step: {step_plan.get('step_name', 'Unknown')} (ID: {step_id})") diff --git a/openhcs/formats/pattern/pattern_discovery.py b/openhcs/formats/pattern/pattern_discovery.py index 6b335d744..d98807406 100644 --- a/openhcs/formats/pattern/pattern_discovery.py +++ b/openhcs/formats/pattern/pattern_discovery.py @@ -385,6 +385,12 @@ def _generate_patterns_for_files( # 🔒 Clause 92 — Structural Validation First # Validate the final pattern list if not patterns: - logger.warning("No patterns generated from files") + raise ValueError( + "No patterns generated from files. This indicates either: " + "(1) no image files found in the directory, " + "(2) files don't match the expected naming convention, or " + "(3) pattern generation logic failed. " + "Check that image files exist and follow the expected naming pattern." + ) return patterns diff --git a/openhcs/io/zarr.py b/openhcs/io/zarr.py index 11eda3f70..d1ffe68a2 100644 --- a/openhcs/io/zarr.py +++ b/openhcs/io/zarr.py @@ -208,7 +208,7 @@ def load_batch(self, file_paths: List[Union[str, Path]], **kwargs) -> List[Any]: # Check which requested files are in this well for i, path in enumerate(file_paths): - filename = str(path) # Use full path for matching + filename = Path(path).name # Use filename only for matching if filename in filename_map: if well_name not in well_to_files: well_to_files[well_name] = [] @@ -410,7 +410,7 @@ def save_batch(self, data_list: List[Any], output_paths: List[Union[str, Path]], z_idx = remaining % n_z # Store as tuple (field, channel, z) - y,x are full slices - filename_map[str(path)] = (field_idx, channel_idx, z_idx) + filename_map[Path(path).name] = (field_idx, channel_idx, z_idx) field_array = field_group['0'] field_array.attrs["openhcs_filename_map"] = filename_map diff --git a/openhcs/pyqt_gui/windows/help_windows.py b/openhcs/pyqt_gui/windows/help_windows.py index f1395e9bc..a919211d5 100644 --- a/openhcs/pyqt_gui/windows/help_windows.py +++ b/openhcs/pyqt_gui/windows/help_windows.py @@ -230,7 +230,7 @@ def show_docstring_help(cls, target: Union[Callable, type], title: Optional[str] cls._docstring_window = None # Create new window - cls._docstring_window = DocstringHelpWindow(target, title, parent) + cls._docstring_window = DocstringHelpWindow(target, title=title, parent=parent) cls._docstring_window.show() except Exception as e: @@ -257,7 +257,7 @@ def show_parameter_help(cls, param_name: str, param_description: str, param_type cls._parameter_window = None # Create new window - cls._parameter_window = ParameterHelpWindow(param_name, param_description, param_type, parent) + cls._parameter_window = ParameterHelpWindow(param_name, param_description, param_type, parent=parent) cls._parameter_window.show() except Exception as e: