mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Allow specific --only-binary and --no-binary packages to override :all: (#4067)
Updates `--no-binary <package>` to take precedence over `--only-binary :all:` and `--only-binary <package>` to take precedence over `--no-binary :all:`. I'm not entirely sure about this behavior, e.g. maybe I provided `--only-binary :all:` later on the command line and really want it to override those earlier arguments of `--no-binary <package>` for safety. Right now we just fail to solve though since we can't satisfy the overlapping requests. Closes https://github.com/astral-sh/uv/issues/4063
This commit is contained in:
parent
cf830288f3
commit
1ab4041baa
12 changed files with 291 additions and 27 deletions
|
|
@ -218,6 +218,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
|||
site_packages,
|
||||
&Reinstall::default(),
|
||||
&NoBinary::default(),
|
||||
&NoBuild::default(),
|
||||
&HashStrategy::default(),
|
||||
self.index_locations,
|
||||
self.cache(),
|
||||
|
|
@ -316,7 +317,17 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
|||
) -> Result<SourceBuild> {
|
||||
match self.no_build {
|
||||
NoBuild::All => debug_assert!(
|
||||
matches!(build_kind, BuildKind::Editable),
|
||||
match self.no_binary {
|
||||
// Allow `all` to be overridden by specific binary exclusions
|
||||
NoBinary::Packages(packages) => {
|
||||
if let Some(dist) = dist {
|
||||
packages.contains(dist.name())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
} || matches!(build_kind, BuildKind::Editable),
|
||||
"Only editable builds are exempt from 'no build' checks"
|
||||
),
|
||||
NoBuild::None => {}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,11 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
|||
) -> Result<LocalWheel, Error> {
|
||||
let no_binary = match self.build_context.no_binary() {
|
||||
NoBinary::None => false,
|
||||
NoBinary::All => true,
|
||||
NoBinary::All => match self.build_context.no_build() {
|
||||
// Allow `all` to be overridden by specific build exclusions
|
||||
NoBuild::Packages(packages) => !packages.contains(dist.name()),
|
||||
_ => true,
|
||||
},
|
||||
NoBinary::Packages(packages) => packages.contains(dist.name()),
|
||||
};
|
||||
|
||||
|
|
@ -415,7 +419,13 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
|||
hashes: HashPolicy<'_>,
|
||||
) -> Result<ArchiveMetadata, Error> {
|
||||
let no_build = match self.build_context.no_build() {
|
||||
NoBuild::All => true,
|
||||
NoBuild::All => match self.build_context.no_binary() {
|
||||
// Allow `all` to be overridden by specific binary exclusions
|
||||
NoBinary::Packages(packages) => {
|
||||
!source.name().is_some_and(|name| packages.contains(name))
|
||||
}
|
||||
_ => true,
|
||||
},
|
||||
NoBuild::None => false,
|
||||
NoBuild::Packages(packages) => {
|
||||
source.name().is_some_and(|name| packages.contains(name))
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use uv_cache::{
|
|||
use uv_client::{
|
||||
CacheControl, CachedClientError, Connectivity, DataWithCachePolicy, RegistryClient,
|
||||
};
|
||||
use uv_configuration::{BuildKind, NoBuild, PreviewMode};
|
||||
use uv_configuration::{BuildKind, NoBinary, NoBuild, PreviewMode};
|
||||
use uv_extract::hash::Hasher;
|
||||
use uv_fs::{write_atomic, LockedFile};
|
||||
use uv_types::{BuildContext, SourceBuildTrait};
|
||||
|
|
@ -1391,7 +1391,13 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
|||
|
||||
// Guard against build of source distributions when disabled.
|
||||
let no_build = match self.build_context.no_build() {
|
||||
NoBuild::All => true,
|
||||
NoBuild::All => match self.build_context.no_binary() {
|
||||
// Allow `all` to be overridden by specific binary exclusions
|
||||
NoBinary::Packages(packages) => {
|
||||
!source.name().is_some_and(|name| packages.contains(name))
|
||||
}
|
||||
_ => true,
|
||||
},
|
||||
NoBuild::None => false,
|
||||
NoBuild::Packages(packages) => {
|
||||
source.name().is_some_and(|name| packages.contains(name))
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use distribution_types::{
|
|||
use platform_tags::Tags;
|
||||
use pypi_types::{Requirement, RequirementSource};
|
||||
use uv_cache::{ArchiveTimestamp, Cache, CacheBucket, WheelCache};
|
||||
use uv_configuration::{NoBinary, Reinstall};
|
||||
use uv_configuration::{NoBinary, NoBuild, Reinstall};
|
||||
use uv_distribution::{
|
||||
BuiltWheelIndex, HttpArchivePointer, LocalArchivePointer, RegistryWheelIndex,
|
||||
};
|
||||
|
|
@ -57,6 +57,7 @@ impl<'a> Planner<'a> {
|
|||
mut site_packages: SitePackages,
|
||||
reinstall: &Reinstall,
|
||||
no_binary: &NoBinary,
|
||||
no_build: &NoBuild,
|
||||
hasher: &HashStrategy,
|
||||
index_locations: &IndexLocations,
|
||||
cache: &Cache,
|
||||
|
|
@ -109,7 +110,11 @@ impl<'a> Planner<'a> {
|
|||
// Check if installation of a binary version of the package should be allowed.
|
||||
let no_binary = match no_binary {
|
||||
NoBinary::None => false,
|
||||
NoBinary::All => true,
|
||||
NoBinary::All => match no_build {
|
||||
// Allow `all` to be overridden by specific build exclusions
|
||||
NoBuild::Packages(packages) => !packages.contains(&requirement.name),
|
||||
_ => true,
|
||||
},
|
||||
NoBinary::Packages(packages) => packages.contains(&requirement.name),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -78,8 +78,14 @@ impl FlatIndex {
|
|||
DistFilename::WheelFilename(filename) => {
|
||||
let version = filename.version.clone();
|
||||
|
||||
let compatibility =
|
||||
Self::wheel_compatibility(&filename, &file.hashes, tags, hasher, no_binary);
|
||||
let compatibility = Self::wheel_compatibility(
|
||||
&filename,
|
||||
&file.hashes,
|
||||
tags,
|
||||
hasher,
|
||||
no_binary,
|
||||
no_build,
|
||||
);
|
||||
let dist = RegistryBuiltWheel {
|
||||
filename,
|
||||
file: Box::new(file),
|
||||
|
|
@ -95,8 +101,13 @@ impl FlatIndex {
|
|||
}
|
||||
}
|
||||
DistFilename::SourceDistFilename(filename) => {
|
||||
let compatibility =
|
||||
Self::source_dist_compatibility(&filename, &file.hashes, hasher, no_build);
|
||||
let compatibility = Self::source_dist_compatibility(
|
||||
&filename,
|
||||
&file.hashes,
|
||||
hasher,
|
||||
no_build,
|
||||
no_binary,
|
||||
);
|
||||
let dist = RegistrySourceDist {
|
||||
name: filename.name.clone(),
|
||||
version: filename.version.clone(),
|
||||
|
|
@ -121,11 +132,16 @@ impl FlatIndex {
|
|||
hashes: &[HashDigest],
|
||||
hasher: &HashStrategy,
|
||||
no_build: &NoBuild,
|
||||
no_binary: &NoBinary,
|
||||
) -> SourceDistCompatibility {
|
||||
// Check if source distributions are allowed for this package.
|
||||
let no_build = match no_build {
|
||||
NoBuild::None => false,
|
||||
NoBuild::All => true,
|
||||
NoBuild::All => match no_binary {
|
||||
// Allow `all` to be overridden by specific binary exclusions
|
||||
NoBinary::Packages(packages) => !packages.contains(&filename.name),
|
||||
_ => true,
|
||||
},
|
||||
NoBuild::Packages(packages) => packages.contains(&filename.name),
|
||||
};
|
||||
|
||||
|
|
@ -155,11 +171,16 @@ impl FlatIndex {
|
|||
tags: Option<&Tags>,
|
||||
hasher: &HashStrategy,
|
||||
no_binary: &NoBinary,
|
||||
no_build: &NoBuild,
|
||||
) -> WheelCompatibility {
|
||||
// Check if binaries are allowed for this package.
|
||||
let no_binary = match no_binary {
|
||||
NoBinary::None => false,
|
||||
NoBinary::All => true,
|
||||
NoBinary::All => match no_build {
|
||||
// Allow `all` to be overridden by specific build exclusions
|
||||
NoBuild::Packages(packages) => !packages.contains(&filename.name),
|
||||
_ => true,
|
||||
},
|
||||
NoBinary::Packages(packages) => packages.contains(&filename.name),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -96,18 +96,28 @@ impl VersionMap {
|
|||
},
|
||||
}
|
||||
}
|
||||
let (no_binary, no_build) = (
|
||||
// Check if binaries are allowed for this package.
|
||||
let no_binary = match no_binary {
|
||||
match no_binary {
|
||||
NoBinary::None => false,
|
||||
NoBinary::All => true,
|
||||
NoBinary::All => match no_build {
|
||||
// Allow `all` to be overridden by specific build exclusions
|
||||
NoBuild::Packages(packages) => !packages.contains(package_name),
|
||||
_ => true,
|
||||
},
|
||||
NoBinary::Packages(packages) => packages.contains(package_name),
|
||||
};
|
||||
},
|
||||
// Check if source distributions are allowed for this package.
|
||||
let no_build = match no_build {
|
||||
match no_build {
|
||||
NoBuild::None => false,
|
||||
NoBuild::All => true,
|
||||
NoBuild::All => match no_binary {
|
||||
// Allow `all` to be overridden by specific binary exclusions
|
||||
NoBinary::Packages(packages) => !packages.contains(package_name),
|
||||
_ => true,
|
||||
},
|
||||
NoBuild::Packages(packages) => packages.contains(package_name),
|
||||
};
|
||||
},
|
||||
);
|
||||
let allowed_yanks = allowed_yanks
|
||||
.allowed_versions(package_name)
|
||||
.cloned()
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ pub(crate) async fn pip_install(
|
|||
Modifications::Sufficient,
|
||||
&reinstall,
|
||||
&no_binary,
|
||||
&no_build,
|
||||
link_mode,
|
||||
compile,
|
||||
&index_locations,
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ use pypi_types::Requirement;
|
|||
use uv_cache::Cache;
|
||||
use uv_client::{BaseClientBuilder, RegistryClient};
|
||||
use uv_configuration::{
|
||||
Concurrency, Constraints, ExtrasSpecification, NoBinary, Overrides, PreviewMode, Reinstall,
|
||||
Upgrade,
|
||||
Concurrency, Constraints, ExtrasSpecification, NoBinary, NoBuild, Overrides, PreviewMode,
|
||||
Reinstall, Upgrade,
|
||||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_distribution::DistributionDatabase;
|
||||
|
|
@ -290,6 +290,7 @@ pub(crate) async fn install(
|
|||
modifications: Modifications,
|
||||
reinstall: &Reinstall,
|
||||
no_binary: &NoBinary,
|
||||
no_build: &NoBuild,
|
||||
link_mode: LinkMode,
|
||||
compile: bool,
|
||||
index_urls: &IndexLocations,
|
||||
|
|
@ -317,6 +318,7 @@ pub(crate) async fn install(
|
|||
site_packages,
|
||||
reinstall,
|
||||
no_binary,
|
||||
no_build,
|
||||
hasher,
|
||||
index_urls,
|
||||
cache,
|
||||
|
|
|
|||
|
|
@ -343,6 +343,7 @@ pub(crate) async fn pip_sync(
|
|||
Modifications::Exact,
|
||||
reinstall,
|
||||
&no_binary,
|
||||
&no_build,
|
||||
link_mode,
|
||||
compile,
|
||||
&index_locations,
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@ pub(crate) async fn update_environment(
|
|||
pip::operations::Modifications::Sufficient,
|
||||
&reinstall,
|
||||
&no_binary,
|
||||
&no_build,
|
||||
link_mode,
|
||||
compile,
|
||||
index_locations,
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ pub(super) async fn do_sync(
|
|||
Modifications::Sufficient,
|
||||
&reinstall,
|
||||
&no_binary,
|
||||
&no_build,
|
||||
link_mode,
|
||||
compile,
|
||||
index_locations,
|
||||
|
|
|
|||
|
|
@ -1830,6 +1830,201 @@ fn reinstall_no_binary() {
|
|||
context.assert_command("import anyio").success();
|
||||
}
|
||||
|
||||
/// Overlapping usage of `--no-binary` and `--only-binary`
|
||||
#[test]
|
||||
fn install_no_binary_overrides_only_binary_all() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// The specific `--no-binary` should override the less specific `--only-binary`
|
||||
let mut command = context.install();
|
||||
command
|
||||
.arg("anyio")
|
||||
.arg("--only-binary")
|
||||
.arg(":all:")
|
||||
.arg("--no-binary")
|
||||
.arg("idna")
|
||||
.arg("--strict");
|
||||
uv_snapshot!(
|
||||
command,
|
||||
@r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Downloaded 3 packages in [TIME]
|
||||
Installed 3 packages in [TIME]
|
||||
+ anyio==4.3.0
|
||||
+ idna==3.6
|
||||
+ sniffio==1.3.1
|
||||
"###
|
||||
);
|
||||
|
||||
context.assert_command("import anyio").success();
|
||||
}
|
||||
|
||||
/// Overlapping usage of `--no-binary` and `--only-binary`
|
||||
#[test]
|
||||
fn install_only_binary_overrides_no_binary_all() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// The specific `--only-binary` should override the less specific `--no-binary`
|
||||
let mut command = context.install();
|
||||
command
|
||||
.arg("anyio")
|
||||
.arg("--no-binary")
|
||||
.arg(":all:")
|
||||
.arg("--only-binary")
|
||||
.arg("idna")
|
||||
.arg("--strict");
|
||||
uv_snapshot!(
|
||||
command,
|
||||
@r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Downloaded 3 packages in [TIME]
|
||||
Installed 3 packages in [TIME]
|
||||
+ anyio==4.3.0
|
||||
+ idna==3.6
|
||||
+ sniffio==1.3.1
|
||||
"###
|
||||
);
|
||||
|
||||
context.assert_command("import anyio").success();
|
||||
}
|
||||
|
||||
/// Overlapping usage of `--no-binary` and `--only-binary`
|
||||
// TODO(zanieb): We should have a better error message here
|
||||
#[test]
|
||||
fn install_only_binary_all_and_no_binary_all() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// With both as `:all:` we can't install
|
||||
let mut command = context.install();
|
||||
command
|
||||
.arg("anyio")
|
||||
.arg("--no-binary")
|
||||
.arg(":all:")
|
||||
.arg("--only-binary")
|
||||
.arg(":all:")
|
||||
.arg("--strict");
|
||||
uv_snapshot!(
|
||||
command,
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× No solution found when resolving dependencies:
|
||||
╰─▶ Because only the following versions of anyio are available:
|
||||
anyio>=1.0.0,<=1.4.0
|
||||
anyio>=2.0.0,<=2.2.0
|
||||
anyio>=3.0.0,<=3.6.2
|
||||
anyio>=3.7.0,<=3.7.1
|
||||
anyio>=4.0.0
|
||||
and anyio==1.0.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<1.1.0
|
||||
anyio>1.4.0,<2.0.0
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==1.1.0 has no usable wheels and building from source is disabled and anyio==1.2.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<1.2.1
|
||||
anyio>1.4.0,<2.0.0
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==1.2.1 has no usable wheels and building from source is disabled and anyio==1.2.2 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<1.2.3
|
||||
anyio>1.4.0,<2.0.0
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==1.2.3 has no usable wheels and building from source is disabled and anyio==1.3.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<1.3.1
|
||||
anyio>1.4.0,<2.0.0
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==1.3.1 has no usable wheels and building from source is disabled and anyio==1.4.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<2.0.0
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==2.0.0 has no usable wheels and building from source is disabled and anyio==2.0.1 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<2.0.2
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==2.0.2 has no usable wheels and building from source is disabled and anyio==2.1.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<2.2.0
|
||||
anyio>2.2.0,<3.0.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==2.2.0 has no usable wheels and building from source is disabled and anyio==3.0.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.0.1
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.0.1 has no usable wheels and building from source is disabled and anyio==3.1.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.2.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.2.0 has no usable wheels and building from source is disabled and anyio==3.2.1 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.3.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.3.0 has no usable wheels and building from source is disabled and anyio==3.3.1 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.3.2
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.3.2 has no usable wheels and building from source is disabled and anyio==3.3.3 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.3.4
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.3.4 has no usable wheels and building from source is disabled and anyio==3.4.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.5.0
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.5.0 has no usable wheels and building from source is disabled and anyio==3.6.0 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.6.1
|
||||
anyio>3.6.2,<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.6.1 has no usable wheels and building from source is disabled and anyio==3.6.2 has no usable wheels and building from source is disabled, we can conclude that any of:
|
||||
anyio<3.7.0
|
||||
anyio>3.7.1,<4.0.0
|
||||
cannot be used.
|
||||
And because anyio==3.7.0 has no usable wheels and building from source is disabled and anyio==3.7.1 has no usable wheels and building from source is disabled, we can conclude that anyio<4.0.0 cannot be used.
|
||||
And because anyio==4.0.0 has no usable wheels and building from source is disabled and anyio==4.1.0 has no usable wheels and building from source is disabled, we can conclude that anyio<4.2.0 cannot be used.
|
||||
And because anyio==4.2.0 has no usable wheels and building from source is disabled and anyio==4.3.0 has no usable wheels and building from source is disabled, we can conclude that anyio<4.4.0 cannot be used.
|
||||
And because anyio==4.4.0 has no usable wheels and building from source is disabled and you require anyio, we can conclude that the requirements are unsatisfiable.
|
||||
|
||||
hint: Pre-releases are available for anyio in the requested range (e.g., 4.0.0rc1), but pre-releases weren't enabled (try: `--prerelease=allow`)
|
||||
"###
|
||||
);
|
||||
|
||||
context.assert_command("import anyio").failure();
|
||||
}
|
||||
|
||||
/// Respect `--only-binary` flags in `requirements.txt`
|
||||
#[test]
|
||||
fn only_binary_requirements_txt() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue