Fallback to requires-python in certain cases when target-version is not found (#16721)

## Summary

Restores https://github.com/astral-sh/ruff/pull/16319 after it got
dropped from the 0.10 release branch :(

---------

Co-authored-by: dylwil3 <dylwil3@gmail.com>
This commit is contained in:
Micha Reiser 2025-03-14 09:36:51 +01:00 committed by GitHub
parent 2382fe1f25
commit 595565015b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 2915 additions and 66 deletions

View file

@ -9,12 +9,13 @@ use ignore::{WalkBuilder, WalkState};
use ruff_linter::settings::types::GlobPath;
use ruff_linter::{settings::types::FilePattern, settings::types::PreviewMode};
use ruff_workspace::pyproject::find_fallback_target_version;
use ruff_workspace::resolver::match_exclusion;
use ruff_workspace::Settings;
use ruff_workspace::{
configuration::{Configuration, FormatConfiguration, LintConfiguration, RuleSelection},
pyproject::{find_user_settings_toml, settings_toml},
resolver::{ConfigurationTransformer, Relativity},
resolver::ConfigurationTransformer,
};
use crate::session::settings::{
@ -64,12 +65,36 @@ impl RuffSettings {
/// In the absence of a valid configuration file, it gracefully falls back to
/// editor-only settings.
pub(crate) fn fallback(editor_settings: &ResolvedEditorSettings, root: &Path) -> RuffSettings {
struct FallbackTransformer<'a> {
inner: EditorConfigurationTransformer<'a>,
}
impl ConfigurationTransformer for FallbackTransformer<'_> {
fn transform(&self, mut configuration: Configuration) -> Configuration {
let fallback = find_fallback_target_version(self.inner.1);
if let Some(fallback) = fallback {
tracing::debug!(
"Derived `target-version` from found `requires-python`: {fallback:?}"
);
configuration.target_version = Some(fallback.into());
}
self.inner.transform(configuration)
}
}
find_user_settings_toml()
.and_then(|user_settings| {
tracing::debug!(
"Loading settings from user configuration file: `{}`",
user_settings.display()
);
ruff_workspace::resolver::resolve_root_settings(
&user_settings,
Relativity::Cwd,
&EditorConfigurationTransformer(editor_settings, root),
&FallbackTransformer {
inner: EditorConfigurationTransformer(editor_settings, root),
},
ruff_workspace::resolver::ConfigurationOrigin::UserSettings,
)
.ok()
.map(|settings| RuffSettings {
@ -77,21 +102,45 @@ impl RuffSettings {
settings,
})
})
.unwrap_or_else(|| Self::editor_only(editor_settings, root))
.unwrap_or_else(|| {
let fallback = find_fallback_target_version(root);
if let Some(fallback) = fallback {
tracing::debug!(
"Derived `target-version` from found `requires-python` for fallback configuration: {fallback:?}"
);
}
let configuration = Configuration {
target_version: fallback.map(Into::into),
..Configuration::default()
};
Self::with_editor_settings(editor_settings, root, configuration).expect(
"editor configuration should merge successfully with default configuration",
)
})
}
/// Constructs [`RuffSettings`] by merging the editor-defined settings with the
/// default configuration.
fn editor_only(editor_settings: &ResolvedEditorSettings, root: &Path) -> RuffSettings {
let settings = EditorConfigurationTransformer(editor_settings, root)
.transform(Configuration::default())
.into_settings(root)
.expect("editor configuration should merge successfully with default configuration");
Self::with_editor_settings(editor_settings, root, Configuration::default())
.expect("editor configuration should merge successfully with default configuration")
}
RuffSettings {
/// Merges the `configuration` with the editor defined settings.
fn with_editor_settings(
editor_settings: &ResolvedEditorSettings,
root: &Path,
configuration: Configuration,
) -> anyhow::Result<RuffSettings> {
let settings = EditorConfigurationTransformer(editor_settings, root)
.transform(configuration)
.into_settings(root)?;
Ok(RuffSettings {
path: None,
settings,
}
})
}
}
@ -140,10 +189,11 @@ impl RuffSettingsIndex {
Ok(Some(pyproject)) => {
match ruff_workspace::resolver::resolve_root_settings(
&pyproject,
Relativity::Parent,
&EditorConfigurationTransformer(editor_settings, root),
ruff_workspace::resolver::ConfigurationOrigin::Ancestor,
) {
Ok(settings) => {
tracing::debug!("Loaded settings from: `{}`", pyproject.display());
respect_gitignore = Some(settings.file_resolver.respect_gitignore);
index.insert(
@ -264,10 +314,15 @@ impl RuffSettingsIndex {
Ok(Some(pyproject)) => {
match ruff_workspace::resolver::resolve_root_settings(
&pyproject,
Relativity::Parent,
&EditorConfigurationTransformer(editor_settings, root),
ruff_workspace::resolver::ConfigurationOrigin::Ancestor,
) {
Ok(settings) => {
tracing::debug!(
"Loaded settings from: `{}` for `{}`",
pyproject.display(),
directory.display()
);
index.write().unwrap().insert(
directory,
Arc::new(RuffSettings {
@ -437,8 +492,8 @@ impl ConfigurationTransformer for EditorConfigurationTransformer<'_> {
fn open_configuration_file(config_path: &Path) -> crate::Result<Configuration> {
ruff_workspace::resolver::resolve_configuration(
config_path,
Relativity::Cwd,
&IdentityTransformer,
ruff_workspace::resolver::ConfigurationOrigin::UserSpecified,
)
}