Coverage for lintro / config / lintro_config.py: 100%
33 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Main Lintro configuration model."""
3from typing import Any
5from pydantic import BaseModel, ConfigDict, Field
7from lintro.ai.config import AIConfig
8from lintro.config.enforce_config import EnforceConfig
9from lintro.config.execution_config import ExecutionConfig
10from lintro.config.tool_config import LintroToolConfig
12__all__ = [
13 "AIConfig",
14 "EnforceConfig",
15 "ExecutionConfig",
16 "LintroConfig",
17 "LintroToolConfig",
18]
21class LintroConfig(BaseModel):
22 """Main Lintro configuration container.
24 This is the root configuration object loaded from .lintro-config.yaml.
25 Follows the tiered model:
27 1. execution: What tools run and how
28 2. enforce: Cross-cutting settings that override native configs
29 3. defaults: Fallback config when no native config exists
30 4. tools: Per-tool enable/disable and config source
31 5. ai: Optional AI-powered issue intelligence
33 Attributes:
34 model_config: Pydantic model configuration.
35 execution: Execution control settings.
36 enforce: Cross-cutting settings enforced via CLI flags.
37 defaults: Fallback configs for tools without native configs.
38 tools: Per-tool configuration, keyed by tool name.
39 ai: AI-powered features configuration (optional, disabled by default).
40 config_path: Path to the config file (set by loader).
41 """
43 model_config = ConfigDict(frozen=False, extra="forbid")
45 execution: ExecutionConfig = Field(default_factory=ExecutionConfig)
46 enforce: EnforceConfig = Field(default_factory=EnforceConfig)
47 defaults: dict[str, dict[str, Any]] = Field(default_factory=dict)
48 tools: dict[str, LintroToolConfig] = Field(default_factory=dict)
49 ai: AIConfig = Field(default_factory=AIConfig)
50 config_path: str | None = None
52 def get_tool_config(self, tool_name: str) -> LintroToolConfig:
53 """Get configuration for a specific tool.
55 Args:
56 tool_name: Name of the tool (e.g., "ruff", "black").
58 Returns:
59 LintroToolConfig: Tool configuration. Returns default config if not
60 explicitly configured.
61 """
62 return self.tools.get(tool_name.lower(), LintroToolConfig())
64 def is_tool_enabled(self, tool_name: str) -> bool:
65 """Check if a tool is enabled.
67 A tool is enabled if:
68 1. execution.enabled_tools is empty (all tools enabled), OR
69 2. tool_name is in execution.enabled_tools, AND
70 3. The tool's config has enabled=True (default)
72 Args:
73 tool_name: Name of the tool.
75 Returns:
76 bool: True if tool should run.
77 """
78 tool_lower = tool_name.lower()
80 # Check execution.enabled_tools filter
81 if self.execution.enabled_tools:
82 enabled_lower = [t.lower() for t in self.execution.enabled_tools]
83 if tool_lower not in enabled_lower:
84 return False
86 # Check tool-specific enabled flag
87 tool_config = self.get_tool_config(tool_lower)
88 return bool(tool_config.enabled)
90 def get_tool_defaults(self, tool_name: str) -> dict[str, Any]:
91 """Get default configuration for a tool.
93 Used when the tool has no native config file.
95 Args:
96 tool_name: Name of the tool.
98 Returns:
99 dict[str, Any]: Default configuration or empty dict.
100 """
101 return self.defaults.get(tool_name.lower(), {})
103 def get_effective_line_length(self, tool_name: str) -> int | None:
104 """Get effective line length for a specific tool.
106 In the tiered model, this simply returns the enforce.line_length
107 value, which will be injected via CLI flags.
109 Args:
110 tool_name: Name of the tool (unused, kept for compatibility).
112 Returns:
113 int | None: Enforced line length or None.
114 """
115 line_length: int | None = self.enforce.line_length
116 return line_length
118 def get_effective_target_python(self, tool_name: str) -> str | None:
119 """Get effective Python target version for a specific tool.
121 In the tiered model, this simply returns the enforce.target_python
122 value, which will be injected via CLI flags.
124 Args:
125 tool_name: Name of the tool (unused, kept for compatibility).
127 Returns:
128 str | None: Enforced target version or None.
129 """
130 target_python: str | None = self.enforce.target_python
131 return target_python