mirror of https://github.com/astral-sh/uv
Allow downloading wheels for metadata with `--no-binary` (#5707)
## Summary We allow the use of (e.g.) `.whl.metadata` files when `--no-binary` is enabled, so it makes sense that we'd also also allow wheels to be downloaded for metadata extraction. So now, we validate `--no-binary` at install time, rather than metadata-fetch time. Closes https://github.com/astral-sh/uv/issues/5699.
This commit is contained in:
parent
3b75d8b6d6
commit
9346946c7f
|
|
@ -337,6 +337,15 @@ package is "allowed" in such cases without building its metadata.
|
||||||
Both pip and uv allow editables requirements to be built and installed even when `--only-binary` is
|
Both pip and uv allow editables requirements to be built and installed even when `--only-binary` is
|
||||||
provided. For example, `uv pip install -e . --only-binary :all:` is allowed.
|
provided. For example, `uv pip install -e . --only-binary :all:` is allowed.
|
||||||
|
|
||||||
|
## `--no-binary` enforcement
|
||||||
|
|
||||||
|
The `--no-binary` argument is used to restrict installation to source distributions. When
|
||||||
|
`--no-binary` is provided, uv will refuse to install pre-built binary distributions, but _will_
|
||||||
|
reuse any binary distributions that are already present in the local cache.
|
||||||
|
|
||||||
|
Additionally, and in contrast to pip, uv's resolver will still read metadata from pre-built binary
|
||||||
|
distributions when `--no-binary` is provided.
|
||||||
|
|
||||||
## Bytecode compilation
|
## Bytecode compilation
|
||||||
|
|
||||||
Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv
|
Unlike pip, uv does not compile `.py` files to `.pyc` files during installation by default (i.e., uv
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
self.cache,
|
self.cache,
|
||||||
tags,
|
tags,
|
||||||
&HashStrategy::None,
|
&HashStrategy::None,
|
||||||
|
self.build_options,
|
||||||
DistributionDatabase::new(
|
DistributionDatabase::new(
|
||||||
self.client,
|
self.client,
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -145,14 +145,6 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
dist: &BuiltDist,
|
dist: &BuiltDist,
|
||||||
hashes: HashPolicy<'_>,
|
hashes: HashPolicy<'_>,
|
||||||
) -> Result<LocalWheel, Error> {
|
) -> Result<LocalWheel, Error> {
|
||||||
if self
|
|
||||||
.build_context
|
|
||||||
.build_options()
|
|
||||||
.no_binary_package(dist.name())
|
|
||||||
{
|
|
||||||
return Err(Error::NoBinary);
|
|
||||||
}
|
|
||||||
|
|
||||||
match dist {
|
match dist {
|
||||||
BuiltDist::Registry(wheels) => {
|
BuiltDist::Registry(wheels) => {
|
||||||
let wheel = wheels.best_wheel();
|
let wheel = wheels.best_wheel();
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ use uv_normalize::PackageName;
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Building source distributions is disabled")]
|
#[error("Building source distributions is disabled")]
|
||||||
NoBuild,
|
NoBuild,
|
||||||
#[error("Using pre-built wheels is disabled")]
|
|
||||||
NoBinary,
|
|
||||||
|
|
||||||
// Network error
|
// Network error
|
||||||
#[error("Failed to parse URL: {0}")]
|
#[error("Failed to parse URL: {0}")]
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,33 @@ use std::sync::Arc;
|
||||||
use futures::{stream::FuturesUnordered, FutureExt, Stream, TryFutureExt, TryStreamExt};
|
use futures::{stream::FuturesUnordered, FutureExt, Stream, TryFutureExt, TryStreamExt};
|
||||||
use pep508_rs::PackageName;
|
use pep508_rs::PackageName;
|
||||||
use tokio::task::JoinError;
|
use tokio::task::JoinError;
|
||||||
use tracing::instrument;
|
use tracing::{debug, instrument};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use distribution_types::{BuildableSource, CachedDist, Dist, Hashed, Identifier, RemoteSource};
|
use distribution_types::{
|
||||||
|
BuildableSource, CachedDist, Dist, Hashed, Identifier, Name, RemoteSource,
|
||||||
|
};
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
|
use uv_configuration::BuildOptions;
|
||||||
use uv_distribution::{DistributionDatabase, LocalWheel};
|
use uv_distribution::{DistributionDatabase, LocalWheel};
|
||||||
use uv_types::{BuildContext, HashStrategy, InFlight};
|
use uv_types::{BuildContext, HashStrategy, InFlight};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Building source distributions is disabled, but attempted to build `{0}`")]
|
||||||
|
NoBuild(PackageName),
|
||||||
|
#[error("Using pre-built wheels is disabled, but attempted to use `{0}`")]
|
||||||
|
NoBinary(PackageName),
|
||||||
#[error("Failed to unzip wheel: {0}")]
|
#[error("Failed to unzip wheel: {0}")]
|
||||||
Unzip(Dist, #[source] uv_extract::Error),
|
Unzip(Dist, #[source] Box<uv_extract::Error>),
|
||||||
#[error("Failed to fetch wheel: {0}")]
|
#[error("Failed to fetch wheel: {0}")]
|
||||||
Fetch(Dist, #[source] uv_distribution::Error),
|
Fetch(Dist, #[source] Box<uv_distribution::Error>),
|
||||||
/// Should not occur; only seen when another task panicked.
|
/// Should not occur; only seen when another task panicked.
|
||||||
#[error("The task executor is broken, did some other task panic?")]
|
#[error("The task executor is broken, did some other task panic?")]
|
||||||
Join(#[from] JoinError),
|
Join(#[from] JoinError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Editable(#[from] uv_distribution::Error),
|
Editable(#[from] Box<uv_distribution::Error>),
|
||||||
#[error("Failed to write to the client cache")]
|
#[error("Failed to write to the client cache")]
|
||||||
CacheWrite(#[source] std::io::Error),
|
CacheWrite(#[source] std::io::Error),
|
||||||
#[error("Unzip failed in another thread: {0}")]
|
#[error("Unzip failed in another thread: {0}")]
|
||||||
|
|
@ -37,6 +44,7 @@ pub struct Preparer<'a, Context: BuildContext> {
|
||||||
tags: &'a Tags,
|
tags: &'a Tags,
|
||||||
cache: &'a Cache,
|
cache: &'a Cache,
|
||||||
hashes: &'a HashStrategy,
|
hashes: &'a HashStrategy,
|
||||||
|
build_options: &'a BuildOptions,
|
||||||
database: DistributionDatabase<'a, Context>,
|
database: DistributionDatabase<'a, Context>,
|
||||||
reporter: Option<Arc<dyn Reporter>>,
|
reporter: Option<Arc<dyn Reporter>>,
|
||||||
}
|
}
|
||||||
|
|
@ -46,12 +54,14 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> {
|
||||||
cache: &'a Cache,
|
cache: &'a Cache,
|
||||||
tags: &'a Tags,
|
tags: &'a Tags,
|
||||||
hashes: &'a HashStrategy,
|
hashes: &'a HashStrategy,
|
||||||
|
build_options: &'a BuildOptions,
|
||||||
database: DistributionDatabase<'a, Context>,
|
database: DistributionDatabase<'a, Context>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tags,
|
tags,
|
||||||
cache,
|
cache,
|
||||||
hashes,
|
hashes,
|
||||||
|
build_options,
|
||||||
database,
|
database,
|
||||||
reporter: None,
|
reporter: None,
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +75,7 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> {
|
||||||
tags: self.tags,
|
tags: self.tags,
|
||||||
cache: self.cache,
|
cache: self.cache,
|
||||||
hashes: self.hashes,
|
hashes: self.hashes,
|
||||||
|
build_options: self.build_options,
|
||||||
database: self.database.with_reporter(Facade::from(reporter.clone())),
|
database: self.database.with_reporter(Facade::from(reporter.clone())),
|
||||||
reporter: Some(reporter.clone()),
|
reporter: Some(reporter.clone()),
|
||||||
}
|
}
|
||||||
|
|
@ -110,10 +121,27 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> {
|
||||||
|
|
||||||
Ok(wheels)
|
Ok(wheels)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Download, build, and unzip a single wheel.
|
/// Download, build, and unzip a single wheel.
|
||||||
#[instrument(skip_all, fields(name = % dist, size = ? dist.size(), url = dist.file().map(| file | file.url.to_string()).unwrap_or_default()))]
|
#[instrument(skip_all, fields(name = % dist, size = ? dist.size(), url = dist.file().map(| file | file.url.to_string()).unwrap_or_default()))]
|
||||||
pub async fn get_wheel(&self, dist: Dist, in_flight: &InFlight) -> Result<CachedDist, Error> {
|
pub async fn get_wheel(&self, dist: Dist, in_flight: &InFlight) -> Result<CachedDist, Error> {
|
||||||
|
// Validate that the distribution is compatible with the build options.
|
||||||
|
match dist {
|
||||||
|
Dist::Built(ref dist) => {
|
||||||
|
if self.build_options.no_binary_package(dist.name()) {
|
||||||
|
return Err(Error::NoBinary(dist.name().clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Dist::Source(ref dist) => {
|
||||||
|
if self.build_options.no_build_package(dist.name()) {
|
||||||
|
if dist.is_editable() {
|
||||||
|
debug!("Allowing build for editable source distribution: {dist}");
|
||||||
|
} else {
|
||||||
|
return Err(Error::NoBuild(dist.name().clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let id = dist.distribution_id();
|
let id = dist.distribution_id();
|
||||||
if in_flight.downloads.register(id.clone()) {
|
if in_flight.downloads.register(id.clone()) {
|
||||||
let policy = self.hashes.get(&dist);
|
let policy = self.hashes.get(&dist);
|
||||||
|
|
@ -122,7 +150,7 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> {
|
||||||
.database
|
.database
|
||||||
.get_or_build_wheel(&dist, self.tags, policy)
|
.get_or_build_wheel(&dist, self.tags, policy)
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
.map_err(|err| Error::Fetch(dist.clone(), err))
|
.map_err(|err| Error::Fetch(dist.clone(), Box::new(err)))
|
||||||
.await
|
.await
|
||||||
.and_then(|wheel: LocalWheel| {
|
.and_then(|wheel: LocalWheel| {
|
||||||
if wheel.satisfies(policy) {
|
if wheel.satisfies(policy) {
|
||||||
|
|
@ -130,11 +158,11 @@ impl<'a, Context: BuildContext> Preparer<'a, Context> {
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Fetch(
|
Err(Error::Fetch(
|
||||||
dist.clone(),
|
dist.clone(),
|
||||||
uv_distribution::Error::hash_mismatch(
|
Box::new(uv_distribution::Error::hash_mismatch(
|
||||||
dist.to_string(),
|
dist.to_string(),
|
||||||
policy.digests(),
|
policy.digests(),
|
||||||
wheel.hashes(),
|
wheel.hashes(),
|
||||||
),
|
)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -400,6 +400,7 @@ pub(crate) async fn install(
|
||||||
cache,
|
cache,
|
||||||
tags,
|
tags,
|
||||||
hasher,
|
hasher,
|
||||||
|
build_options,
|
||||||
DistributionDatabase::new(client, build_dispatch, concurrency.downloads, preview),
|
DistributionDatabase::new(client, build_dispatch, concurrency.downloads, preview),
|
||||||
)
|
)
|
||||||
.with_reporter(PrepareReporter::from(printer).with_length(remote.len() as u64));
|
.with_reporter(PrepareReporter::from(printer).with_length(remote.len() as u64));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue