Build backend: Add direct builds to the resolver and installer (#9621)

This is like #9556, but at the level of all other builds, including the
resolver and installer. Going through PEP 517 to build a package is
slow, so when building a package with the uv build backend, we can call
into the uv build backend directly instead: No temporary virtual env, no
temp venv sync, no python subprocess calls, no uv subprocess calls.

This fast path is gated through preview. Since the uv wheel is not
available at test time, I've manually confirmed the feature by comparing
`uv venv && cargo run pip install . -v --preview --reinstall .` and `uv
venv && cargo run pip install . -v --reinstall .`. When hacking the
preview so that the python uv build backend works without the setting
the direct build also (wheel built with `maturin build --profile
profiling`), we can see the perfomance difference:

```
$ hyperfine --prepare "uv venv" --warmup 3 \
    "UV_PREVIEW=1 target/profiling/uv pip install --no-deps --reinstall scripts/packages/built-by-uv --preview" \
    "target/profiling/uv pip install --no-deps --reinstall scripts/packages/built-by-uv --find-links target/wheels/"
Benchmark 1: UV_PREVIEW=1 target/profiling/uv pip install --no-deps --reinstall scripts/packages/built-by-uv --preview
  Time (mean ± σ):      33.1 ms ±   2.5 ms    [User: 25.7 ms, System: 13.0 ms]
  Range (min … max):    29.8 ms …  47.3 ms    73 runs
 
Benchmark 2: target/profiling/uv pip install --no-deps --reinstall scripts/packages/built-by-uv --find-links target/wheels/
  Time (mean ± σ):     115.1 ms ±   4.3 ms    [User: 54.0 ms, System: 27.0 ms]
  Range (min … max):   109.2 ms … 123.8 ms    25 runs
 
Summary
  UV_PREVIEW=1 target/profiling/uv pip install --no-deps --reinstall scripts/packages/built-by-uv --preview ran
    3.48 ± 0.29 times faster than target/profiling/uv pip install --no-deps --reinstall scripts/packages/built-by-uv --find-links target/wheels/
```

Do we need a global option to disable the fast path? There is one for
`uv build` because `--force-pep517` moves `uv build` much closer to a
`pip install` from source that a user of a library would experience (See
discussion at #9610), but uv overall doesn't really make guarantees
around the build env of dependencies, so I consider the direct build a
valid option.

Best reviewed commit-by-commit, only the last commit is the actual
implementation, while the preview mode introduction is just a
refactoring touching too many files.
This commit is contained in:
konsti 2024-12-04 16:57:18 +01:00 committed by GitHub
parent 566c178276
commit 6ed6fc108e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 304 additions and 86 deletions

6
Cargo.lock generated
View file

@ -4583,6 +4583,7 @@ dependencies = [
"uv-pep440",
"uv-pep508",
"uv-pypi-types",
"uv-version",
"uv-warnings",
"version-ranges",
"walkdir",
@ -4841,12 +4842,15 @@ dependencies = [
"futures",
"itertools 0.13.0",
"rustc-hash",
"tokio",
"tracing",
"uv-build-backend",
"uv-build-frontend",
"uv-cache",
"uv-client",
"uv-configuration",
"uv-distribution",
"uv-distribution-filename",
"uv-distribution-types",
"uv-git",
"uv-install-wheel",
@ -4855,6 +4859,7 @@ dependencies = [
"uv-python",
"uv-resolver",
"uv-types",
"uv-version",
]
[[package]]
@ -5590,6 +5595,7 @@ dependencies = [
"url",
"uv-cache",
"uv-configuration",
"uv-distribution-filename",
"uv-distribution-types",
"uv-git",
"uv-normalize",

View file

@ -87,7 +87,7 @@ mod resolver {
use uv_client::RegistryClient;
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, Constraints, IndexStrategy, LowerBound,
SourceStrategy,
PreviewMode, SourceStrategy,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution::DistributionDatabase;
@ -190,6 +190,7 @@ mod resolver {
LowerBound::default(),
sources,
concurrency,
PreviewMode::Enabled,
);
let markers = if universal {

View file

@ -20,6 +20,7 @@ uv-normalize = { workspace = true }
uv-pep440 = { workspace = true }
uv-pep508 = { workspace = true }
uv-pypi-types = { workspace = true }
uv-version = { workspace = true }
uv-warnings = { workspace = true }
csv = { workspace = true }

View file

@ -2,7 +2,7 @@ mod metadata;
mod source_dist;
mod wheel;
pub use metadata::PyProjectToml;
pub use metadata::{check_direct_build, PyProjectToml};
pub use source_dist::{build_source_dist, list_source_dist};
pub use wheel::{build_editable, build_wheel, list_wheel, metadata};

View file

@ -3,6 +3,7 @@ use itertools::Itertools;
use serde::Deserialize;
use std::collections::{BTreeMap, Bound};
use std::ffi::OsStr;
use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use tracing::{debug, trace};
@ -50,6 +51,41 @@ pub enum ValidationError {
InvalidSpdx(String, #[source] spdx::error::ParseError),
}
/// Check if the build backend is matching the currently running uv version.
pub fn check_direct_build(source_tree: &Path, name: impl Display) -> bool {
let pyproject_toml: PyProjectToml =
match fs_err::read_to_string(source_tree.join("pyproject.toml"))
.map_err(|err| err.to_string())
.and_then(|pyproject_toml| {
toml::from_str(&pyproject_toml).map_err(|err| err.to_string())
}) {
Ok(pyproject_toml) => pyproject_toml,
Err(err) => {
debug!(
"Not using uv build backend direct build of {name}, no pyproject.toml: {err}"
);
return false;
}
};
match pyproject_toml
.check_build_system(uv_version::version())
.as_slice()
{
// No warnings -> match
[] => true,
// Any warning -> no match
[first, others @ ..] => {
debug!(
"Not using uv build backend direct build of {name}, pyproject.toml does not match: {first}"
);
for other in others {
trace!("Further uv build backend direct build of {name} mismatch: {other}");
}
false
}
}
}
/// A `pyproject.toml` as specified in PEP 517.
#[derive(Deserialize, Debug, Clone)]
#[serde(

View file

@ -251,7 +251,7 @@ impl SourceBuild {
interpreter: &Interpreter,
build_context: &impl BuildContext,
source_build_context: SourceBuildContext,
version_id: Option<String>,
version_id: Option<&str>,
locations: &IndexLocations,
source_strategy: SourceStrategy,
config_settings: ConfigSettings,
@ -376,7 +376,7 @@ impl SourceBuild {
build_context,
package_name.as_ref(),
package_version.as_ref(),
version_id.as_deref(),
version_id,
locations,
source_strategy,
build_kind,
@ -401,7 +401,7 @@ impl SourceBuild {
metadata_directory: None,
package_name,
package_version,
version_id,
version_id: version_id.map(ToString::to_string),
environment_variables,
modified_path,
runner,

View file

@ -17,11 +17,13 @@ doctest = false
workspace = true
[dependencies]
uv-build-backend = { workspace = true }
uv-build-frontend = { workspace = true }
uv-cache = { workspace = true }
uv-client = { workspace = true }
uv-configuration = { workspace = true }
uv-distribution = { workspace = true }
uv-distribution-filename = { workspace = true }
uv-distribution-types = { workspace = true }
uv-git = { workspace = true }
uv-install-wheel = { workspace = true }
@ -30,9 +32,11 @@ uv-pypi-types = { workspace = true }
uv-python = { workspace = true }
uv-resolver = { workspace = true }
uv-types = { workspace = true }
uv-version = { workspace = true }
anyhow = { workspace = true }
futures = { workspace = true }
itertools = { workspace = true }
rustc-hash = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }

View file

@ -9,17 +9,18 @@ use anyhow::{anyhow, Context, Result};
use futures::FutureExt;
use itertools::Itertools;
use rustc_hash::FxHashMap;
use tracing::{debug, instrument};
use tracing::{debug, instrument, trace};
use uv_build_backend::check_direct_build;
use uv_build_frontend::{SourceBuild, SourceBuildContext};
use uv_cache::Cache;
use uv_client::RegistryClient;
use uv_configuration::{
BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, LowerBound, Reinstall,
SourceStrategy,
BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, LowerBound, PreviewMode,
Reinstall, SourceStrategy,
};
use uv_configuration::{BuildOutput, Concurrency};
use uv_distribution::DistributionDatabase;
use uv_distribution_filename::DistFilename;
use uv_distribution_types::{
CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, Name, Resolution,
SourceDist, VersionOrUrlRef,
@ -57,6 +58,7 @@ pub struct BuildDispatch<'a> {
bounds: LowerBound,
sources: SourceStrategy,
concurrency: Concurrency,
preview: PreviewMode,
}
impl<'a> BuildDispatch<'a> {
@ -79,6 +81,7 @@ impl<'a> BuildDispatch<'a> {
bounds: LowerBound,
sources: SourceStrategy,
concurrency: Concurrency,
preview: PreviewMode,
) -> Self {
Self {
client,
@ -101,6 +104,7 @@ impl<'a> BuildDispatch<'a> {
bounds,
sources,
concurrency,
preview,
}
}
@ -340,7 +344,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
source: &'data Path,
subdirectory: Option<&'data Path>,
install_path: &'data Path,
version_id: Option<String>,
version_id: Option<&'data str>,
dist: Option<&'data SourceDist>,
sources: SourceStrategy,
build_kind: BuildKind,
@ -394,6 +398,73 @@ impl<'a> BuildContext for BuildDispatch<'a> {
.await?;
Ok(builder)
}
async fn direct_build<'data>(
&'data self,
source: &'data Path,
subdirectory: Option<&'data Path>,
output_dir: &'data Path,
build_kind: BuildKind,
version_id: Option<&'data str>,
) -> Result<Option<DistFilename>> {
// Direct builds are a preview feature with the uv build backend.
if self.preview.is_disabled() {
trace!("Preview is disabled, not checking for direct build");
return Ok(None);
}
let source_tree = if let Some(subdir) = subdirectory {
source.join(subdir)
} else {
source.to_path_buf()
};
// Only perform the direct build if the backend is uv in a compatible version.
let source_tree_str = source_tree.display().to_string();
let identifier = version_id.unwrap_or_else(|| &source_tree_str);
if !check_direct_build(&source_tree, identifier) {
trace!("Requirements for direct build not matched: {identifier}");
return Ok(None);
}
debug!("Performing direct build for {identifier}");
let output_dir = output_dir.to_path_buf();
let filename = tokio::task::spawn_blocking(move || -> Result<_> {
let filename = match build_kind {
BuildKind::Wheel => {
let wheel = uv_build_backend::build_wheel(
&source_tree,
&output_dir,
None,
uv_version::version(),
)?;
DistFilename::WheelFilename(wheel)
}
BuildKind::Sdist => {
let source_dist = uv_build_backend::build_source_dist(
&source_tree,
&output_dir,
uv_version::version(),
)?;
DistFilename::SourceDistFilename(source_dist)
}
BuildKind::Editable => {
let wheel = uv_build_backend::build_editable(
&source_tree,
&output_dir,
None,
uv_version::version(),
)?;
DistFilename::WheelFilename(wheel)
}
};
Ok(filename)
})
.await??;
Ok(Some(filename))
}
}
/// Shared state used during resolution and installation.

View file

@ -1803,13 +1803,32 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
fs::create_dir_all(&cache_shard)
.await
.map_err(Error::CacheWrite)?;
let disk_filename = self
// Try a direct build if that isn't disabled and the uv build backend is used.
let disk_filename = if let Some(name) = self
.build_context
.direct_build(
source_root,
subdirectory,
temp_dir.path(),
if source.is_editable() {
BuildKind::Editable
} else {
BuildKind::Wheel
},
Some(&source.to_string()),
)
.await
.map_err(Error::Build)?
{
// In the uv build backend, the normalized filename and the disk filename are the same.
name.to_string()
} else {
self.build_context
.setup_build(
source_root,
subdirectory,
source_root,
Some(source.to_string()),
Some(&source.to_string()),
source.as_dist(),
source_strategy,
if source.is_editable() {
@ -1823,7 +1842,8 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.map_err(Error::Build)?
.wheel(temp_dir.path())
.await
.map_err(Error::Build)?;
.map_err(Error::Build)?
};
// Read the metadata from the wheel.
let filename = WheelFilename::from_str(&disk_filename)?;
@ -1884,7 +1904,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
source_root,
subdirectory,
source_root,
Some(source.to_string()),
Some(&source.to_string()),
source.as_dist(),
source_strategy,
if source.is_editable() {

View file

@ -18,6 +18,7 @@ workspace = true
[dependencies]
uv-cache = { workspace = true }
uv-configuration = { workspace = true }
uv-distribution-filename = { workspace = true }
uv-distribution-types = { workspace = true }
uv-git = { workspace = true }
uv-normalize = { workspace = true }

View file

@ -1,5 +1,6 @@
use std::future::Future;
use std::path::{Path, PathBuf};
use uv_distribution_filename::DistFilename;
use anyhow::Result;
@ -115,12 +116,27 @@ pub trait BuildContext {
source: &'a Path,
subdirectory: Option<&'a Path>,
install_path: &'a Path,
version_id: Option<String>,
version_id: Option<&'a str>,
dist: Option<&'a SourceDist>,
sources: SourceStrategy,
build_kind: BuildKind,
build_output: BuildOutput,
) -> impl Future<Output = Result<Self::SourceDistBuilder>> + 'a;
/// Build by calling directly into the uv build backend without PEP 517, if possible.
///
/// Checks if the source tree uses uv as build backend. If not, it returns `Ok(None)`, otherwise
/// it builds and returns the name of the built file.
///
/// `version_id` is for error reporting only.
fn direct_build<'a>(
&'a self,
source: &'a Path,
subdirectory: Option<&'a Path>,
output_dir: &'a Path,
build_kind: BuildKind,
version_id: Option<&'a str>,
) -> impl Future<Output = Result<Option<DistFilename>>> + 'a;
}
/// A wrapper for `uv_build::SourceBuild` to avoid cyclical crate dependencies.
@ -140,7 +156,9 @@ pub trait SourceBuildTrait {
///
/// For PEP 517 builds, this calls `build_wheel`.
///
/// Returns the filename of the built wheel inside the given `wheel_dir`.
/// Returns the filename of the built wheel inside the given `wheel_dir`. The filename is a
/// string and not a `WheelFilename` because the on disk filename might not be normalized in the
/// same way as uv would.
fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> impl Future<Output = Result<String>> + 'a;
}

View file

@ -8,7 +8,7 @@ use std::{fmt, io};
use anyhow::{Context, Result};
use owo_colors::OwoColorize;
use thiserror::Error;
use tracing::{debug, instrument, trace};
use tracing::instrument;
use crate::commands::pip::operations;
use crate::commands::project::find_requires_python;
@ -17,7 +17,7 @@ use crate::commands::ExitStatus;
use crate::printer::Printer;
use crate::settings::{ResolverSettings, ResolverSettingsRef};
use uv_auth::store_credentials;
use uv_build_backend::PyProjectToml;
use uv_build_backend::check_direct_build;
use uv_cache::{Cache, CacheBucket};
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
@ -190,7 +190,7 @@ async fn build_impl(
preview: PreviewMode,
) -> Result<BuildResult> {
if list && preview.is_disabled() {
// We need the fast path for list and that is preview only.
// We need the direct build for list and that is preview only.
writeln!(
printer.stderr(),
"The `--list` option is only available in preview mode; add the `--preview` flag to use `--list`"
@ -576,6 +576,7 @@ async fn build_package(
LowerBound::Allow,
sources,
concurrency,
preview,
);
prepare_output_directory(&output_dir).await?;
@ -590,14 +591,17 @@ async fn build_package(
return Err(Error::ListForcePep517);
}
if !check_fast_path(source.path()) {
if !check_direct_build(source.path(), source.path().user_display()) {
// TODO(konsti): Provide more context on what mismatched
return Err(Error::ListNonUv);
}
BuildAction::List
} else if preview.is_enabled() && !force_pep517 && check_fast_path(source.path()) {
BuildAction::FastPath
} else if preview.is_enabled()
&& !force_pep517
&& check_direct_build(source.path(), source.path().user_display())
{
BuildAction::DirectBuild
} else {
BuildAction::Pep517
};
@ -816,7 +820,7 @@ enum BuildAction {
/// Only list the files that would be included, don't actually build.
List,
/// Build by calling directly into the build backend.
FastPath,
DirectBuild,
/// Build through the PEP 517 hooks.
Pep517,
}
@ -826,14 +830,14 @@ impl BuildAction {
fn force_build(self) -> Self {
match self {
// List is only available for the uv build backend
Self::List => Self::FastPath,
Self::FastPath => Self::FastPath,
Self::List => Self::DirectBuild,
Self::DirectBuild => Self::DirectBuild,
Self::Pep517 => Self::Pep517,
}
}
}
/// Build a source distribution, either through PEP 517 or through the fast path.
/// Build a source distribution, either through PEP 517 or through a direct build.
#[instrument(skip_all)]
async fn build_sdist(
source_tree: &Path,
@ -864,7 +868,7 @@ async fn build_sdist(
file_list,
}
}
BuildAction::FastPath => {
BuildAction::DirectBuild => {
writeln!(
printer.stderr(),
"{}",
@ -911,7 +915,7 @@ async fn build_sdist(
source_tree,
subdirectory,
source.path(),
version_id.map(ToString::to_string),
version_id,
dist,
sources,
BuildKind::Sdist,
@ -932,7 +936,7 @@ async fn build_sdist(
Ok(build_result)
}
/// Build a wheel, either through PEP 517 or through the fast path.
/// Build a wheel, either through PEP 517 or through a direct build.
#[instrument(skip_all)]
async fn build_wheel(
source_tree: &Path,
@ -964,7 +968,7 @@ async fn build_wheel(
file_list,
}
}
BuildAction::FastPath => {
BuildAction::DirectBuild => {
writeln!(
printer.stderr(),
"{}",
@ -1008,7 +1012,7 @@ async fn build_wheel(
source_tree,
subdirectory,
source.path(),
version_id.map(ToString::to_string),
version_id,
dist,
sources,
BuildKind::Wheel,
@ -1246,33 +1250,3 @@ impl BuildPlan {
})
}
}
/// Check if the build backend is matching the currently running uv version.
fn check_fast_path(source_tree: &Path) -> bool {
let pyproject_toml: PyProjectToml =
match fs_err::read_to_string(source_tree.join("pyproject.toml"))
.map_err(anyhow::Error::from)
.and_then(|pyproject_toml| Ok(toml::from_str(&pyproject_toml)?))
{
Ok(pyproject_toml) => pyproject_toml,
Err(err) => {
debug!("Not using uv build backend fast path, no pyproject.toml: {err}");
return false;
}
};
match pyproject_toml
.check_build_system(uv_version::version())
.as_slice()
{
// No warnings -> match
[] => true,
// Any warning -> no match
[first, others @ ..] => {
debug!("Not using uv build backend fast path, pyproject.toml does not match: {first}");
for other in others {
trace!("Further uv build backend fast path mismatch: {other}");
}
false
}
}
}

View file

@ -11,7 +11,7 @@ use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, IndexStrategy,
LowerBound, NoBinary, NoBuild, Reinstall, SourceStrategy, TrustedHost, Upgrade,
LowerBound, NoBinary, NoBuild, PreviewMode, Reinstall, SourceStrategy, TrustedHost, Upgrade,
};
use uv_configuration::{KeyringProviderType, TargetTriple};
use uv_dispatch::{BuildDispatch, SharedState};
@ -96,6 +96,7 @@ pub(crate) async fn pip_compile(
quiet: bool,
cache: Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
// If the user requests `extras` but does not provide a valid source (e.g., a `pyproject.toml`),
// return an error.
@ -354,6 +355,7 @@ pub(crate) async fn pip_compile(
LowerBound::Warn,
sources,
concurrency,
preview,
);
let options = OptionsBuilder::new()

View file

@ -8,7 +8,7 @@ use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, HashCheckingMode,
IndexStrategy, LowerBound, Reinstall, SourceStrategy, TrustedHost, Upgrade,
IndexStrategy, LowerBound, PreviewMode, Reinstall, SourceStrategy, TrustedHost, Upgrade,
};
use uv_configuration::{KeyringProviderType, TargetTriple};
use uv_dispatch::{BuildDispatch, SharedState};
@ -85,6 +85,7 @@ pub(crate) async fn pip_install(
cache: Cache,
dry_run: bool,
printer: Printer,
preview: PreviewMode,
) -> anyhow::Result<ExitStatus> {
let start = std::time::Instant::now();
@ -383,6 +384,7 @@ pub(crate) async fn pip_install(
LowerBound::Warn,
sources,
concurrency,
preview,
);
let options = OptionsBuilder::new()

View file

@ -8,7 +8,7 @@ use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, Constraints, ExtrasSpecification, HashCheckingMode,
IndexStrategy, LowerBound, Reinstall, SourceStrategy, TrustedHost, Upgrade,
IndexStrategy, LowerBound, PreviewMode, Reinstall, SourceStrategy, TrustedHost, Upgrade,
};
use uv_configuration::{KeyringProviderType, TargetTriple};
use uv_dispatch::{BuildDispatch, SharedState};
@ -74,6 +74,7 @@ pub(crate) async fn pip_sync(
cache: Cache,
dry_run: bool,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
@ -326,6 +327,7 @@ pub(crate) async fn pip_sync(
LowerBound::Warn,
sources,
concurrency,
preview,
);
// Determine the set of installed packages.

View file

@ -15,8 +15,8 @@ use uv_cache_key::RepositoryUrl;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, DevMode, EditableMode,
ExtrasSpecification, GroupsSpecification, InstallOptions, LowerBound, SourceStrategy,
TrustedHost,
ExtrasSpecification, GroupsSpecification, InstallOptions, LowerBound, PreviewMode,
SourceStrategy, TrustedHost,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution::DistributionDatabase;
@ -85,6 +85,7 @@ pub(crate) async fn add(
no_config: bool,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
for source in &requirements {
match source {
@ -340,6 +341,7 @@ pub(crate) async fn add(
bounds,
sources,
concurrency,
preview,
);
// Resolve any unnamed requirements.
@ -681,6 +683,7 @@ pub(crate) async fn add(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -724,6 +727,7 @@ async fn lock_and_sync(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<(), ProjectError> {
let mode = if locked {
LockMode::Locked(environment.interpreter())
@ -744,6 +748,7 @@ async fn lock_and_sync(
allow_insecure_host,
cache,
printer,
preview,
)
.await?
.into_lock();
@ -862,6 +867,7 @@ async fn lock_and_sync(
allow_insecure_host,
cache,
printer,
preview,
)
.await?
.into_lock();
@ -928,6 +934,7 @@ async fn lock_and_sync(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;

View file

@ -9,7 +9,7 @@ use crate::settings::ResolverInstallerSettings;
use uv_cache::{Cache, CacheBucket};
use uv_cache_key::{cache_digest, hash_digest};
use uv_client::Connectivity;
use uv_configuration::{Concurrency, TrustedHost};
use uv_configuration::{Concurrency, PreviewMode, TrustedHost};
use uv_dispatch::SharedState;
use uv_distribution_types::{Name, Resolution};
use uv_python::{Interpreter, PythonEnvironment};
@ -41,6 +41,7 @@ impl CachedEnvironment {
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<Self, ProjectError> {
// When caching, always use the base interpreter, rather than that of the virtual
// environment.
@ -72,6 +73,7 @@ impl CachedEnvironment {
allow_insecure_host,
cache,
printer,
preview,
)
.await?,
);
@ -125,6 +127,7 @@ impl CachedEnvironment {
allow_insecure_host,
cache,
printer,
preview,
)
.await?;

View file

@ -10,7 +10,7 @@ use uv_cache::Cache;
use uv_client::Connectivity;
use uv_configuration::{
Concurrency, DevGroupsSpecification, EditableMode, ExportFormat, ExtrasSpecification,
InstallOptions, LowerBound, TrustedHost,
InstallOptions, LowerBound, PreviewMode, TrustedHost,
};
use uv_dispatch::SharedState;
use uv_normalize::PackageName;
@ -58,6 +58,7 @@ pub(crate) async fn export(
quiet: bool,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
// Identify the project.
let project = if frozen {
@ -145,6 +146,7 @@ pub(crate) async fn export(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{

View file

@ -11,8 +11,8 @@ use tracing::debug;
use uv_cache::Cache;
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, Constraints, ExtrasSpecification, LowerBound, Reinstall, SourceStrategy,
TrustedHost, Upgrade,
Concurrency, Constraints, ExtrasSpecification, LowerBound, PreviewMode, Reinstall,
SourceStrategy, TrustedHost, Upgrade,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution::DistributionDatabase;
@ -89,6 +89,7 @@ pub(crate) async fn lock(
no_config: bool,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> anyhow::Result<ExitStatus> {
// Find the project requirements.
let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
@ -142,6 +143,7 @@ pub(crate) async fn lock(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -201,6 +203,7 @@ pub(super) async fn do_safe_lock(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<LockResult, ProjectError> {
match mode {
LockMode::Frozen => {
@ -231,6 +234,7 @@ pub(super) async fn do_safe_lock(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;
@ -270,6 +274,7 @@ pub(super) async fn do_safe_lock(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;
@ -300,6 +305,7 @@ async fn do_lock(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<LockResult, ProjectError> {
let start = std::time::Instant::now();
@ -502,6 +508,7 @@ async fn do_lock(
bounds,
sources,
concurrency,
preview,
);
let database = DistributionDatabase::new(&client, &build_dispatch, concurrency.downloads);

View file

@ -9,7 +9,7 @@ use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, ExtrasSpecification,
GroupsSpecification, LowerBound, Reinstall, TrustedHost, Upgrade,
GroupsSpecification, LowerBound, PreviewMode, Reinstall, TrustedHost, Upgrade,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution::DistributionDatabase;
@ -896,6 +896,7 @@ pub(crate) async fn resolve_names(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<Vec<Requirement>, uv_requirements::Error> {
// Partition the requirements into named and unnamed requirements.
let (mut requirements, unnamed): (Vec<_>, Vec<_>) =
@ -991,6 +992,7 @@ pub(crate) async fn resolve_names(
LowerBound::Allow,
*sources,
concurrency,
preview,
);
// Resolve the unnamed requirements.
@ -1045,6 +1047,7 @@ pub(crate) async fn resolve_environment<'a>(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ResolverOutput, ProjectError> {
warn_on_requirements_txt_setting(&spec.requirements, settings);
@ -1172,6 +1175,7 @@ pub(crate) async fn resolve_environment<'a>(
LowerBound::Allow,
sources,
concurrency,
preview,
);
// Resolve the requirements.
@ -1218,6 +1222,7 @@ pub(crate) async fn sync_environment(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<PythonEnvironment, ProjectError> {
let InstallerSettingsRef {
index_locations,
@ -1305,6 +1310,7 @@ pub(crate) async fn sync_environment(
LowerBound::Allow,
sources,
concurrency,
preview,
);
// Sync the environment.
@ -1370,6 +1376,7 @@ pub(crate) async fn update_environment(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<EnvironmentUpdate, ProjectError> {
warn_on_requirements_txt_setting(&spec, settings.as_ref().into());
@ -1510,6 +1517,7 @@ pub(crate) async fn update_environment(
LowerBound::Allow,
*sources,
concurrency,
preview,
);
// Resolve the requirements.

View file

@ -8,7 +8,7 @@ use uv_cache::Cache;
use uv_client::Connectivity;
use uv_configuration::{
Concurrency, DevGroupsManifest, EditableMode, ExtrasSpecification, InstallOptions, LowerBound,
TrustedHost,
PreviewMode, TrustedHost,
};
use uv_dispatch::SharedState;
use uv_fs::Simplified;
@ -54,6 +54,7 @@ pub(crate) async fn remove(
no_config: bool,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
let target = if let Some(script) = script {
// If we found a PEP 723 script and the user provided a project-only setting, warn.
@ -231,6 +232,7 @@ pub(crate) async fn remove(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -285,6 +287,7 @@ pub(crate) async fn remove(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{

View file

@ -18,7 +18,7 @@ use uv_cli::ExternalCommand;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{
Concurrency, DevGroupsSpecification, EditableMode, ExtrasSpecification, GroupsSpecification,
InstallOptions, LowerBound, SourceStrategy, TrustedHost,
InstallOptions, LowerBound, PreviewMode, SourceStrategy, TrustedHost,
};
use uv_dispatch::SharedState;
use uv_distribution::LoweredRequirement;
@ -86,6 +86,7 @@ pub(crate) async fn run(
printer: Printer,
env_file: Vec<PathBuf>,
no_env_file: bool,
preview: PreviewMode,
) -> anyhow::Result<ExitStatus> {
// These cases seem quite complex because (in theory) they should change the "current package".
// Let's ban them entirely for now.
@ -341,6 +342,7 @@ pub(crate) async fn run(
allow_insecure_host,
cache,
printer,
preview,
)
.await;
@ -662,6 +664,7 @@ pub(crate) async fn run(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -742,6 +745,7 @@ pub(crate) async fn run(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -893,6 +897,7 @@ pub(crate) async fn run(
allow_insecure_host,
cache,
printer,
preview,
)
.await;

View file

@ -10,7 +10,7 @@ use uv_cache::Cache;
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, Constraints, DevGroupsManifest, DevGroupsSpecification, EditableMode,
ExtrasSpecification, HashCheckingMode, InstallOptions, LowerBound, TrustedHost,
ExtrasSpecification, HashCheckingMode, InstallOptions, LowerBound, PreviewMode, TrustedHost,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution_types::{
@ -67,6 +67,7 @@ pub(crate) async fn sync(
no_config: bool,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
// Identify the project.
let project = if frozen {
@ -156,6 +157,7 @@ pub(crate) async fn sync(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -231,6 +233,7 @@ pub(crate) async fn sync(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{
@ -265,6 +268,7 @@ pub(super) async fn do_sync(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<(), ProjectError> {
// Use isolated state for universal resolution. When resolving, we don't enforce that the
// prioritized distributions match the current platform. So if we lock here, then try to
@ -425,6 +429,7 @@ pub(super) async fn do_sync(
bounds,
sources,
concurrency,
preview,
);
let site_packages = SitePackages::from_environment(venv)?;

View file

@ -8,7 +8,7 @@ use uv_cache::{Cache, Refresh};
use uv_cache_info::Timestamp;
use uv_client::{Connectivity, RegistryClientBuilder};
use uv_configuration::{
Concurrency, DevGroupsSpecification, LowerBound, TargetTriple, TrustedHost,
Concurrency, DevGroupsSpecification, LowerBound, PreviewMode, TargetTriple, TrustedHost,
};
use uv_dispatch::SharedState;
use uv_distribution_types::IndexCapabilities;
@ -58,6 +58,7 @@ pub(crate) async fn tree(
no_config: bool,
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
// Find the project requirements.
let workspace = Workspace::discover(project_dir, &DiscoveryOptions::default()).await?;
@ -121,6 +122,7 @@ pub(crate) async fn tree(
allow_insecure_host,
cache,
printer,
preview,
)
.await
{

View file

@ -8,7 +8,7 @@ use tracing::{debug, trace};
use uv_cache::{Cache, Refresh};
use uv_cache_info::Timestamp;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{Concurrency, Reinstall, TrustedHost, Upgrade};
use uv_configuration::{Concurrency, PreviewMode, Reinstall, TrustedHost, Upgrade};
use uv_dispatch::SharedState;
use uv_distribution_types::{NameRequirementSpecification, UnresolvedRequirementSpecification};
use uv_normalize::PackageName;
@ -61,6 +61,7 @@ pub(crate) async fn install(
allow_insecure_host: &[TrustedHost],
cache: Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
@ -128,6 +129,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await?
.pop()
@ -202,6 +204,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await?
.pop()
@ -266,6 +269,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await?,
);
@ -291,6 +295,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await?;
@ -422,6 +427,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await
{
@ -456,6 +462,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await
{
@ -490,6 +497,7 @@ pub(crate) async fn install(
allow_insecure_host,
&cache,
printer,
preview,
)
.await
.inspect_err(|_| {

View file

@ -14,7 +14,7 @@ use uv_cache::{Cache, Refresh};
use uv_cache_info::Timestamp;
use uv_cli::ExternalCommand;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{Concurrency, TrustedHost};
use uv_configuration::{Concurrency, PreviewMode, TrustedHost};
use uv_dispatch::SharedState;
use uv_distribution_types::{Name, UnresolvedRequirementSpecification};
use uv_installer::{SatisfiesResult, SitePackages};
@ -84,6 +84,7 @@ pub(crate) async fn run(
allow_insecure_host: &[TrustedHost],
cache: Cache,
printer: Printer,
preview: PreviewMode,
) -> anyhow::Result<ExitStatus> {
let Some(command) = command else {
// When a command isn't provided, we'll show a brief help including available tools
@ -128,6 +129,7 @@ pub(crate) async fn run(
allow_insecure_host,
&cache,
printer,
preview,
)
.await;
@ -446,6 +448,7 @@ async fn get_or_create_environment(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<(Requirement, PythonEnvironment), ProjectError> {
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
@ -529,6 +532,7 @@ async fn get_or_create_environment(
allow_insecure_host,
cache,
printer,
preview,
)
.await?
.pop()
@ -560,6 +564,7 @@ async fn get_or_create_environment(
allow_insecure_host,
cache,
printer,
preview,
)
.await?,
);
@ -638,6 +643,7 @@ async fn get_or_create_environment(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;

View file

@ -7,7 +7,7 @@ use tracing::debug;
use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{Concurrency, TrustedHost};
use uv_configuration::{Concurrency, PreviewMode, TrustedHost};
use uv_dispatch::SharedState;
use uv_fs::CWD;
use uv_normalize::PackageName;
@ -48,6 +48,7 @@ pub(crate) async fn upgrade(
allow_insecure_host: &[TrustedHost],
cache: &Cache,
printer: Printer,
preview: PreviewMode,
) -> Result<ExitStatus> {
let installed_tools = InstalledTools::from_settings()?.init()?;
let _lock = installed_tools.lock().await?;
@ -129,6 +130,7 @@ pub(crate) async fn upgrade(
concurrency,
native_tls,
allow_insecure_host,
preview,
)
.await;
@ -219,6 +221,7 @@ async fn upgrade_tool(
concurrency: Concurrency,
native_tls: bool,
allow_insecure_host: &[TrustedHost],
preview: PreviewMode,
) -> Result<UpgradeOutcome> {
// Ensure the tool is installed.
let existing_tool_receipt = match installed_tools.get_tool_receipt(name) {
@ -301,6 +304,7 @@ async fn upgrade_tool(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;
@ -319,6 +323,7 @@ async fn upgrade_tool(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;
@ -344,6 +349,7 @@ async fn upgrade_tool(
allow_insecure_host,
cache,
printer,
preview,
)
.await?;

View file

@ -13,7 +13,7 @@ use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, Constraints, IndexStrategy, KeyringProviderType,
LowerBound, NoBinary, NoBuild, SourceStrategy, TrustedHost,
LowerBound, NoBinary, NoBuild, PreviewMode, SourceStrategy, TrustedHost,
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution_types::{DependencyMetadata, Index, IndexLocations};
@ -65,6 +65,7 @@ pub(crate) async fn venv(
cache: &Cache,
printer: Printer,
relocatable: bool,
preview: PreviewMode,
) -> Result<ExitStatus> {
match venv_impl(
project_dir,
@ -92,6 +93,7 @@ pub(crate) async fn venv(
cache,
printer,
relocatable,
preview,
)
.await
{
@ -150,6 +152,7 @@ async fn venv_impl(
cache: &Cache,
printer: Printer,
relocatable: bool,
preview: PreviewMode,
) -> miette::Result<ExitStatus> {
let project = if no_project {
None
@ -333,6 +336,7 @@ async fn venv_impl(
LowerBound::Allow,
sources,
concurrency,
preview,
);
// Resolve the seed packages.

View file

@ -374,6 +374,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
globals.quiet,
cache,
printer,
globals.preview,
)
.await
}
@ -445,6 +446,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
cache,
args.dry_run,
printer,
globals.preview,
)
.await
}
@ -535,6 +537,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
cache,
args.dry_run,
printer,
globals.preview,
)
.await
}
@ -807,6 +810,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&cache,
printer,
args.relocatable,
globals.preview,
)
.await
}
@ -927,6 +931,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&globals.allow_insecure_host,
cache,
printer,
globals.preview,
)
.await
}
@ -991,6 +996,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&globals.allow_insecure_host,
cache,
printer,
globals.preview,
))
.await
}
@ -1037,6 +1043,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&globals.allow_insecure_host,
&cache,
printer,
globals.preview,
))
.await
}
@ -1375,6 +1382,7 @@ async fn run_project(
printer,
args.env_file,
args.no_env_file,
globals.preview,
))
.await
}
@ -1414,6 +1422,7 @@ async fn run_project(
no_config,
&cache,
printer,
globals.preview,
)
.await
}
@ -1445,6 +1454,7 @@ async fn run_project(
no_config,
&cache,
printer,
globals.preview,
)
.await
}
@ -1500,6 +1510,7 @@ async fn run_project(
no_config,
&cache,
printer,
globals.preview,
))
.await
}
@ -1544,6 +1555,7 @@ async fn run_project(
no_config,
&cache,
printer,
globals.preview,
))
.await
}
@ -1581,6 +1593,7 @@ async fn run_project(
no_config,
&cache,
printer,
globals.preview,
)
.await
}
@ -1620,6 +1633,7 @@ async fn run_project(
globals.quiet,
&cache,
printer,
globals.preview,
)
.await
}