@@ -253,25 +253,40 @@ fn parse_index_url(input: &str) -> Result<Maybe<IndexUrl>, String> {
253
253
}
254
254
}
255
255
256
- /// Parse a string into a [`PathBuf`], mapping the empty string to `None`.
257
- fn parse_file_path ( input : & str ) -> Result < Maybe < PathBuf > , String > {
258
- if input. is_empty ( ) {
259
- Ok ( Maybe :: None )
256
+ /// Parse a string into a [`PathBuf`]. The string can represent a file, either as a path or a
257
+ /// `file://` URL.
258
+ fn parse_file_path ( input : & str ) -> Result < PathBuf , String > {
259
+ if input. starts_with ( "file://" ) {
260
+ let url = match url:: Url :: from_str ( input) {
261
+ Ok ( url) => url,
262
+ Err ( err) => return Err ( err. to_string ( ) ) ,
263
+ } ;
264
+ url. to_file_path ( )
265
+ . map_err ( |( ) | "invalid file URL" . to_string ( ) )
260
266
} else {
261
267
match PathBuf :: from_str ( input) {
262
- Ok ( path) => Ok ( Maybe :: Some ( path) ) ,
268
+ Ok ( path) => Ok ( path) ,
263
269
Err ( err) => Err ( err. to_string ( ) ) ,
264
270
}
265
271
}
266
272
}
267
273
274
+ /// Parse a string into a [`PathBuf`], mapping the empty string to `None`.
275
+ fn parse_maybe_file_path ( input : & str ) -> Result < Maybe < PathBuf > , String > {
276
+ if input. is_empty ( ) {
277
+ Ok ( Maybe :: None )
278
+ } else {
279
+ parse_file_path ( input) . map ( Maybe :: Some )
280
+ }
281
+ }
282
+
268
283
#[ derive( Args ) ]
269
284
#[ allow( clippy:: struct_excessive_bools) ]
270
285
pub ( crate ) struct PipCompileArgs {
271
286
/// Include all packages listed in the given `requirements.in` files.
272
287
///
273
288
/// When the path is `-`, then requirements are read from stdin.
274
- #[ arg( required( true ) ) ]
289
+ #[ arg( required( true ) , value_parser = parse_file_path ) ]
275
290
pub ( crate ) src_file : Vec < PathBuf > ,
276
291
277
292
/// Constrain versions using the given requirements files.
@@ -281,7 +296,7 @@ pub(crate) struct PipCompileArgs {
281
296
/// trigger the installation of that package.
282
297
///
283
298
/// This is equivalent to pip's `--constraint` option.
284
- #[ arg( long, short, env = "UV_CONSTRAINT" , value_delimiter = ' ' , value_parser = parse_file_path ) ]
299
+ #[ arg( long, short, env = "UV_CONSTRAINT" , value_delimiter = ' ' , value_parser = parse_maybe_file_path ) ]
285
300
pub ( crate ) constraint : Vec < Maybe < PathBuf > > ,
286
301
287
302
/// Override versions using the given requirements files.
@@ -293,7 +308,7 @@ pub(crate) struct PipCompileArgs {
293
308
/// While constraints are _additive_, in that they're combined with the requirements of the
294
309
/// constituent packages, overrides are _absolute_, in that they completely replace the
295
310
/// requirements of the constituent packages.
296
- #[ arg( long) ]
311
+ #[ arg( long, value_parser = parse_file_path ) ]
297
312
pub ( crate ) r#override : Vec < PathBuf > ,
298
313
299
314
/// Include optional dependencies in the given extra group name; may be provided more than once.
@@ -593,7 +608,7 @@ pub(crate) struct PipCompileArgs {
593
608
#[ allow( clippy:: struct_excessive_bools) ]
594
609
pub ( crate ) struct PipSyncArgs {
595
610
/// Include all packages listed in the given `requirements.txt` files.
596
- #[ arg( required( true ) ) ]
611
+ #[ arg( required( true ) , value_parser = parse_file_path ) ]
597
612
pub ( crate ) src_file : Vec < PathBuf > ,
598
613
599
614
/// Constrain versions using the given requirements files.
@@ -603,7 +618,7 @@ pub(crate) struct PipSyncArgs {
603
618
/// trigger the installation of that package.
604
619
///
605
620
/// This is equivalent to pip's `--constraint` option.
606
- #[ arg( long, short, env = "UV_CONSTRAINT" , value_delimiter = ' ' , value_parser = parse_file_path ) ]
621
+ #[ arg( long, short, env = "UV_CONSTRAINT" , value_delimiter = ' ' , value_parser = parse_maybe_file_path ) ]
607
622
pub ( crate ) constraint : Vec < Maybe < PathBuf > > ,
608
623
609
624
/// Reinstall all packages, regardless of whether they're already installed.
@@ -892,7 +907,7 @@ pub(crate) struct PipInstallArgs {
892
907
pub ( crate ) package : Vec < String > ,
893
908
894
909
/// Install all packages listed in the given requirements files.
895
- #[ arg( long, short, group = "sources" ) ]
910
+ #[ arg( long, short, group = "sources" , value_parser = parse_file_path ) ]
896
911
pub ( crate ) requirement : Vec < PathBuf > ,
897
912
898
913
/// Install the editable package based on the provided local file path.
@@ -906,7 +921,7 @@ pub(crate) struct PipInstallArgs {
906
921
/// trigger the installation of that package.
907
922
///
908
923
/// This is equivalent to pip's `--constraint` option.
909
- #[ arg( long, short, env = "UV_CONSTRAINT" , value_delimiter = ' ' , value_parser = parse_file_path ) ]
924
+ #[ arg( long, short, env = "UV_CONSTRAINT" , value_delimiter = ' ' , value_parser = parse_maybe_file_path ) ]
910
925
pub ( crate ) constraint : Vec < Maybe < PathBuf > > ,
911
926
912
927
/// Override versions using the given requirements files.
@@ -918,7 +933,7 @@ pub(crate) struct PipInstallArgs {
918
933
/// While constraints are _additive_, in that they're combined with the requirements of the
919
934
/// constituent packages, overrides are _absolute_, in that they completely replace the
920
935
/// requirements of the constituent packages.
921
- #[ arg( long) ]
936
+ #[ arg( long, value_parser = parse_file_path ) ]
922
937
pub ( crate ) r#override : Vec < PathBuf > ,
923
938
924
939
/// Include optional dependencies in the given extra group name; may be provided more than once.
@@ -1259,7 +1274,7 @@ pub(crate) struct PipUninstallArgs {
1259
1274
pub ( crate ) package : Vec < String > ,
1260
1275
1261
1276
/// Uninstall all packages listed in the given requirements files.
1262
- #[ arg( long, short, group = "sources" ) ]
1277
+ #[ arg( long, short, group = "sources" , value_parser = parse_file_path ) ]
1263
1278
pub ( crate ) requirement : Vec < PathBuf > ,
1264
1279
1265
1280
/// The Python interpreter from which packages should be uninstalled.
0 commit comments