mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
Use ast::PythonVersion
internally in the formatter and linter (#16170)
## Summary This PR updates the formatter and linter to use the `PythonVersion` struct from the `ruff_python_ast` crate internally. While this doesn't remove the need for the `linter::PythonVersion` enum, it does remove the `formatter::PythonVersion` enum and limits the use in the linter to deserializing from CLI arguments and config files and moves most of the remaining methods to the `ast::PythonVersion` struct. ## Test Plan Existing tests, with some inputs and outputs updated to reflect the new (de)serialization format. I think these are test-specific and shouldn't affect any external (de)serialization. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
0868e73d2c
commit
a9efdea113
153 changed files with 456 additions and 539 deletions
|
@ -30,13 +30,14 @@ 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, PythonVersion, RequiredVersion, UnsafeFixes,
|
||||
PerFileIgnore, PreviewMode, RequiredVersion, UnsafeFixes,
|
||||
};
|
||||
use ruff_linter::settings::{LinterSettings, DEFAULT_SELECTORS, DUMMY_VARIABLE_RGX, TASK_TAGS};
|
||||
use ruff_linter::{
|
||||
fs, warn_user_once, warn_user_once_by_id, warn_user_once_by_message, RuleSelector,
|
||||
RUFF_PKG_VERSION,
|
||||
};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_formatter::{
|
||||
DocstringCode, DocstringCodeLineWidth, MagicTrailingComma, QuoteStyle,
|
||||
};
|
||||
|
@ -136,7 +137,7 @@ pub struct Configuration {
|
|||
pub builtins: Option<Vec<String>>,
|
||||
pub namespace_packages: Option<Vec<PathBuf>>,
|
||||
pub src: Option<Vec<PathBuf>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub target_version: Option<ast::PythonVersion>,
|
||||
|
||||
// Global formatting options
|
||||
pub line_length: Option<LineLength>,
|
||||
|
@ -177,15 +178,7 @@ impl Configuration {
|
|||
exclude: FilePatternSet::try_from_iter(format.exclude.unwrap_or_default())?,
|
||||
extension: self.extension.clone().unwrap_or_default(),
|
||||
preview: format_preview,
|
||||
target_version: match target_version {
|
||||
PythonVersion::Py37 => ruff_python_formatter::PythonVersion::Py37,
|
||||
PythonVersion::Py38 => ruff_python_formatter::PythonVersion::Py38,
|
||||
PythonVersion::Py39 => ruff_python_formatter::PythonVersion::Py39,
|
||||
PythonVersion::Py310 => ruff_python_formatter::PythonVersion::Py310,
|
||||
PythonVersion::Py311 => ruff_python_formatter::PythonVersion::Py311,
|
||||
PythonVersion::Py312 => ruff_python_formatter::PythonVersion::Py312,
|
||||
PythonVersion::Py313 => ruff_python_formatter::PythonVersion::Py313,
|
||||
},
|
||||
target_version,
|
||||
line_width: self
|
||||
.line_length
|
||||
.map_or(format_defaults.line_width, |length| {
|
||||
|
@ -539,7 +532,7 @@ impl Configuration {
|
|||
.src
|
||||
.map(|src| resolve_src(&src, project_root))
|
||||
.transpose()?,
|
||||
target_version: options.target_version,
|
||||
target_version: options.target_version.map(ast::PythonVersion::from),
|
||||
// `--extension` is a hidden command-line argument that isn't supported in configuration
|
||||
// files at present.
|
||||
extension: None,
|
||||
|
|
|
@ -3848,9 +3848,7 @@ impl From<LintOptionsWire> for LintOptions {
|
|||
mod tests {
|
||||
use crate::options::Flake8SelfOptions;
|
||||
use ruff_linter::rules::flake8_self;
|
||||
use ruff_linter::settings::types::PythonVersion as LinterPythonVersion;
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_formatter::PythonVersion as FormatterPythonVersion;
|
||||
|
||||
#[test]
|
||||
fn flake8_self_options() {
|
||||
|
@ -3898,28 +3896,4 @@ mod tests {
|
|||
vec![Name::new_static("_foo"), Name::new_static("_bar")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatter_and_linter_target_version_have_same_default() {
|
||||
assert_eq!(
|
||||
FormatterPythonVersion::default().as_tuple(),
|
||||
LinterPythonVersion::default().as_tuple()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatter_and_linter_target_version_have_same_latest() {
|
||||
assert_eq!(
|
||||
FormatterPythonVersion::latest().as_tuple(),
|
||||
LinterPythonVersion::latest().as_tuple()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatter_and_linter_target_version_have_same_minimal_supported() {
|
||||
assert_eq!(
|
||||
FormatterPythonVersion::minimal_supported().as_tuple(),
|
||||
LinterPythonVersion::minimal_supported().as_tuple()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use anyhow::{Context, Result};
|
||||
use log::debug;
|
||||
use pep440_rs::VersionSpecifiers;
|
||||
use pep440_rs::{Operator, Version, VersionSpecifiers};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_linter::settings::types::PythonVersion;
|
||||
|
||||
|
@ -150,8 +151,7 @@ pub(super) fn load_options<P: AsRef<Path>>(path: P) -> Result<Options> {
|
|||
if ruff.target_version.is_none() {
|
||||
if let Some(project) = pyproject.project {
|
||||
if let Some(requires_python) = project.requires_python {
|
||||
ruff.target_version =
|
||||
PythonVersion::get_minimum_supported_version(&requires_python);
|
||||
ruff.target_version = get_minimum_supported_version(&requires_python);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +167,38 @@ pub(super) fn load_options<P: AsRef<Path>>(path: P) -> Result<Options> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Infer the minimum supported [`PythonVersion`] from a `requires-python` specifier.
|
||||
fn get_minimum_supported_version(requires_version: &VersionSpecifiers) -> Option<PythonVersion> {
|
||||
/// Truncate a version to its major and minor components.
|
||||
fn major_minor(version: &Version) -> Option<Version> {
|
||||
let major = version.release().first()?;
|
||||
let minor = version.release().get(1)?;
|
||||
Some(Version::new([major, minor]))
|
||||
}
|
||||
|
||||
// Extract the minimum supported version from the specifiers.
|
||||
let minimum_version = requires_version
|
||||
.iter()
|
||||
.filter(|specifier| {
|
||||
matches!(
|
||||
specifier.operator(),
|
||||
Operator::Equal
|
||||
| Operator::EqualStar
|
||||
| Operator::ExactEqual
|
||||
| Operator::TildeEqual
|
||||
| Operator::GreaterThan
|
||||
| Operator::GreaterThanEqual
|
||||
)
|
||||
})
|
||||
.filter_map(|specifier| major_minor(specifier.version()))
|
||||
.min()?;
|
||||
|
||||
debug!("Detected minimum supported `requires-python` version: {minimum_version}");
|
||||
|
||||
// Find the Python version that matches the minimum supported version.
|
||||
PythonVersion::iter().find(|version| Version::from(*version) == minimum_version)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
|
|
|
@ -164,7 +164,7 @@ pub struct FormatterSettings {
|
|||
pub exclude: FilePatternSet,
|
||||
pub extension: ExtensionMapping,
|
||||
pub preview: PreviewMode,
|
||||
pub target_version: ruff_python_formatter::PythonVersion,
|
||||
pub target_version: ruff_python_ast::PythonVersion,
|
||||
|
||||
pub line_width: LineWidth,
|
||||
|
||||
|
@ -247,7 +247,7 @@ impl fmt::Display for FormatterSettings {
|
|||
namespace = "formatter",
|
||||
fields = [
|
||||
self.exclude,
|
||||
self.target_version | debug,
|
||||
self.target_version,
|
||||
self.preview,
|
||||
self.line_width,
|
||||
self.line_ending,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue