Add per-file-target-version option (#16257)

## Summary

This PR is another step in preparing to detect syntax errors in the
parser. It introduces the new `per-file-target-version` top-level
configuration option, which holds a mapping of compiled glob patterns to
Python versions. I intend to use the
`LinterSettings::resolve_target_version` method here to pass to the
parser:


f50849aeef/crates/ruff_linter/src/linter.rs (L491-L493)

## Test Plan

I added two new CLI tests to show that the `per-file-target-version` is
respected in both the formatter and the linter.
This commit is contained in:
Brent Westbrook 2025-02-24 08:47:13 -05:00 committed by GitHub
parent 42a5f5ef6a
commit e7a6c19e3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 820 additions and 274 deletions

View file

@ -9,7 +9,7 @@ use std::num::{NonZeroU16, NonZeroU8};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use anyhow::{anyhow, Result};
use anyhow::{anyhow, Context, Result};
use glob::{glob, GlobError, Paths, PatternError};
use itertools::Itertools;
use regex::Regex;
@ -29,8 +29,9 @@ use ruff_linter::rules::{flake8_import_conventions, isort, pycodestyle};
use ruff_linter::settings::fix_safety_table::FixSafetyTable;
use ruff_linter::settings::rule_table::RuleTable;
use ruff_linter::settings::types::{
CompiledPerFileIgnoreList, ExtensionMapping, FilePattern, FilePatternSet, OutputFormat,
PerFileIgnore, PreviewMode, RequiredVersion, UnsafeFixes,
CompiledPerFileIgnoreList, CompiledPerFileTargetVersionList, ExtensionMapping, FilePattern,
FilePatternSet, OutputFormat, PerFileIgnore, PerFileTargetVersion, PreviewMode,
RequiredVersion, UnsafeFixes,
};
use ruff_linter::settings::{LinterSettings, DEFAULT_SELECTORS, DUMMY_VARIABLE_RGX, TASK_TAGS};
use ruff_linter::{
@ -138,6 +139,7 @@ pub struct Configuration {
pub namespace_packages: Option<Vec<PathBuf>>,
pub src: Option<Vec<PathBuf>>,
pub target_version: Option<ast::PythonVersion>,
pub per_file_target_version: Option<Vec<PerFileTargetVersion>>,
// Global formatting options
pub line_length: Option<LineLength>,
@ -174,11 +176,17 @@ impl Configuration {
PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled,
};
let per_file_target_version = CompiledPerFileTargetVersionList::resolve(
self.per_file_target_version.unwrap_or_default(),
)
.context("failed to resolve `per-file-target-version` table")?;
let formatter = FormatterSettings {
exclude: FilePatternSet::try_from_iter(format.exclude.unwrap_or_default())?,
extension: self.extension.clone().unwrap_or_default(),
preview: format_preview,
target_version,
unresolved_target_version: target_version,
per_file_target_version: per_file_target_version.clone(),
line_width: self
.line_length
.map_or(format_defaults.line_width, |length| {
@ -278,7 +286,8 @@ impl Configuration {
exclude: FilePatternSet::try_from_iter(lint.exclude.unwrap_or_default())?,
extension: self.extension.unwrap_or_default(),
preview: lint_preview,
target_version,
unresolved_target_version: target_version,
per_file_target_version,
project_root: project_root.to_path_buf(),
allowed_confusables: lint
.allowed_confusables
@ -533,6 +542,18 @@ impl Configuration {
.map(|src| resolve_src(&src, project_root))
.transpose()?,
target_version: options.target_version.map(ast::PythonVersion::from),
per_file_target_version: options.per_file_target_version.map(|versions| {
versions
.into_iter()
.map(|(pattern, version)| {
PerFileTargetVersion::new(
pattern,
ast::PythonVersion::from(version),
Some(project_root),
)
})
.collect()
}),
// `--extension` is a hidden command-line argument that isn't supported in configuration
// files at present.
extension: None,
@ -580,6 +601,9 @@ impl Configuration {
show_fixes: self.show_fixes.or(config.show_fixes),
src: self.src.or(config.src),
target_version: self.target_version.or(config.target_version),
per_file_target_version: self
.per_file_target_version
.or(config.per_file_target_version),
preview: self.preview.or(config.preview),
extension: self.extension.or(config.extension),