Spaces:
Sleeping
Sleeping
| """Utility validators for user-provided parameters and resources. | |
| This module centralizes sanity checks for user supplied values so they can | |
| be reused from both the Gradio callbacks and lower level logic modules. | |
| """ | |
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import Iterable | |
| from PIL import Image | |
| class ValidationError(ValueError): | |
| """Raised when user parameters cannot be processed safely.""" | |
| def ensure_image(image: Image.Image | None) -> Image.Image: | |
| """Return a validated PIL image. | |
| Args: | |
| image (PIL.Image.Image | None): User uploaded image. | |
| Returns: | |
| PIL.Image.Image: The validated image converted to RGB mode. | |
| Raises: | |
| ValidationError: If the image is missing. | |
| """ | |
| if image is None: | |
| raise ValidationError("Please upload an image or select an example before generating a mosaic.") | |
| if image.mode != "RGB": | |
| image = image.convert("RGB") | |
| return image | |
| def ensure_positive_int(value: int, name: str, minimum: int = 1, maximum: int | None = None) -> int: | |
| """Validate that an integer parameter falls within an accepted range. | |
| Args: | |
| value (int): Parameter to validate. | |
| name (str): Human readable parameter name for error messages. | |
| minimum (int, optional): Inclusive lower bound. Defaults to 1. | |
| maximum (int | None, optional): Inclusive upper bound when provided. | |
| Returns: | |
| int: The validated integer. | |
| Raises: | |
| ValidationError: If the value is outside of the allowed range. | |
| """ | |
| if not isinstance(value, int): | |
| raise ValidationError(f"{name} must be an integer.") | |
| if value < minimum: | |
| raise ValidationError(f"{name} must be at least {minimum}.") | |
| if maximum is not None and value > maximum: | |
| raise ValidationError(f"{name} must be less than or equal to {maximum}.") | |
| return value | |
| def ensure_grid_divisibility(image_size: int, grid_size: int) -> bool: | |
| """Return whether the grid divides the image size, without raising. | |
| Mosaic generation crops the resized image to the nearest size that fits the | |
| requested grid. Instead of blocking users when the division is imperfect, | |
| this helper simply returns ``False`` so callers may display an informational | |
| warning if desired while still proceeding with the closest valid size. | |
| Args: | |
| image_size (int): Target square image size in pixels. | |
| grid_size (int): Number of grid cells per axis. | |
| Returns: | |
| bool: ``True`` if divisible, ``False`` otherwise. | |
| """ | |
| if grid_size <= 0 or image_size <= 0: | |
| raise ValidationError("Image and grid sizes must be positive integers.") | |
| if grid_size > image_size: | |
| raise ValidationError("Grid size cannot exceed the resized image dimensions.") | |
| return image_size % grid_size == 0 | |
| def ensure_file_exists(path: str | Path, description: str) -> Path: | |
| """Validate that a file path exists. | |
| Args: | |
| path (str | Path): Path to the required resource. | |
| description (str): Friendly description for the user. | |
| Returns: | |
| pathlib.Path: Normalized path. | |
| Raises: | |
| FileNotFoundError: If the path does not exist. | |
| """ | |
| normalized = Path(path) | |
| if not normalized.exists(): | |
| raise FileNotFoundError(f"{description} not found at {normalized}.") | |
| return normalized | |
| def ensure_non_empty(collection: Iterable, description: str) -> None: | |
| """Verify that an iterable contains elements. | |
| Args: | |
| collection (Iterable): Collection to check. | |
| description (str): Description of the expected content. | |
| Raises: | |
| ValidationError: If the iterable is empty. | |
| """ | |
| if not any(True for _ in collection): | |
| raise ValidationError(description) | |