@@ -12,7 +12,9 @@ use toml_edit::{value, Array, ArrayOfTables, Item, Table};
1212use url:: Url ;
1313
1414use uv_cache_key:: RepositoryUrl ;
15- use uv_configuration:: { DependencyGroupsWithDefaults , ExtrasSpecification , InstallOptions } ;
15+ use uv_configuration:: {
16+ BuildOptions , DependencyGroupsWithDefaults , ExtrasSpecification , InstallOptions ,
17+ } ;
1618use uv_distribution_filename:: {
1719 BuildTag , DistExtension , ExtensionError , SourceDistExtension , SourceDistFilename ,
1820 SourceDistFilenameError , WheelFilename , WheelFilenameError ,
@@ -80,6 +82,18 @@ pub enum PylockTomlError {
8082 PathToUrl ,
8183 #[ error( "Failed to convert URL to path" ) ]
8284 UrlToPath ,
85+ #[ error( "Package `{0}` can't be installed because it doesn't have a source distribution or wheel for the current platform" ) ]
86+ NeitherSourceDistNorWheel ( PackageName ) ,
87+ #[ error( "Package `{0}` can't be installed because it is marked as both `--no-binary` and `--no-build`" ) ]
88+ NoBinaryNoBuild ( PackageName ) ,
89+ #[ error( "Package `{0}` can't be installed because it is marked as `--no-binary` but has no source distribution" ) ]
90+ NoBinary ( PackageName ) ,
91+ #[ error( "Package `{0}` can't be installed because it is marked as `--no-build` but has no binary distribution" ) ]
92+ NoBuild ( PackageName ) ,
93+ #[ error( "Package `{0}` can't be installed because the binary distribution is incompatible with the current platform" ) ]
94+ IncompatibleWheelOnly ( PackageName ) ,
95+ #[ error( "Package `{0}` can't be installed because it is marked as `--no-binary` but is itself a binary distribution" ) ]
96+ NoBinaryWheelOnly ( PackageName ) ,
8397 #[ error( transparent) ]
8498 WheelFilename ( #[ from] WheelFilenameError ) ,
8599 #[ error( transparent) ]
@@ -857,6 +871,7 @@ impl<'lock> PylockToml {
857871 install_path : & Path ,
858872 markers : & MarkerEnvironment ,
859873 tags : & Tags ,
874+ build_options : & BuildOptions ,
860875 ) -> Result < Resolution , PylockTomlError > {
861876 let mut graph =
862877 petgraph:: graph:: DiGraph :: with_capacity ( self . packages . len ( ) , self . packages . len ( ) ) ;
@@ -914,8 +929,19 @@ impl<'lock> PylockToml {
914929 _ => { }
915930 }
916931
932+ let no_binary = build_options. no_binary_package ( & package. name ) ;
933+ let no_build = build_options. no_build_package ( & package. name ) ;
934+ let is_wheel = package
935+ . archive
936+ . as_ref ( )
937+ . map ( |archive| archive. is_wheel ( & package. name ) )
938+ . transpose ( ) ?
939+ . unwrap_or_default ( ) ;
940+
917941 // Search for a matching wheel.
918- let dist = if let Some ( best_wheel) = package. find_best_wheel ( tags) {
942+ let dist = if let Some ( best_wheel) =
943+ package. find_best_wheel ( tags) . filter ( |_| !no_binary)
944+ {
919945 let hashes = HashDigests :: from ( best_wheel. hashes . clone ( ) ) ;
920946 let built_dist = Dist :: Built ( BuiltDist :: Registry ( RegistryBuiltDist {
921947 wheels : vec ! [ best_wheel. to_registry_wheel(
@@ -935,7 +961,7 @@ impl<'lock> PylockToml {
935961 hashes,
936962 install : true ,
937963 }
938- } else if let Some ( sdist) = package. sdist . as_ref ( ) {
964+ } else if let Some ( sdist) = package. sdist . as_ref ( ) . filter ( |_| !no_build ) {
939965 let hashes = HashDigests :: from ( sdist. hashes . clone ( ) ) ;
940966 let sdist = Dist :: Source ( SourceDist :: Registry ( sdist. to_sdist (
941967 install_path,
@@ -952,7 +978,7 @@ impl<'lock> PylockToml {
952978 hashes,
953979 install : true ,
954980 }
955- } else if let Some ( sdist) = package. directory . as_ref ( ) {
981+ } else if let Some ( sdist) = package. directory . as_ref ( ) . filter ( |_| !no_build ) {
956982 let hashes = HashDigests :: empty ( ) ;
957983 let sdist = Dist :: Source ( SourceDist :: Directory (
958984 sdist. to_sdist ( install_path, & package. name ) ?,
@@ -966,7 +992,7 @@ impl<'lock> PylockToml {
966992 hashes,
967993 install : true ,
968994 }
969- } else if let Some ( sdist) = package. vcs . as_ref ( ) {
995+ } else if let Some ( sdist) = package. vcs . as_ref ( ) . filter ( |_| !no_build ) {
970996 let hashes = HashDigests :: empty ( ) ;
971997 let sdist = Dist :: Source ( SourceDist :: Git (
972998 sdist. to_sdist ( install_path, & package. name ) ?,
@@ -980,7 +1006,12 @@ impl<'lock> PylockToml {
9801006 hashes,
9811007 install : true ,
9821008 }
983- } else if let Some ( dist) = package. archive . as_ref ( ) {
1009+ } else if let Some ( dist) =
1010+ package
1011+ . archive
1012+ . as_ref ( )
1013+ . filter ( |_| if is_wheel { !no_binary } else { !no_build } )
1014+ {
9841015 let hashes = HashDigests :: from ( dist. hashes . clone ( ) ) ;
9851016 let dist = dist. to_dist ( install_path, & package. name , package. version . as_ref ( ) ) ?;
9861017 let dist = ResolvedDist :: Installable {
@@ -993,13 +1024,20 @@ impl<'lock> PylockToml {
9931024 install : true ,
9941025 }
9951026 } else {
996- // This is only reachable if the package contains a `wheels` entry (and nothing
997- // else), but there are no wheels available for the current environment. (If the
998- // package doesn't contain _any_ of `wheels`, `sdist`, etc., then we error in the
999- // match above.)
1000- //
1001- // TODO(charlie): Include a hint, like in `uv.lock`.
1002- return Err ( PylockTomlError :: MissingWheel ( package. name . clone ( ) ) ) ;
1027+ return match ( no_binary, no_build) {
1028+ ( true , true ) => Err ( PylockTomlError :: NoBinaryNoBuild ( package. name . clone ( ) ) ) ,
1029+ ( true , false ) if is_wheel => {
1030+ Err ( PylockTomlError :: NoBinaryWheelOnly ( package. name . clone ( ) ) )
1031+ }
1032+ ( true , false ) => Err ( PylockTomlError :: NoBinary ( package. name . clone ( ) ) ) ,
1033+ ( false , true ) => Err ( PylockTomlError :: NoBuild ( package. name . clone ( ) ) ) ,
1034+ ( false , false ) if is_wheel => {
1035+ Err ( PylockTomlError :: IncompatibleWheelOnly ( package. name . clone ( ) ) )
1036+ }
1037+ ( false , false ) => Err ( PylockTomlError :: NeitherSourceDistNorWheel (
1038+ package. name . clone ( ) ,
1039+ ) ) ,
1040+ } ;
10031041 } ;
10041042
10051043 let index = graph. add_node ( dist) ;
@@ -1441,6 +1479,31 @@ impl PylockTomlArchive {
14411479 return Err ( PylockTomlError :: ArchiveMissingPathUrl ( name. clone ( ) ) ) ;
14421480 }
14431481 }
1482+
1483+ /// Returns `true` if the [`PylockTomlArchive`] is a wheel.
1484+ fn is_wheel ( & self , name : & PackageName ) -> Result < bool , PylockTomlError > {
1485+ if let Some ( url) = self . url . as_ref ( ) {
1486+ let filename = url
1487+ . filename ( )
1488+ . map_err ( |_| PylockTomlError :: UrlMissingFilename ( url. clone ( ) ) ) ?;
1489+
1490+ let ext = DistExtension :: from_path ( filename. as_ref ( ) ) ?;
1491+ Ok ( matches ! ( ext, DistExtension :: Wheel ) )
1492+ } else if let Some ( path) = self . path . as_ref ( ) {
1493+ let filename = path
1494+ . as_ref ( )
1495+ . file_name ( )
1496+ . and_then ( OsStr :: to_str)
1497+ . ok_or_else ( || {
1498+ PylockTomlError :: PathMissingFilename ( Box :: < Path > :: from ( path. clone ( ) ) )
1499+ } ) ?;
1500+
1501+ let ext = DistExtension :: from_path ( filename) ?;
1502+ Ok ( matches ! ( ext, DistExtension :: Wheel ) )
1503+ } else {
1504+ return Err ( PylockTomlError :: ArchiveMissingPathUrl ( name. clone ( ) ) ) ;
1505+ }
1506+ }
14441507}
14451508
14461509/// Convert a Jiff timestamp to a TOML datetime.
0 commit comments