Skip to content

Commit 5955650

Browse files
MichaReiserdylwil3
andauthored
Fallback to requires-python in certain cases when target-version is not found (#16721)
## Summary Restores #16319 after it got dropped from the 0.10 release branch :( --------- Co-authored-by: dylwil3 <[email protected]>
1 parent 2382fe1 commit 5955650

File tree

10 files changed

+2914
-65
lines changed

10 files changed

+2914
-65
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ toml = { version = "0.8.11" }
154154
tracing = { version = "0.1.40" }
155155
tracing-flame = { version = "0.2.0" }
156156
tracing-indicatif = { version = "0.3.6" }
157+
tracing-log = { version = "0.2.0" }
157158
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
158159
"env-filter",
159160
"fmt",

crates/ruff/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ pub fn run(
153153
}));
154154
}
155155

156-
set_up_logging(global_options.log_level())?;
156+
// Don't set up logging for the server command, as it has its own logging setup
157+
// and setting the global logger can only be done once.
158+
if !matches!(command, Command::Server { .. }) {
159+
set_up_logging(global_options.log_level())?;
160+
}
157161

158162
match command {
159163
Command::Version { output_format } => {

crates/ruff/src/resolve.rs

+55-7
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ use log::debug;
55
use path_absolutize::path_dedot;
66

77
use ruff_workspace::configuration::Configuration;
8-
use ruff_workspace::pyproject;
8+
use ruff_workspace::pyproject::{self, find_fallback_target_version};
99
use ruff_workspace::resolver::{
10-
resolve_root_settings, ConfigurationTransformer, PyprojectConfig, PyprojectDiscoveryStrategy,
11-
Relativity,
10+
resolve_root_settings, ConfigurationOrigin, ConfigurationTransformer, PyprojectConfig,
11+
PyprojectDiscoveryStrategy,
1212
};
1313

14+
use ruff_python_ast as ast;
15+
1416
use crate::args::ConfigArguments;
1517

1618
/// Resolve the relevant settings strategy and defaults for the current
@@ -35,7 +37,11 @@ pub fn resolve(
3537
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
3638
// current working directory. (This matches ESLint's behavior.)
3739
if let Some(pyproject) = config_arguments.config_file() {
38-
let settings = resolve_root_settings(pyproject, Relativity::Cwd, config_arguments)?;
40+
let settings = resolve_root_settings(
41+
pyproject,
42+
config_arguments,
43+
ConfigurationOrigin::UserSpecified,
44+
)?;
3945
debug!(
4046
"Using user-specified configuration file at: {}",
4147
pyproject.display()
@@ -61,7 +67,8 @@ pub fn resolve(
6167
"Using configuration file (via parent) at: {}",
6268
pyproject.display()
6369
);
64-
let settings = resolve_root_settings(&pyproject, Relativity::Parent, config_arguments)?;
70+
let settings =
71+
resolve_root_settings(&pyproject, config_arguments, ConfigurationOrigin::Ancestor)?;
6572
return Ok(PyprojectConfig::new(
6673
PyprojectDiscoveryStrategy::Hierarchical,
6774
settings,
@@ -74,11 +81,35 @@ pub fn resolve(
7481
// end up the "closest" `pyproject.toml` file for every Python file later on, so
7582
// these act as the "default" settings.)
7683
if let Some(pyproject) = pyproject::find_user_settings_toml() {
84+
struct FallbackTransformer<'a> {
85+
arguments: &'a ConfigArguments,
86+
}
87+
88+
impl ConfigurationTransformer for FallbackTransformer<'_> {
89+
fn transform(&self, mut configuration: Configuration) -> Configuration {
90+
// The `requires-python` constraint from the `pyproject.toml` takes precedence
91+
// over the `target-version` from the user configuration.
92+
let fallback = find_fallback_target_version(&*path_dedot::CWD);
93+
if let Some(fallback) = fallback {
94+
debug!("Derived `target-version` from found `requires-python`: {fallback:?}");
95+
configuration.target_version = Some(fallback.into());
96+
}
97+
98+
self.arguments.transform(configuration)
99+
}
100+
}
101+
77102
debug!(
78103
"Using configuration file (via cwd) at: {}",
79104
pyproject.display()
80105
);
81-
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, config_arguments)?;
106+
let settings = resolve_root_settings(
107+
&pyproject,
108+
&FallbackTransformer {
109+
arguments: config_arguments,
110+
},
111+
ConfigurationOrigin::UserSettings,
112+
)?;
82113
return Ok(PyprojectConfig::new(
83114
PyprojectDiscoveryStrategy::Hierarchical,
84115
settings,
@@ -91,7 +122,24 @@ pub fn resolve(
91122
// "closest" `pyproject.toml` file for every Python file later on, so these act
92123
// as the "default" settings.)
93124
debug!("Using Ruff default settings");
94-
let config = config_arguments.transform(Configuration::default());
125+
let mut config = config_arguments.transform(Configuration::default());
126+
if config.target_version.is_none() {
127+
// If we have arrived here we know that there was no `pyproject.toml`
128+
// containing a `[tool.ruff]` section found in an ancestral directory.
129+
// (This is an implicit requirement in the function
130+
// `pyproject::find_settings_toml`.)
131+
// However, there may be a `pyproject.toml` with a `requires-python`
132+
// specified, and that is what we look for in this step.
133+
let fallback = find_fallback_target_version(
134+
stdin_filename
135+
.as_ref()
136+
.unwrap_or(&path_dedot::CWD.as_path()),
137+
);
138+
if let Some(version) = fallback {
139+
debug!("Derived `target-version` from found `requires-python`: {version:?}");
140+
}
141+
config.target_version = fallback.map(ast::PythonVersion::from);
142+
}
95143
let settings = config.into_settings(&path_dedot::CWD)?;
96144
Ok(PyprojectConfig::new(
97145
PyprojectDiscoveryStrategy::Hierarchical,

0 commit comments

Comments
 (0)