@@ -5,12 +5,14 @@ use log::debug;
5
5
use path_absolutize:: path_dedot;
6
6
7
7
use ruff_workspace:: configuration:: Configuration ;
8
- use ruff_workspace:: pyproject;
8
+ use ruff_workspace:: pyproject:: { self , find_fallback_target_version } ;
9
9
use ruff_workspace:: resolver:: {
10
- resolve_root_settings, ConfigurationTransformer , PyprojectConfig , PyprojectDiscoveryStrategy ,
11
- Relativity ,
10
+ resolve_root_settings, ConfigurationOrigin , ConfigurationTransformer , PyprojectConfig ,
11
+ PyprojectDiscoveryStrategy ,
12
12
} ;
13
13
14
+ use ruff_python_ast as ast;
15
+
14
16
use crate :: args:: ConfigArguments ;
15
17
16
18
/// Resolve the relevant settings strategy and defaults for the current
@@ -35,7 +37,11 @@ pub fn resolve(
35
37
// `pyproject.toml` for _all_ configuration, and resolve paths relative to the
36
38
// current working directory. (This matches ESLint's behavior.)
37
39
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
+ ) ?;
39
45
debug ! (
40
46
"Using user-specified configuration file at: {}" ,
41
47
pyproject. display( )
@@ -61,7 +67,8 @@ pub fn resolve(
61
67
"Using configuration file (via parent) at: {}" ,
62
68
pyproject. display( )
63
69
) ;
64
- let settings = resolve_root_settings ( & pyproject, Relativity :: Parent , config_arguments) ?;
70
+ let settings =
71
+ resolve_root_settings ( & pyproject, config_arguments, ConfigurationOrigin :: Ancestor ) ?;
65
72
return Ok ( PyprojectConfig :: new (
66
73
PyprojectDiscoveryStrategy :: Hierarchical ,
67
74
settings,
@@ -74,11 +81,35 @@ pub fn resolve(
74
81
// end up the "closest" `pyproject.toml` file for every Python file later on, so
75
82
// these act as the "default" settings.)
76
83
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
+
77
102
debug ! (
78
103
"Using configuration file (via cwd) at: {}" ,
79
104
pyproject. display( )
80
105
) ;
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
+ ) ?;
82
113
return Ok ( PyprojectConfig :: new (
83
114
PyprojectDiscoveryStrategy :: Hierarchical ,
84
115
settings,
@@ -91,7 +122,24 @@ pub fn resolve(
91
122
// "closest" `pyproject.toml` file for every Python file later on, so these act
92
123
// as the "default" settings.)
93
124
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
+ }
95
143
let settings = config. into_settings ( & path_dedot:: CWD ) ?;
96
144
Ok ( PyprojectConfig :: new (
97
145
PyprojectDiscoveryStrategy :: Hierarchical ,
0 commit comments