1
+ use std:: ffi:: OsStr ;
1
2
use std:: path:: { Path , PathBuf } ;
2
3
3
4
use anyhow:: { Context , Result } ;
4
5
use console:: Term ;
5
6
6
7
use uv_fs:: { Simplified , CWD } ;
7
8
use uv_requirements_txt:: RequirementsTxtRequirement ;
8
- use uv_warnings:: warn_user;
9
9
10
10
#[ derive( Debug , Clone ) ]
11
11
pub enum RequirementsSource {
@@ -32,89 +32,127 @@ pub enum RequirementsSource {
32
32
impl RequirementsSource {
33
33
/// Parse a [`RequirementsSource`] from a [`PathBuf`]. The file type is determined by the file
34
34
/// extension.
35
- pub fn from_requirements_file ( path : PathBuf ) -> Self {
35
+ pub fn from_requirements_file ( path : PathBuf ) -> Result < Self > {
36
36
if path. ends_with ( "pyproject.toml" ) {
37
- Self :: PyprojectToml ( path)
37
+ Ok ( Self :: PyprojectToml ( path) )
38
38
} else if path. ends_with ( "setup.py" ) {
39
- Self :: SetupPy ( path)
39
+ Ok ( Self :: SetupPy ( path) )
40
40
} else if path. ends_with ( "setup.cfg" ) {
41
- Self :: SetupCfg ( path)
41
+ Ok ( Self :: SetupCfg ( path) )
42
42
} else if path. ends_with ( "environment.yml" ) {
43
- Self :: EnvironmentYml ( path)
43
+ Ok ( Self :: EnvironmentYml ( path) )
44
44
} else if path
45
45
. file_name ( )
46
46
. is_some_and ( |file_name| file_name. to_str ( ) . is_some_and ( is_pylock_toml) )
47
47
{
48
- Self :: PylockToml ( path)
48
+ Ok ( Self :: PylockToml ( path) )
49
+ } else if path
50
+ . extension ( )
51
+ . is_some_and ( |ext| ext. eq_ignore_ascii_case ( "toml" ) )
52
+ {
53
+ Err ( anyhow:: anyhow!(
54
+ "`{}` is not a valid PEP 751 filename: expected TOML file to start with `pylock.` and end with `.toml` (e.g., `pylock.toml`, `pylock.dev.toml`)" ,
55
+ path. user_display( ) ,
56
+ ) )
49
57
} else {
50
- Self :: RequirementsTxt ( path)
58
+ Ok ( Self :: RequirementsTxt ( path) )
51
59
}
52
60
}
53
61
54
62
/// Parse a [`RequirementsSource`] from a `requirements.txt` file.
55
- pub fn from_requirements_txt ( path : PathBuf ) -> Self {
63
+ pub fn from_requirements_txt ( path : PathBuf ) -> Result < Self > {
56
64
for file_name in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
57
65
if path. ends_with ( file_name) {
58
- warn_user ! (
59
- "The file `{}` appears to be a `{}` file, but requirements must be specified in `requirements.txt` format. " ,
66
+ return Err ( anyhow :: anyhow !(
67
+ "The file `{}` appears to be a `{}` file, but requirements must be specified in `requirements.txt` format" ,
60
68
path. user_display( ) ,
61
69
file_name
62
- ) ;
70
+ ) ) ;
63
71
}
64
72
}
65
- if let Some ( file_name) = path. file_name ( ) {
66
- if file_name. to_str ( ) . is_some_and ( is_pylock_toml) {
67
- warn_user ! (
68
- "The file `{}` appears to be a `pylock.toml` file, but requirements must be specified in `requirements.txt` format." ,
69
- path. user_display( ) ,
70
- ) ;
71
- }
73
+ if path
74
+ . file_name ( )
75
+ . and_then ( OsStr :: to_str)
76
+ . is_some_and ( is_pylock_toml)
77
+ {
78
+ return Err ( anyhow:: anyhow!(
79
+ "The file `{}` appears to be a `pylock.toml` file, but requirements must be specified in `requirements.txt` format" ,
80
+ path. user_display( ) ,
81
+ ) ) ;
82
+ } else if path
83
+ . extension ( )
84
+ . is_some_and ( |ext| ext. eq_ignore_ascii_case ( "toml" ) )
85
+ {
86
+ return Err ( anyhow:: anyhow!(
87
+ "The file `{}` appears to be a TOML file, but requirements must be specified in `requirements.txt` format" ,
88
+ path. user_display( ) ,
89
+ ) ) ;
72
90
}
73
- Self :: RequirementsTxt ( path)
91
+ Ok ( Self :: RequirementsTxt ( path) )
74
92
}
75
93
76
94
/// Parse a [`RequirementsSource`] from a `constraints.txt` file.
77
- pub fn from_constraints_txt ( path : PathBuf ) -> Self {
95
+ pub fn from_constraints_txt ( path : PathBuf ) -> Result < Self > {
78
96
for file_name in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
79
97
if path. ends_with ( file_name) {
80
- warn_user ! (
81
- "The file `{}` appears to be a `{}` file, but constraints must be specified in `requirements.txt` format. " ,
98
+ return Err ( anyhow :: anyhow !(
99
+ "The file `{}` appears to be a `{}` file, but constraints must be specified in `requirements.txt` format" ,
82
100
path. user_display( ) ,
83
101
file_name
84
- ) ;
102
+ ) ) ;
85
103
}
86
104
}
87
- if let Some ( file_name) = path. file_name ( ) {
88
- if file_name. to_str ( ) . is_some_and ( is_pylock_toml) {
89
- warn_user ! (
90
- "The file `{}` appears to be a `pylock.toml` file, but constraints must be specified in `requirements.txt` format." ,
91
- path. user_display( ) ,
92
- ) ;
93
- }
105
+ if path
106
+ . file_name ( )
107
+ . and_then ( OsStr :: to_str)
108
+ . is_some_and ( is_pylock_toml)
109
+ {
110
+ return Err ( anyhow:: anyhow!(
111
+ "The file `{}` appears to be a `pylock.toml` file, but constraints must be specified in `requirements.txt` format" ,
112
+ path. user_display( ) ,
113
+ ) ) ;
114
+ } else if path
115
+ . extension ( )
116
+ . is_some_and ( |ext| ext. eq_ignore_ascii_case ( "toml" ) )
117
+ {
118
+ return Err ( anyhow:: anyhow!(
119
+ "The file `{}` appears to be a TOML file, but constraints must be specified in `requirements.txt` format" ,
120
+ path. user_display( ) ,
121
+ ) ) ;
94
122
}
95
- Self :: RequirementsTxt ( path)
123
+ Ok ( Self :: RequirementsTxt ( path) )
96
124
}
97
125
98
126
/// Parse a [`RequirementsSource`] from an `overrides.txt` file.
99
- pub fn from_overrides_txt ( path : PathBuf ) -> Self {
127
+ pub fn from_overrides_txt ( path : PathBuf ) -> Result < Self > {
100
128
for file_name in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
101
129
if path. ends_with ( file_name) {
102
- warn_user ! (
103
- "The file `{}` appears to be a `{}` file, but overrides must be specified in `requirements.txt` format. " ,
130
+ return Err ( anyhow :: anyhow !(
131
+ "The file `{}` appears to be a `{}` file, but overrides must be specified in `requirements.txt` format" ,
104
132
path. user_display( ) ,
105
133
file_name
106
- ) ;
134
+ ) ) ;
107
135
}
108
136
}
109
- if let Some ( file_name) = path. file_name ( ) {
110
- if file_name. to_str ( ) . is_some_and ( is_pylock_toml) {
111
- warn_user ! (
112
- "The file `{}` appears to be a `pylock.toml` file, but overrides must be specified in `requirements.txt` format." ,
113
- path. user_display( ) ,
114
- ) ;
115
- }
137
+ if path
138
+ . file_name ( )
139
+ . and_then ( OsStr :: to_str)
140
+ . is_some_and ( is_pylock_toml)
141
+ {
142
+ return Err ( anyhow:: anyhow!(
143
+ "The file `{}` appears to be a `pylock.toml` file, but overrides must be specified in `requirements.txt` format" ,
144
+ path. user_display( ) ,
145
+ ) ) ;
146
+ } else if path
147
+ . extension ( )
148
+ . is_some_and ( |ext| ext. eq_ignore_ascii_case ( "toml" ) )
149
+ {
150
+ return Err ( anyhow:: anyhow!(
151
+ "The file `{}` appears to be a TOML file, but overrides must be specified in `requirements.txt` format" ,
152
+ path. user_display( ) ,
153
+ ) ) ;
116
154
}
117
- Self :: RequirementsTxt ( path)
155
+ Ok ( Self :: RequirementsTxt ( path) )
118
156
}
119
157
120
158
/// Parse a [`RequirementsSource`] from a user-provided string, assumed to be a positional
@@ -134,7 +172,7 @@ impl RequirementsSource {
134
172
) ;
135
173
let confirmation = uv_console:: confirm ( & prompt, & term, true ) ?;
136
174
if confirmation {
137
- return Ok ( Self :: from_requirements_file ( name. into ( ) ) ) ;
175
+ return Self :: from_requirements_file ( name. into ( ) ) ;
138
176
}
139
177
}
140
178
}
@@ -154,7 +192,7 @@ impl RequirementsSource {
154
192
) ;
155
193
let confirmation = uv_console:: confirm ( & prompt, & term, true ) ?;
156
194
if confirmation {
157
- return Ok ( Self :: from_requirements_file ( name. into ( ) ) ) ;
195
+ return Self :: from_requirements_file ( name. into ( ) ) ;
158
196
}
159
197
}
160
198
}
@@ -182,7 +220,7 @@ impl RequirementsSource {
182
220
) ;
183
221
let confirmation = uv_console:: confirm ( & prompt, & term, true ) ?;
184
222
if confirmation {
185
- return Ok ( Self :: from_requirements_file ( name. into ( ) ) ) ;
223
+ return Self :: from_requirements_file ( name. into ( ) ) ;
186
224
}
187
225
}
188
226
}
@@ -202,7 +240,7 @@ impl RequirementsSource {
202
240
) ;
203
241
let confirmation = uv_console:: confirm ( & prompt, & term, true ) ?;
204
242
if confirmation {
205
- return Ok ( Self :: from_requirements_file ( name. into ( ) ) ) ;
243
+ return Self :: from_requirements_file ( name. into ( ) ) ;
206
244
}
207
245
}
208
246
}
0 commit comments