# Gin-Config Strict Pattern: Reference Examples ## Example 1: Config class + loader + .gin file ### `src/conf/pipeline_conf.py` ```python from __future__ import annotations import gin @gin.configurable class PipelineConfig: """Configuration for the augmentation pipeline stages and output.""" def __init__( self, input_root: str = "/data/UAV-GeoLoc", output_root: str = "/data/UAV-GeoLoc-aug", stages: list[str] | None = None, save_npy: bool = True, save_vis: bool = True, save_concat: bool = False, resume: bool = True, subset: str | None = None, source: str | None = None, log_level: str = "INFO", ) -> None: self.input_root = input_root self.output_root = output_root self.stages = stages or ["depth", "edges", "segmentation"] self.save_npy = save_npy self.save_vis = save_vis self.save_concat = save_concat self.resume = resume self.subset = subset self.source = source self.log_level = log_level def get_pipeline_cfg(path2cfg: str) -> PipelineConfig: """Load pipeline config from gin file. Args: path2cfg: Path to config directory (with trailing slash). Returns: Instantiated PipelineConfig with values from gin file. """ gin.parse_config_file(f"{path2cfg}pipeline.gin") return PipelineConfig() ``` ### `in/config_files/pipeline.gin` ```gin # Pipeline configuration PipelineConfig.input_root = '/data/UAV-GeoLoc' PipelineConfig.output_root = '/data/UAV-GeoLoc-aug' PipelineConfig.stages = ['depth', 'edges', 'segmentation'] PipelineConfig.save_npy = True PipelineConfig.save_vis = True PipelineConfig.save_concat = False PipelineConfig.resume = True PipelineConfig.subset = 'Rot' PipelineConfig.source = None PipelineConfig.log_level = 'INFO' ``` ## Example 2: Model config with fallback IDs ### `src/conf/models_conf.py` ```python from __future__ import annotations import gin @gin.configurable class ModelsConfig: """Model identifiers and fallback strategy.""" def __init__( self, depth_model_id: str = "depth-anything/DA3-BASE", depth_fallback_id: str = "depth-anything/Depth-Anything-V2-Large-hf", seg_model_type: str = "segearth-ov3", seg_fallback_type: str = "segformer-b5", seg_fallback_id: str = "nvidia/segformer-b5-finetuned-ade-640-640", ) -> None: self.depth_model_id = depth_model_id self.depth_fallback_id = depth_fallback_id self.seg_model_type = seg_model_type self.seg_fallback_type = seg_fallback_type self.seg_fallback_id = seg_fallback_id def get_models_cfg(path2cfg: str) -> ModelsConfig: """Load models config from gin file.""" gin.parse_config_file(f"{path2cfg}models.gin") return ModelsConfig() ``` ## Example 3: Central config loader (`src/conf/config_loader.py`) ```python from __future__ import annotations import logging from pathlib import Path from typing import Any import gin from conf.pipeline_conf import PipelineConfig from conf.hardware_conf import HardwareConfig from conf.models_conf import ModelsConfig from conf.input_conf import InputConfig from conf.seg_conf import SegConfig logger = logging.getLogger(__name__) def load_all_configs(path2cfg: str) -> dict[str, Any]: """Parse ALL .gin files at once and return all config objects. CRITICAL: calls gin.clear_config() to reset global state. Args: path2cfg: Path to config directory (WITH trailing slash). Returns: Dict with config objects keyed by name. """ cfg_dir = Path(path2cfg) gin_files = sorted(cfg_dir.glob("*.gin")) gin.clear_config() # MUST reset before loading gin.parse_config_files_and_bindings( config_files=[str(f) for f in gin_files], bindings=[], ) return { "pipeline": PipelineConfig(), "hardware": HardwareConfig(), "models": ModelsConfig(), "input": InputConfig(), "seg": SegConfig(), } # Individual loaders — TESTING ONLY (always clear_config first): def get_hardware_cfg(path2cfg: str) -> HardwareConfig: gin.clear_config() gin.parse_config_file(f"{path2cfg}hardware.gin") return HardwareConfig() ``` ## Example 4: Main entry point (uses load_all_configs) ### `src/main.py` ```python from __future__ import annotations import logging import numpy as np import torch from conf.config_loader import load_all_configs from utils.utils_file_dir import get_proj_dir logger = logging.getLogger(__name__) def main() -> None: """Entry point: load ALL configs at once, then run pipeline.""" proj_dir = get_proj_dir() path2cfg = f"{proj_dir}in/config_files/" # ONE call loads everything: configs = load_all_configs(path2cfg) # Set seeds: torch.manual_seed(42) np.random.seed(42) # Pass configs explicitly: run_pipeline( configs["pipeline"], configs["hardware"], configs["models"], configs["input"], configs["seg"], ) if __name__ == "__main__": main() ``` ## Example 4: Model loading with config object ```python from __future__ import annotations import gc import logging from typing import Any import torch import torch.nn as nn logger = logging.getLogger(__name__) def load_depth_model( models_conf: ModelsConfig, hardware_conf: HardwareConfig, device: torch.device, ) -> nn.Module: """Load depth estimation model based on config. Args: models_conf: Model IDs from gin config. hardware_conf: FP16 and device settings. device: Target CUDA device. Returns: Loaded depth model on device. """ model_id = models_conf.depth_model_id logger.info("Loading depth: %s", model_id) try: from depth_anything_3 import DepthAnything3 model = DepthAnything3.from_pretrained(model_id) if hardware_conf.use_fp16: model = model.half() return model.to(device).eval() except ImportError: logger.warning("DA3 not found, falling back to %s", models_conf.depth_fallback_id) from transformers import AutoModelForDepthEstimation dtype = torch.float16 if hardware_conf.use_fp16 else torch.float32 model = AutoModelForDepthEstimation.from_pretrained( models_conf.depth_fallback_id, torch_dtype=dtype, ) return model.to(device).eval() def unload_model(model: Any) -> None: """Free GPU memory after model use.""" del model gc.collect() if torch.cuda.is_available(): torch.cuda.empty_cache() ``` ## Example 5: Model loading with config objects (not hardcoded IDs) ```python # BAD: dataclass + gin @gin.configurable @dataclass # ← FORBIDDEN class Config: param: int = 1 # BAD: argparse parser = argparse.ArgumentParser() # ← FORBIDDEN, use gin # BAD: global gin state inside function def process(): val = gin.query_parameter("Config.param") # ← FORBIDDEN # BAD: gin.constant / macros LEARNING_RATE = gin.constant("lr", 0.001) # ← FORBIDDEN # BAD: hardcoded model ID model = AutoModel.from_pretrained("depth-anything/DA3-BASE") # ← move to gin ```