@@ -5,12 +5,14 @@ use log::debug;
55use path_absolutize:: path_dedot;
66
77use ruff_workspace:: configuration:: Configuration ;
8- use ruff_workspace:: pyproject;
8+ use ruff_workspace:: pyproject:: { self , find_fallback_target_version } ;
99use 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+
1416use 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