"""Handles loading and merging of configuration files from standard locations."""frompathlibimportPathimportyamlfromtypingimportAny,Dict# Define the standard search paths in one place.# The order is from least specific (system-wide) to most specific (user).CONFIG_SEARCH_PATHS=[Path("/etc/arguslib/"),Path.home()/".arguslib",Path.home()/".config"/"arguslib",]
[docs]defload_config(filename:str)->Dict[str,Any]:""" Loads and merges YAML configuration from standard locations. It searches for `filename` in the defined `CONFIG_SEARCH_PATHS`. Configurations are merged, with values from files found later in the search path (i.e., user-specific) overriding earlier ones (system-wide). Args: filename: The name of the YAML file (e.g., 'cameras.yml'). Returns: A dictionary containing the merged configuration. Raises: FileNotFoundError: If no configuration file is found in any of the search paths. """configs=[]forconfig_dirinCONFIG_SEARCH_PATHS:config_file=config_dir/filenameifconfig_file.exists():withopen(config_file,"r")asf:loaded_yaml=yaml.safe_load(f)ifloaded_yaml:# Ensure file is not emptyconfigs.append(loaded_yaml)ifnotconfigs:raiseFileNotFoundError(f"No configuration file named '{filename}' found in search paths.")# Merge configs. The last one found (most specific) wins.merged_config={}forconfiginconfigs:merged_config.update(config)returnmerged_config
[docs]defload_path_from_config(filename:str)->Path:"""Loads a single path from a text file in standard config locations."""# Search in reverse order to find user-specific file firstforconfig_dirinreversed(CONFIG_SEARCH_PATHS):config_file=config_dir/filenameifconfig_file.exists():returnPath(config_file.read_text().strip())raiseFileNotFoundError(f"Path configuration file '{filename}' not found.")