Coverage for lintro / tools / implementations / ruff / commands.py: 84%
67 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"""Ruff command building utilities.
3Functions for building ruff check and format command line arguments.
4"""
6import os
7from typing import TYPE_CHECKING, Any
9from lintro.enums.env_bool import EnvBool
11if TYPE_CHECKING:
12 from lintro.tools.definitions.ruff import RuffPlugin
15def _get_list_option(options: dict[str, Any], key: str) -> list[str]:
16 """Get a list option from options dict, returning empty list if not set.
18 Args:
19 options: Dictionary of options to retrieve from.
20 key: Key to look up in the options dictionary.
22 Returns:
23 List of string values, or empty list if key not found.
24 """
25 value = options.get(key)
26 if value is None:
27 return []
28 # Handle single string value
29 if isinstance(value, str):
30 return [value]
31 # Handle list, tuple, set, or other iterables
32 try:
33 return [str(item) for item in value]
34 except TypeError:
35 # Non-iterable scalar value
36 return [str(value)]
39def _get_set_option(options: dict[str, Any], key: str) -> set[str]:
40 """Get a set option from options dict, returning empty set if not set.
42 Args:
43 options: dict[str, Any]: Dictionary of options to retrieve from.
44 key: str: Key to look up in the options dictionary.
46 Returns:
47 set[str]: Set of string values, or empty set if key not found.
48 """
49 return set(_get_list_option(options, key))
52# Constants from tool_ruff.py
53RUFF_OUTPUT_FORMAT: str = "json"
56def build_ruff_check_command(
57 tool: "RuffPlugin",
58 files: list[str],
59 fix: bool = False,
60) -> list[str]:
61 """Build the ruff check command.
63 Args:
64 tool: RuffTool instance
65 files: list[str]: List of files to check.
66 fix: bool: Whether to apply fixes.
68 Returns:
69 list[str]: List of command arguments.
70 """
71 cmd: list[str] = tool._get_executable_command(tool_name="ruff") + ["check"]
73 # Get enforced settings to avoid duplicate CLI args
74 enforced = tool._get_enforced_settings()
76 # Add Lintro config injection args (--line-length, --target-version)
77 # from enforce tier. This takes precedence over native config and options
78 config_args = tool._build_config_args()
79 if config_args:
80 cmd.extend(config_args)
81 # Add --isolated if in test mode (fallback when no Lintro config)
82 elif os.environ.get("LINTRO_TEST_MODE") == EnvBool.TRUE:
83 cmd.append("--isolated")
85 # Add configuration options
86 selected_rules = _get_list_option(tool.options, "select")
87 ignored_rules = _get_set_option(tool.options, "ignore")
88 extend_selected_rules = _get_list_option(tool.options, "extend_select")
90 # Ensure E501 is included when selecting E-family
91 # Check in selected_rules, extend_selected_rules, and ignored_rules
92 has_e_family = ("E" in selected_rules) or ("E" in extend_selected_rules)
94 # Add E501 when E-family is present and E501 not already ignored
95 # or in selected/extend
96 if (
97 has_e_family
98 and "E501" not in ignored_rules
99 and "E501" not in selected_rules
100 and "E501" not in extend_selected_rules
101 ):
102 extend_selected_rules.append("E501")
104 if selected_rules:
105 cmd.extend(["--select", ",".join(selected_rules)])
106 if ignored_rules:
107 cmd.extend(["--ignore", ",".join(sorted(ignored_rules))])
108 if extend_selected_rules:
109 cmd.extend(["--extend-select", ",".join(extend_selected_rules)])
110 extend_ignored_rules = _get_list_option(tool.options, "extend_ignore")
111 if extend_ignored_rules:
112 cmd.extend(["--extend-ignore", ",".join(extend_ignored_rules)])
113 # Only add line_length/target_version from options if not enforced.
114 # Note: enforced uses Lintro's generic names (line_length, target_python)
115 # while options use tool-specific names (line_length, target_version).
116 if tool.options.get("line_length") and "line_length" not in enforced:
117 cmd.extend(["--line-length", str(tool.options["line_length"])])
118 if tool.options.get("target_version") and "target_python" not in enforced:
119 cmd.extend(["--target-version", str(tool.options["target_version"])])
121 # Fix options
122 if fix:
123 cmd.append("--fix")
124 if tool.options.get("unsafe_fixes"):
125 cmd.append("--unsafe-fixes")
126 if tool.options.get("show_fixes"):
127 cmd.append("--show-fixes")
128 if tool.options.get("fix_only"):
129 cmd.append("--fix-only")
131 # Output format
132 cmd.extend(["--output-format", RUFF_OUTPUT_FORMAT])
134 # Add files
135 cmd.extend(files)
137 return cmd
140def build_ruff_format_command(
141 tool: "RuffPlugin",
142 files: list[str],
143 check_only: bool = False,
144) -> list[str]:
145 """Build the ruff format command.
147 Args:
148 tool: RuffTool instance
149 files: list[str]: List of files to format.
150 check_only: bool: Whether to only check formatting without applying changes.
152 Returns:
153 list[str]: List of command arguments.
154 """
155 cmd: list[str] = tool._get_executable_command(tool_name="ruff") + ["format"]
157 if check_only:
158 cmd.append("--check")
160 # Add Lintro config injection args (--isolated, --config)
161 config_args = tool._build_config_args()
162 if config_args:
163 cmd.extend(config_args)
164 else:
165 # Fallback to options-based configuration
166 if tool.options.get("line_length"):
167 cmd.extend(["--line-length", str(tool.options["line_length"])])
168 if tool.options.get("target_version"):
169 cmd.extend(["--target-version", str(tool.options["target_version"])])
171 # Add files
172 cmd.extend(files)
174 return cmd