Coverage for lintro / plugins / protocol.py: 94%
31 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"""Plugin protocol defining the contract for all Lintro tools.
3This module defines the core abstractions for Lintro's plugin system:
4- ToolDefinition: Metadata describing what a tool IS
5- LintroPlugin: Protocol contract that all tools must satisfy
7Example:
8 >>> from lintro.plugins.protocol import ToolDefinition, LintroPlugin
9 >>> from lintro.plugins.base import BaseToolPlugin
10 >>>
11 >>> class MyPlugin(BaseToolPlugin):
12 ... @property
13 ... def definition(self) -> ToolDefinition:
14 ... return ToolDefinition(name="my-tool", description="My custom tool")
15"""
17from __future__ import annotations
19from dataclasses import dataclass, field
20from typing import TYPE_CHECKING, Protocol, runtime_checkable
22from lintro.enums.tool_type import ToolType
24if TYPE_CHECKING:
25 from lintro.models.core.tool_result import ToolResult
28@dataclass(frozen=True)
29class ToolDefinition:
30 """Metadata describing a Lintro tool.
32 This is the single source of truth for what a tool IS.
33 Implementation details go in separate files (under implementations/).
35 Attributes:
36 name: Unique identifier for the tool (lowercase, e.g., "hadolint").
37 description: Human-readable description of what the tool does.
38 can_fix: Whether the tool can auto-fix issues.
39 tool_type: Bitmask of ToolType flags describing capabilities.
40 file_patterns: Glob patterns for files this tool operates on.
41 priority: Execution priority (lower = runs first). Default is 50.
42 conflicts_with: Names of tools that conflict with this one.
43 native_configs: Config files the tool respects natively
44 (Lintro won't interfere).
45 version_command: Command to check tool version
46 (e.g., ["hadolint", "--version"]).
47 min_version: Minimum required version string.
48 default_options: Default tool-specific options.
49 default_timeout: Default execution timeout in seconds.
50 """
52 # Identity
53 name: str
54 description: str
56 # Capabilities
57 can_fix: bool = False
58 tool_type: ToolType = ToolType.LINTER
60 # File targeting
61 file_patterns: list[str] = field(default_factory=list)
63 # Execution
64 priority: int = 50
65 conflicts_with: list[str] = field(default_factory=list)
67 # Native config files this tool respects (Lintro should NOT interfere)
68 native_configs: list[str] = field(default_factory=list)
70 # Version checking
71 version_command: list[str] | None = None
72 min_version: str | None = None
74 # Default options
75 default_options: dict[str, object] = field(default_factory=dict)
76 default_timeout: int = 30
78 def __post_init__(self) -> None:
79 """Validate tool definition.
81 Raises:
82 ValueError: If name is empty or priority is negative.
83 """
84 if not self.name:
85 raise ValueError("Tool name cannot be empty")
86 if self.priority < 0:
87 raise ValueError(f"Tool priority must be non-negative, got {self.priority}")
90@runtime_checkable
91class LintroPlugin(Protocol):
92 """Contract that all Lintro tools must satisfy.
94 This protocol defines the interface for tool plugins. Tools can be
95 implemented by inheriting from BaseToolPlugin or by implementing
96 this protocol directly.
98 Example:
99 >>> class MyPlugin:
100 ... @property
101 ... def definition(self) -> ToolDefinition:
102 ... return ToolDefinition(name="my-tool", description="My tool")
103 ...
104 ... def check(self, paths, options) -> ToolResult:
105 ... ... # Implementation here
106 ...
107 ... def fix(self, paths, options) -> ToolResult:
108 ... raise NotImplementedError("No fixing")
109 ...
110 ... def set_options(self, **kwargs: object) -> None:
111 ... # Set tool options
112 ... ...
113 """
115 @property
116 def definition(self) -> ToolDefinition:
117 """Return the tool's metadata."""
118 ...
120 def check(self, paths: list[str], options: dict[str, object]) -> ToolResult:
121 """Check files for issues.
123 Args:
124 paths: List of file or directory paths to check.
125 options: Tool-specific options that override defaults.
127 Returns:
128 ToolResult containing check results and any issues found.
129 """
130 ...
132 def fix(self, paths: list[str], options: dict[str, object]) -> ToolResult:
133 """Fix issues in files.
135 Args:
136 paths: List of file or directory paths to fix.
137 options: Tool-specific options that override defaults.
139 Returns:
140 ToolResult containing fix results and any remaining issues.
141 """
142 ...
144 def set_options(self, **kwargs: object) -> None:
145 """Set tool-specific options.
147 Args:
148 **kwargs: Tool-specific options to set.
149 """
150 ...
152 def doc_url(self, code: str) -> str | None:
153 """Return a documentation URL for the given rule code.
155 Args:
156 code: The rule/error code (e.g., "E501", "SC2086").
158 Returns:
159 Documentation URL string, or None if no docs available.
160 """
161 ...