Coverage for packages/core/src/langgate/core/utils/config_utils.py: 57%
42 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-04-09 21:23 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-04-09 21:23 +0000
1"""Configuration utilities for LangGate."""
3import os
4from pathlib import Path
5from typing import TypeVar
7import yaml
8from pydantic import BaseModel, ValidationError
10from langgate.core.logging import StructLogger
12ConfigSchemaT = TypeVar("ConfigSchemaT", bound=BaseModel)
15def resolve_path(
16 env_var: str,
17 arg_path: Path | None = None,
18 default_path: Path | None = None,
19 path_desc: str = "path",
20 logger: StructLogger | None = None,
21) -> Path:
22 """Resolve a file path based on priority: args > env > default.
24 Args:
25 env_var: Environment variable name to check
26 arg_path: Path provided in constructor args
27 default_path: Default path to use if others not provided
28 path_desc: Description for logging
29 logger: Optional logger instance for recording path resolution
31 Returns:
32 Resolved Path object
33 """
34 # Priority: args > env > default
35 resolved_path = arg_path or Path(os.getenv(env_var, str(default_path)))
37 # Log the resolved path and its existence
38 if logger:
39 exists = resolved_path.exists()
40 logger.debug(
41 f"resolved_{path_desc}",
42 path=str(resolved_path),
43 exists=exists,
44 source="args" if arg_path else ("env" if os.getenv(env_var) else "default"),
45 )
47 return resolved_path
50def load_yaml_config(
51 config_path: Path,
52 schema_class: type[ConfigSchemaT],
53 logger: StructLogger | None = None,
54) -> ConfigSchemaT | None:
55 """Load and validate a YAML configuration file using a Pydantic schema.
57 Args:
58 config_path: Path to the YAML configuration file
59 schema_class: The Pydantic schema class to validate against
60 logger: Optional logger instance for recording validation results
62 Returns:
63 The validated schema instance or None if validation failed
64 """
65 try:
66 if not config_path.exists():
67 if logger:
68 logger.warning(
69 "config_file_not_found",
70 config_path=str(config_path),
71 )
72 return None
74 with open(config_path) as f:
75 raw_config = yaml.safe_load(f)
77 if raw_config is None:
78 if logger:
79 logger.warning("config_file_is_empty", config_path=str(config_path))
80 return None
82 # Validate configuration using Pydantic schema
83 try:
84 config = schema_class.model_validate(raw_config)
85 if logger:
86 logger.info(
87 "loaded_config",
88 config_path=str(config_path),
89 )
90 return config
91 except ValidationError as exc:
92 if logger:
93 logger.exception(
94 "invalid_config_format",
95 config_path=str(config_path),
96 errors=str(exc),
97 )
98 return None
100 except yaml.YAMLError:
101 if logger:
102 logger.exception(
103 "failed_to_parse_yaml_config", config_path=str(config_path)
104 )
105 return None
106 except Exception:
107 if logger:
108 logger.exception("failed_to_load_config", config_path=str(config_path))
109 return None