Rename uv-traits and split into separate modules (#2674)

This is driving me a little crazy and is becoming a larger problem in
#2596 where I need to move more types (like `Upgrade` and `Reinstall`)
into this crate. Anything that's shared across our core resolver,
install, and build crates needs to be defined in this crate to avoid
cyclic dependencies. We've outgrown it being a single file with some
shared traits.

There are no behavioral changes here.
This commit is contained in:
Zanie Blue 2024-03-26 15:39:43 -05:00 committed by GitHub
parent 39769d82a0
commit 0b08ba1e67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 696 additions and 624 deletions

19
Cargo.lock generated
View file

@ -4361,7 +4361,7 @@ dependencies = [
"uv-normalize",
"uv-requirements",
"uv-resolver",
"uv-traits",
"uv-types",
"uv-virtualenv",
"uv-warnings",
]
@ -4411,7 +4411,7 @@ dependencies = [
"tracing",
"uv-fs",
"uv-interpreter",
"uv-traits",
"uv-types",
"uv-virtualenv",
]
@ -4533,7 +4533,7 @@ dependencies = [
"uv-interpreter",
"uv-normalize",
"uv-resolver",
"uv-traits",
"uv-types",
"walkdir",
]
@ -4554,7 +4554,7 @@ dependencies = [
"uv-installer",
"uv-interpreter",
"uv-resolver",
"uv-traits",
"uv-types",
]
[[package]]
@ -4590,7 +4590,7 @@ dependencies = [
"uv-fs",
"uv-git",
"uv-normalize",
"uv-traits",
"uv-types",
"zip",
]
@ -4684,7 +4684,7 @@ dependencies = [
"uv-fs",
"uv-interpreter",
"uv-normalize",
"uv-traits",
"uv-types",
"uv-warnings",
"walkdir",
]
@ -4757,7 +4757,7 @@ dependencies = [
"uv-fs",
"uv-normalize",
"uv-resolver",
"uv-traits",
"uv-types",
"uv-warnings",
]
@ -4801,18 +4801,19 @@ dependencies = [
"uv-distribution",
"uv-interpreter",
"uv-normalize",
"uv-traits",
"uv-types",
"uv-warnings",
]
[[package]]
name = "uv-traits"
name = "uv-types"
version = "0.0.1"
dependencies = [
"anyhow",
"distribution-types",
"once-map",
"pep508_rs",
"rustc-hash",
"serde",
"serde_json",
"uv-cache",

View file

@ -43,7 +43,7 @@ uv-interpreter = { path = "crates/uv-interpreter" }
uv-normalize = { path = "crates/uv-normalize" }
uv-requirements = { path = "crates/uv-requirements" }
uv-resolver = { path = "crates/uv-resolver" }
uv-traits = { path = "crates/uv-traits" }
uv-types = { path = "crates/uv-types" }
uv-trampoline = { path = "crates/uv-trampoline" }
uv-version = { path = "crates/uv-version" }
uv-virtualenv = { path = "crates/uv-virtualenv" }

View file

@ -66,7 +66,7 @@ Development utilities for uv.
## [uv-dispatch](./uv-dispatch)
A centralized `struct` for resolving and building source distributions in isolated environments.
Implements the traits defined in `uv-traits`.
Implements the traits defined in `uv-types`.
## [uv-distribution](./uv-distribution)
@ -109,7 +109,7 @@ Utilities for reading package requirements from `pyproject.toml` and `requiremen
Functionality for resolving Python packages and their dependencies.
## [uv-traits](./uv-traits)
## [uv-types](./uv-types)
Shared traits for uv, to avoid circular dependencies.

View file

@ -19,7 +19,7 @@ pep440_rs = { workspace = true }
pep508_rs = { workspace = true }
uv-fs = { workspace = true }
uv-interpreter = { workspace = true }
uv-traits = { workspace = true, features = ["serde"] }
uv-types = { workspace = true, features = ["serde"] }
uv-virtualenv = { workspace = true }
anyhow = { workspace = true }

View file

@ -30,7 +30,7 @@ use pep440_rs::{Version, VersionSpecifiers};
use pep508_rs::{PackageName, Requirement};
use uv_fs::{PythonExt, Simplified};
use uv_interpreter::{Interpreter, PythonEnvironment};
use uv_traits::{
use uv_types::{
BuildContext, BuildIsolation, BuildKind, ConfigSettings, SetupPyStrategy, SourceBuildTrait,
};

View file

@ -31,7 +31,7 @@ uv-installer = { workspace = true }
uv-interpreter = { workspace = true }
uv-normalize = { workspace = true }
uv-resolver = { workspace = true }
uv-traits = { workspace = true }
uv-types = { workspace = true }
# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace
# dependencies, to ensure that we're forced to think twice before including them in other crates.

View file

@ -14,7 +14,7 @@ use uv_dispatch::BuildDispatch;
use uv_installer::NoBinary;
use uv_interpreter::PythonEnvironment;
use uv_resolver::InMemoryIndex;
use uv_traits::{
use uv_types::{
BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy,
};

View file

@ -23,7 +23,7 @@ use uv_installer::{Downloader, NoBinary};
use uv_interpreter::PythonEnvironment;
use uv_normalize::PackageName;
use uv_resolver::{DistFinder, InMemoryIndex};
use uv_traits::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
#[derive(Parser)]
pub(crate) struct InstallManyArgs {

View file

@ -17,7 +17,7 @@ use uv_dispatch::BuildDispatch;
use uv_installer::NoBinary;
use uv_interpreter::PythonEnvironment;
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
use uv_traits::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
#[derive(ValueEnum, Default, Clone)]
pub(crate) enum ResolveCliFormat {

View file

@ -20,7 +20,7 @@ use uv_installer::NoBinary;
use uv_interpreter::PythonEnvironment;
use uv_normalize::PackageName;
use uv_resolver::InMemoryIndex;
use uv_traits::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
#[derive(Parser)]
pub(crate) struct ResolveManyArgs {

View file

@ -22,7 +22,7 @@ uv-client = { workspace = true }
uv-installer = { workspace = true }
uv-interpreter = { workspace = true }
uv-resolver = { workspace = true }
uv-traits = { workspace = true }
uv-types = { workspace = true }
anyhow = { workspace = true }
futures = { workspace = true }

View file

@ -20,7 +20,7 @@ use uv_client::{FlatIndex, RegistryClient};
use uv_installer::{Downloader, Installer, NoBinary, Plan, Planner, Reinstall, SitePackages};
use uv_interpreter::{Interpreter, PythonEnvironment};
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
use uv_traits::{
use uv_types::{
BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy,
};

View file

@ -26,7 +26,7 @@ uv-extract = { workspace = true }
uv-fs = { workspace = true, features = ["tokio"] }
uv-git = { workspace = true, features = ["vendored-openssl"] }
uv-normalize = { workspace = true }
uv-traits = { workspace = true }
uv-types = { workspace = true }
pypi-types = { workspace = true }
anyhow = { workspace = true }

View file

@ -19,7 +19,7 @@ use pypi_types::Metadata23;
use uv_cache::{ArchiveTarget, ArchiveTimestamp, Cache, CacheBucket, CacheEntry, WheelCache};
use uv_client::{CacheControl, CachedClientError, Connectivity, RegistryClient};
use uv_git::GitSource;
use uv_traits::{BuildContext, NoBinary, NoBuild};
use uv_types::{BuildContext, NoBinary, NoBuild};
use crate::download::{BuiltWheel, UnzippedWheel};
use crate::locks::Locks;

View file

@ -33,7 +33,7 @@ use uv_client::{
};
use uv_fs::{write_atomic, LockedFile};
use uv_git::{Fetch, GitSource};
use uv_traits::{BuildContext, BuildKind, NoBuild, SourceBuildTrait};
use uv_types::{BuildContext, BuildKind, NoBuild, SourceBuildTrait};
use crate::error::Error;
use crate::reporter::Facade;

View file

@ -27,7 +27,7 @@ uv-extract = { workspace = true }
uv-fs = { workspace = true }
uv-interpreter = { workspace = true }
uv-normalize = { workspace = true }
uv-traits = { workspace = true }
uv-types = { workspace = true }
uv-warnings = { workspace = true }
anyhow = { workspace = true }

View file

@ -15,7 +15,7 @@ use platform_tags::Tags;
use uv_cache::Cache;
use uv_client::RegistryClient;
use uv_distribution::{DistributionDatabase, LocalWheel, Unzip};
use uv_traits::{BuildContext, InFlight};
use uv_types::{BuildContext, InFlight};
use crate::editable::BuiltEditable;

View file

@ -5,7 +5,7 @@ pub use installer::{Installer, Reporter as InstallReporter};
pub use plan::{Plan, Planner, Reinstall};
pub use site_packages::{Diagnostic, SitePackages};
pub use uninstall::{uninstall, UninstallError};
pub use uv_traits::NoBinary;
pub use uv_types::NoBinary;
mod compile;
mod downloader;

View file

@ -17,7 +17,7 @@ use uv_distribution::{BuiltWheelIndex, RegistryWheelIndex};
use uv_fs::Simplified;
use uv_interpreter::PythonEnvironment;
use uv_normalize::PackageName;
use uv_traits::NoBinary;
use uv_types::NoBinary;
use crate::{ResolvedEditable, SitePackages};

View file

@ -21,7 +21,7 @@ uv-distribution = { workspace = true }
uv-fs = { workspace = true }
uv-normalize = { workspace = true }
uv-resolver = { workspace = true, features = ["clap"] }
uv-traits = { workspace = true }
uv-types = { workspace = true }
uv-warnings = { workspace = true }
anyhow = { workspace = true }

View file

@ -20,7 +20,7 @@ use pypi_types::Metadata10;
use uv_client::RegistryClient;
use uv_distribution::{Reporter, SourceDistCachedBuilder};
use uv_normalize::PackageName;
use uv_traits::BuildContext;
use uv_types::BuildContext;
/// Like [`RequirementsSpecification`], but with concrete names for all requirements.
pub struct NamedRequirementsResolver {

View file

@ -10,7 +10,7 @@ use distribution_types::{BuildableSource, PathSourceUrl, SourceUrl};
use pep508_rs::Requirement;
use uv_client::RegistryClient;
use uv_distribution::{Reporter, SourceDistCachedBuilder};
use uv_traits::BuildContext;
use uv_types::BuildContext;
use crate::ExtrasSpecification;

View file

@ -1,48 +1,11 @@
use std::path::Path;
use anyhow::Result;
use rustc_hash::FxHashSet;
use requirements_txt::RequirementsTxt;
use uv_client::{BaseClientBuilder, Connectivity};
use uv_normalize::PackageName;
use uv_resolver::{Preference, PreferenceError};
/// Whether to allow package upgrades.
#[derive(Debug)]
pub enum Upgrade {
/// Prefer pinned versions from the existing lockfile, if possible.
None,
/// Allow package upgrades for all packages, ignoring the existing lockfile.
All,
/// Allow package upgrades, but only for the specified packages.
Packages(FxHashSet<PackageName>),
}
impl Upgrade {
/// Determine the upgrade strategy from the command-line arguments.
pub fn from_args(upgrade: bool, upgrade_package: Vec<PackageName>) -> Self {
if upgrade {
Self::All
} else if !upgrade_package.is_empty() {
Self::Packages(upgrade_package.into_iter().collect())
} else {
Self::None
}
}
/// Returns `true` if no packages should be upgraded.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
/// Returns `true` if all packages should be upgraded.
pub fn is_all(&self) -> bool {
matches!(self, Self::All)
}
}
use uv_types::Upgrade;
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
pub async fn read_lockfile(

View file

@ -27,7 +27,7 @@ uv-client = { workspace = true }
uv-distribution = { workspace = true }
uv-interpreter = { workspace = true }
uv-normalize = { workspace = true }
uv-traits = { workspace = true }
uv-types = { workspace = true }
uv-warnings = { workspace = true }
anstream = { workspace = true }

View file

@ -5,7 +5,7 @@
use anyhow::Result;
use futures::{stream, Stream, StreamExt, TryStreamExt};
use rustc_hash::FxHashMap;
use uv_traits::{NoBinary, NoBuild};
use uv_types::{NoBinary, NoBuild};
use distribution_filename::DistFilename;
use distribution_types::{Dist, IndexUrl, Resolution};

View file

@ -31,7 +31,7 @@ use uv_client::{FlatIndex, RegistryClient};
use uv_distribution::DistributionDatabase;
use uv_interpreter::Interpreter;
use uv_normalize::PackageName;
use uv_traits::BuildContext;
use uv_types::BuildContext;
use crate::candidate_selector::{CandidateDist, CandidateSelector};
use crate::constraints::Constraints;

View file

@ -10,7 +10,7 @@ use pypi_types::Metadata23;
use uv_client::{FlatIndex, RegistryClient};
use uv_distribution::DistributionDatabase;
use uv_normalize::PackageName;
use uv_traits::{BuildContext, NoBinary, NoBuild};
use uv_types::{BuildContext, NoBinary, NoBuild};
use crate::python_requirement::PythonRequirement;
use crate::version_map::VersionMap;

View file

@ -16,7 +16,7 @@ use pypi_types::{Hashes, Yanked};
use rkyv::{de::deserializers::SharedDeserializeMap, Deserialize};
use uv_client::{FlatDistributions, OwnedArchive, SimpleMetadata, VersionFiles};
use uv_normalize::PackageName;
use uv_traits::{NoBinary, NoBuild};
use uv_types::{NoBinary, NoBuild};
use uv_warnings::warn_user_once;
use crate::{python_requirement::PythonRequirement, yanks::AllowedYanks};

View file

@ -20,7 +20,7 @@ use uv_resolver::{
DisplayResolutionGraph, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode,
Preference, ResolutionGraph, ResolutionMode, Resolver,
};
use uv_traits::{
use uv_types::{
BuildContext, BuildIsolation, BuildKind, NoBinary, NoBuild, SetupPyStrategy, SourceBuildTrait,
};

View file

@ -1,535 +0,0 @@
//! Avoid cyclic crate dependencies between resolver, installer and builder.
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
use std::future::Future;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use anyhow::Result;
use distribution_types::{CachedDist, DistributionId, IndexLocations, Resolution, SourceDist};
use once_map::OnceMap;
use pep508_rs::Requirement;
use uv_cache::Cache;
use uv_interpreter::{Interpreter, PythonEnvironment};
use uv_normalize::PackageName;
/// Avoid cyclic crate dependencies between resolver, installer and builder.
///
/// To resolve the dependencies of a packages, we may need to build one or more source
/// distributions. To building a source distribution, we need to create a virtual environment from
/// the same base python as we use for the root resolution, resolve the build requirements
/// (potentially which nested source distributions, recursing a level deeper), installing
/// them and then build. The installer, the resolver and the source distribution builder are each in
/// their own crate. To avoid circular crate dependencies, this type dispatches between the three
/// crates with its three main methods ([`BuildContext::resolve`], [`BuildContext::install`] and
/// [`BuildContext::setup_build`]).
///
/// The overall main crate structure looks like this:
///
/// ```text
/// ┌────────────────┐
/// │ uv │
/// └───────▲────────┘
/// │
/// │
/// ┌───────┴────────┐
/// ┌─────────►│ uv-dispatch │◄─────────┐
/// │ └───────▲────────┘ │
/// │ │ │
/// │ │ │
/// ┌───────┴────────┐ ┌───────┴────────┐ ┌────────┴───────┐
/// │ uv-resolver │ │ uv-installer │ │ uv-build │
/// └───────▲────────┘ └───────▲────────┘ └────────▲───────┘
/// │ │ │
/// └─────────────┐ │ ┌──────────────┘
/// ┌──┴────┴────┴───┐
/// │ uv-traits │
/// └────────────────┘
/// ```
///
/// Put in a different way, this trait allows `uv-resolver` to depend on `uv-build` and
/// `uv-build` to depend on `uv-resolver` which having actual crate dependencies between
/// them.
pub trait BuildContext: Sync {
type SourceDistBuilder: SourceBuildTrait + Send + Sync;
/// Return a reference to the cache.
fn cache(&self) -> &Cache;
/// All (potentially nested) source distribution builds use the same base python and can reuse
/// it's metadata (e.g. wheel compatibility tags).
fn interpreter(&self) -> &Interpreter;
/// Whether to enforce build isolation when building source distributions.
fn build_isolation(&self) -> BuildIsolation;
/// Whether source distribution building is disabled. This [`BuildContext::setup_build`] calls
/// will fail in this case. This method exists to avoid fetching source distributions if we know
/// we can't build them
fn no_build(&self) -> &NoBuild;
/// Whether using pre-built wheels is disabled.
fn no_binary(&self) -> &NoBinary;
/// The index locations being searched.
fn index_locations(&self) -> &IndexLocations;
/// The strategy to use when building source distributions that lack a `pyproject.toml`.
fn setup_py_strategy(&self) -> SetupPyStrategy;
/// Resolve the given requirements into a ready-to-install set of package versions.
fn resolve<'a>(
&'a self,
requirements: &'a [Requirement],
) -> impl Future<Output = Result<Resolution>> + Send + 'a;
/// Install the given set of package versions into the virtual environment. The environment must
/// use the same base Python as [`BuildContext::interpreter`]
fn install<'a>(
&'a self,
resolution: &'a Resolution,
venv: &'a PythonEnvironment,
) -> impl Future<Output = Result<()>> + Send + 'a;
/// Setup a source distribution build by installing the required dependencies. A wrapper for
/// `uv_build::SourceBuild::setup`.
///
/// For PEP 517 builds, this calls `get_requires_for_build_wheel`.
///
/// `package_id` is for error reporting only.
/// `dist` is for safety checks and may be null for editable builds.
fn setup_build<'a>(
&'a self,
source: &'a Path,
subdirectory: Option<&'a Path>,
package_id: &'a str,
dist: Option<&'a SourceDist>,
build_kind: BuildKind,
) -> impl Future<Output = Result<Self::SourceDistBuilder>> + Send + 'a;
}
/// A wrapper for `uv_build::SourceBuild` to avoid cyclical crate dependencies.
///
/// You can either call only `wheel()` to build the wheel directly, call only `metadata()` to get
/// the metadata without performing the actual or first call `metadata()` and then `wheel()`.
pub trait SourceBuildTrait {
/// A wrapper for `uv_build::SourceBuild::get_metadata_without_build`.
///
/// For PEP 517 builds, this calls `prepare_metadata_for_build_wheel`
///
/// Returns the metadata directory if we're having a PEP 517 build and the
/// `prepare_metadata_for_build_wheel` hook exists
fn metadata(&mut self) -> impl Future<Output = Result<Option<PathBuf>>> + Send;
/// A wrapper for `uv_build::SourceBuild::build`.
///
/// For PEP 517 builds, this calls `build_wheel`.
///
/// Returns the filename of the built wheel inside the given `wheel_dir`.
fn wheel<'a>(&'a self, wheel_dir: &'a Path)
-> impl Future<Output = Result<String>> + Send + 'a;
}
#[derive(Default)]
pub struct InFlight {
/// The in-flight distribution downloads.
pub downloads: OnceMap<DistributionId, Result<CachedDist, String>>,
}
/// Whether to enforce build isolation when building source distributions.
#[derive(Debug, Copy, Clone)]
pub enum BuildIsolation<'a> {
Isolated,
Shared(&'a PythonEnvironment),
}
impl<'a> BuildIsolation<'a> {
/// Returns `true` if build isolation is enforced.
pub fn is_isolated(&self) -> bool {
matches!(self, Self::Isolated)
}
}
/// The strategy to use when building source distributions that lack a `pyproject.toml`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum SetupPyStrategy {
/// Perform a PEP 517 build.
#[default]
Pep517,
/// Perform a build by invoking `setuptools` directly.
Setuptools,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum BuildKind {
/// A regular PEP 517 wheel build
#[default]
Wheel,
/// A PEP 660 editable installation wheel build
Editable,
}
impl Display for BuildKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Wheel => f.write_str("wheel"),
Self::Editable => f.write_str("editable"),
}
}
}
#[derive(Debug, Clone)]
pub enum PackageNameSpecifier {
All,
None,
Package(PackageName),
}
impl FromStr for PackageNameSpecifier {
type Err = uv_normalize::InvalidNameError;
fn from_str(name: &str) -> Result<Self, Self::Err> {
match name {
":all:" => Ok(Self::All),
":none:" => Ok(Self::None),
_ => Ok(Self::Package(PackageName::from_str(name)?)),
}
}
}
#[derive(Debug, Clone)]
pub enum PackageNameSpecifiers {
All,
None,
Packages(Vec<PackageName>),
}
impl PackageNameSpecifiers {
fn from_iter(specifiers: impl Iterator<Item = PackageNameSpecifier>) -> Self {
let mut packages = Vec::new();
let mut all: bool = false;
for specifier in specifiers {
match specifier {
PackageNameSpecifier::None => {
packages.clear();
all = false;
}
PackageNameSpecifier::All => {
all = true;
}
PackageNameSpecifier::Package(name) => {
packages.push(name);
}
}
}
if all {
Self::All
} else if packages.is_empty() {
Self::None
} else {
Self::Packages(packages)
}
}
}
#[derive(Debug, Clone)]
pub enum NoBinary {
/// Allow installation of any wheel.
None,
/// Do not allow installation from any wheels.
All,
/// Do not allow installation from the specific wheels.
Packages(Vec<PackageName>),
}
impl NoBinary {
/// Determine the binary installation strategy to use.
pub fn from_args(no_binary: Vec<PackageNameSpecifier>) -> Self {
let combined = PackageNameSpecifiers::from_iter(no_binary.into_iter());
match combined {
PackageNameSpecifiers::All => Self::All,
PackageNameSpecifiers::None => Self::None,
PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
}
}
}
impl NoBinary {
/// Returns `true` if all wheels are allowed.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum NoBuild {
/// Allow building wheels from any source distribution.
None,
/// Do not allow building wheels from any source distribution.
All,
/// Do not allow building wheels from the given package's source distributions.
Packages(Vec<PackageName>),
}
impl NoBuild {
/// Determine the build strategy to use.
pub fn from_args(only_binary: Vec<PackageNameSpecifier>, no_build: bool) -> Self {
if no_build {
Self::All
} else {
let combined = PackageNameSpecifiers::from_iter(only_binary.into_iter());
match combined {
PackageNameSpecifiers::All => Self::All,
PackageNameSpecifiers::None => Self::None,
PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
}
}
}
}
impl NoBuild {
/// Returns `true` if all builds are allowed.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
#[derive(Debug, Clone)]
pub struct ConfigSettingEntry {
/// The key of the setting. For example, given `key=value`, this would be `key`.
key: String,
/// The value of the setting. For example, given `key=value`, this would be `value`.
value: String,
}
impl FromStr for ConfigSettingEntry {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some((key, value)) = s.split_once('=') else {
return Err(anyhow::anyhow!(
"Invalid config setting: {s} (expected `KEY=VALUE`)"
));
};
Ok(Self {
key: key.trim().to_string(),
value: value.trim().to_string(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum ConfigSettingValue {
/// The value consists of a single string.
String(String),
/// The value consists of a list of strings.
List(Vec<String>),
}
/// Settings to pass to a PEP 517 build backend, structured as a map from (string) key to string or
/// list of strings.
///
/// See: <https://peps.python.org/pep-0517/#config-settings>
#[derive(Debug, Default, Clone)]
pub struct ConfigSettings(BTreeMap<String, ConfigSettingValue>);
impl FromIterator<ConfigSettingEntry> for ConfigSettings {
fn from_iter<T: IntoIterator<Item = ConfigSettingEntry>>(iter: T) -> Self {
let mut config = BTreeMap::default();
for entry in iter {
match config.entry(entry.key) {
Entry::Vacant(vacant) => {
vacant.insert(ConfigSettingValue::String(entry.value));
}
Entry::Occupied(mut occupied) => match occupied.get_mut() {
ConfigSettingValue::String(existing) => {
let existing = existing.clone();
occupied.insert(ConfigSettingValue::List(vec![existing, entry.value]));
}
ConfigSettingValue::List(existing) => {
existing.push(entry.value);
}
},
}
}
Self(config)
}
}
#[cfg(feature = "serde")]
impl ConfigSettings {
/// Convert the settings to a string that can be passed directly to a PEP 517 build backend.
pub fn escape_for_python(&self) -> String {
serde_json::to_string(self).expect("Failed to serialize config settings")
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for ConfigSettings {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (key, value) in &self.0 {
match value {
ConfigSettingValue::String(value) => {
map.serialize_entry(&key, &value)?;
}
ConfigSettingValue::List(values) => {
map.serialize_entry(&key, &values)?;
}
}
}
map.end()
}
}
#[cfg(test)]
mod tests {
use anyhow::Error;
use super::*;
#[test]
fn no_build_from_args() -> Result<(), Error> {
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":all:")?], false),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":all:")?], true),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":none:")?], true),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":none:")?], false),
NoBuild::None,
);
assert_eq!(
NoBuild::from_args(
vec![
PackageNameSpecifier::from_str("foo")?,
PackageNameSpecifier::from_str("bar")?
],
false
),
NoBuild::Packages(vec![
PackageName::from_str("foo")?,
PackageName::from_str("bar")?
]),
);
assert_eq!(
NoBuild::from_args(
vec![
PackageNameSpecifier::from_str("test")?,
PackageNameSpecifier::All
],
false
),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(
vec![
PackageNameSpecifier::from_str("foo")?,
PackageNameSpecifier::from_str(":none:")?,
PackageNameSpecifier::from_str("bar")?
],
false
),
NoBuild::Packages(vec![PackageName::from_str("bar")?]),
);
Ok(())
}
#[test]
fn collect_config_settings() {
let settings: ConfigSettings = vec![
ConfigSettingEntry {
key: "key".to_string(),
value: "value".to_string(),
},
ConfigSettingEntry {
key: "key".to_string(),
value: "value2".to_string(),
},
ConfigSettingEntry {
key: "list".to_string(),
value: "value3".to_string(),
},
ConfigSettingEntry {
key: "list".to_string(),
value: "value4".to_string(),
},
]
.into_iter()
.collect();
assert_eq!(
settings.0.get("key"),
Some(&ConfigSettingValue::List(vec![
"value".to_string(),
"value2".to_string()
]))
);
assert_eq!(
settings.0.get("list"),
Some(&ConfigSettingValue::List(vec![
"value3".to_string(),
"value4".to_string()
]))
);
}
#[test]
#[cfg(feature = "serde")]
fn escape_for_python() {
let mut settings = ConfigSettings::default();
settings.0.insert(
"key".to_string(),
ConfigSettingValue::String("value".to_string()),
);
settings.0.insert(
"list".to_string(),
ConfigSettingValue::List(vec!["value1".to_string(), "value2".to_string()]),
);
assert_eq!(
settings.escape_for_python(),
r#"{"key":"value","list":["value1","value2"]}"#
);
let mut settings = ConfigSettings::default();
settings.0.insert(
"key".to_string(),
ConfigSettingValue::String("Hello, \"world!\"".to_string()),
);
settings.0.insert(
"list".to_string(),
ConfigSettingValue::List(vec!["'value1'".to_string()]),
);
assert_eq!(
settings.escape_for_python(),
r#"{"key":"Hello, \"world!\"","list":["'value1'"]}"#
);
let mut settings = ConfigSettings::default();
settings.0.insert(
"key".to_string(),
ConfigSettingValue::String("val\\1 {}ue".to_string()),
);
assert_eq!(settings.escape_for_python(), r#"{"key":"val\\1 {}ue"}"#);
}
}

View file

@ -1,5 +1,5 @@
[package]
name = "uv-traits"
name = "uv-types"
version = "0.0.1"
edition = { workspace = true }
rust-version = { workspace = true }
@ -21,6 +21,7 @@ uv-interpreter = { workspace = true }
uv-normalize = { workspace = true }
anyhow = { workspace = true }
rustc-hash = { workspace = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }

View file

@ -0,0 +1,179 @@
use std::fmt::{Display, Formatter};
use pep508_rs::PackageName;
use uv_interpreter::PythonEnvironment;
use crate::{PackageNameSpecifier, PackageNameSpecifiers};
/// Whether to enforce build isolation when building source distributions.
#[derive(Debug, Copy, Clone)]
pub enum BuildIsolation<'a> {
Isolated,
Shared(&'a PythonEnvironment),
}
impl<'a> BuildIsolation<'a> {
/// Returns `true` if build isolation is enforced.
pub fn is_isolated(&self) -> bool {
matches!(self, Self::Isolated)
}
}
/// The strategy to use when building source distributions that lack a `pyproject.toml`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum SetupPyStrategy {
/// Perform a PEP 517 build.
#[default]
Pep517,
/// Perform a build by invoking `setuptools` directly.
Setuptools,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum BuildKind {
/// A regular PEP 517 wheel build
#[default]
Wheel,
/// A PEP 660 editable installation wheel build
Editable,
}
impl Display for BuildKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Wheel => f.write_str("wheel"),
Self::Editable => f.write_str("editable"),
}
}
}
#[derive(Debug, Clone)]
pub enum NoBinary {
/// Allow installation of any wheel.
None,
/// Do not allow installation from any wheels.
All,
/// Do not allow installation from the specific wheels.
Packages(Vec<PackageName>),
}
impl NoBinary {
/// Determine the binary installation strategy to use.
pub fn from_args(no_binary: Vec<PackageNameSpecifier>) -> Self {
let combined = PackageNameSpecifiers::from_iter(no_binary.into_iter());
match combined {
PackageNameSpecifiers::All => Self::All,
PackageNameSpecifiers::None => Self::None,
PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
}
}
}
impl NoBinary {
/// Returns `true` if all wheels are allowed.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum NoBuild {
/// Allow building wheels from any source distribution.
None,
/// Do not allow building wheels from any source distribution.
All,
/// Do not allow building wheels from the given package's source distributions.
Packages(Vec<PackageName>),
}
impl NoBuild {
/// Determine the build strategy to use.
pub fn from_args(only_binary: Vec<PackageNameSpecifier>, no_build: bool) -> Self {
if no_build {
Self::All
} else {
let combined = PackageNameSpecifiers::from_iter(only_binary.into_iter());
match combined {
PackageNameSpecifiers::All => Self::All,
PackageNameSpecifiers::None => Self::None,
PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
}
}
}
}
impl NoBuild {
/// Returns `true` if all builds are allowed.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use anyhow::Error;
use super::*;
#[test]
fn no_build_from_args() -> Result<(), Error> {
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":all:")?], false),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":all:")?], true),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":none:")?], true),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(vec![PackageNameSpecifier::from_str(":none:")?], false),
NoBuild::None,
);
assert_eq!(
NoBuild::from_args(
vec![
PackageNameSpecifier::from_str("foo")?,
PackageNameSpecifier::from_str("bar")?
],
false
),
NoBuild::Packages(vec![
PackageName::from_str("foo")?,
PackageName::from_str("bar")?
]),
);
assert_eq!(
NoBuild::from_args(
vec![
PackageNameSpecifier::from_str("test")?,
PackageNameSpecifier::All
],
false
),
NoBuild::All,
);
assert_eq!(
NoBuild::from_args(
vec![
PackageNameSpecifier::from_str("foo")?,
PackageNameSpecifier::from_str(":none:")?,
PackageNameSpecifier::from_str("bar")?
],
false
),
NoBuild::Packages(vec![PackageName::from_str("bar")?]),
);
Ok(())
}
}

View file

@ -0,0 +1,176 @@
use std::{
collections::{btree_map::Entry, BTreeMap},
str::FromStr,
};
#[derive(Debug, Clone)]
pub struct ConfigSettingEntry {
/// The key of the setting. For example, given `key=value`, this would be `key`.
key: String,
/// The value of the setting. For example, given `key=value`, this would be `value`.
value: String,
}
impl FromStr for ConfigSettingEntry {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some((key, value)) = s.split_once('=') else {
return Err(anyhow::anyhow!(
"Invalid config setting: {s} (expected `KEY=VALUE`)"
));
};
Ok(Self {
key: key.trim().to_string(),
value: value.trim().to_string(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum ConfigSettingValue {
/// The value consists of a single string.
String(String),
/// The value consists of a list of strings.
List(Vec<String>),
}
/// Settings to pass to a PEP 517 build backend, structured as a map from (string) key to string or
/// list of strings.
///
/// See: <https://peps.python.org/pep-0517/#config-settings>
#[derive(Debug, Default, Clone)]
pub struct ConfigSettings(BTreeMap<String, ConfigSettingValue>);
impl FromIterator<ConfigSettingEntry> for ConfigSettings {
fn from_iter<T: IntoIterator<Item = ConfigSettingEntry>>(iter: T) -> Self {
let mut config = BTreeMap::default();
for entry in iter {
match config.entry(entry.key) {
Entry::Vacant(vacant) => {
vacant.insert(ConfigSettingValue::String(entry.value));
}
Entry::Occupied(mut occupied) => match occupied.get_mut() {
ConfigSettingValue::String(existing) => {
let existing = existing.clone();
occupied.insert(ConfigSettingValue::List(vec![existing, entry.value]));
}
ConfigSettingValue::List(existing) => {
existing.push(entry.value);
}
},
}
}
Self(config)
}
}
#[cfg(feature = "serde")]
impl ConfigSettings {
/// Convert the settings to a string that can be passed directly to a PEP 517 build backend.
pub fn escape_for_python(&self) -> String {
serde_json::to_string(self).expect("Failed to serialize config settings")
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for ConfigSettings {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (key, value) in &self.0 {
match value {
ConfigSettingValue::String(value) => {
map.serialize_entry(&key, &value)?;
}
ConfigSettingValue::List(values) => {
map.serialize_entry(&key, &values)?;
}
}
}
map.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collect_config_settings() {
let settings: ConfigSettings = vec![
ConfigSettingEntry {
key: "key".to_string(),
value: "value".to_string(),
},
ConfigSettingEntry {
key: "key".to_string(),
value: "value2".to_string(),
},
ConfigSettingEntry {
key: "list".to_string(),
value: "value3".to_string(),
},
ConfigSettingEntry {
key: "list".to_string(),
value: "value4".to_string(),
},
]
.into_iter()
.collect();
assert_eq!(
settings.0.get("key"),
Some(&ConfigSettingValue::List(vec![
"value".to_string(),
"value2".to_string()
]))
);
assert_eq!(
settings.0.get("list"),
Some(&ConfigSettingValue::List(vec![
"value3".to_string(),
"value4".to_string()
]))
);
}
#[test]
#[cfg(feature = "serde")]
fn escape_for_python() {
let mut settings = ConfigSettings::default();
settings.0.insert(
"key".to_string(),
ConfigSettingValue::String("value".to_string()),
);
settings.0.insert(
"list".to_string(),
ConfigSettingValue::List(vec!["value1".to_string(), "value2".to_string()]),
);
assert_eq!(
settings.escape_for_python(),
r#"{"key":"value","list":["value1","value2"]}"#
);
let mut settings = ConfigSettings::default();
settings.0.insert(
"key".to_string(),
ConfigSettingValue::String("Hello, \"world!\"".to_string()),
);
settings.0.insert(
"list".to_string(),
ConfigSettingValue::List(vec!["'value1'".to_string()]),
);
assert_eq!(
settings.escape_for_python(),
r#"{"key":"Hello, \"world!\"","list":["'value1'"]}"#
);
let mut settings = ConfigSettings::default();
settings.0.insert(
"key".to_string(),
ConfigSettingValue::String("val\\1 {}ue".to_string()),
);
assert_eq!(settings.escape_for_python(), r#"{"key":"val\\1 {}ue"}"#);
}
}

View file

@ -0,0 +1,8 @@
use distribution_types::{CachedDist, DistributionId};
use once_map::OnceMap;
#[derive(Default)]
pub struct InFlight {
/// The in-flight distribution downloads.
pub downloads: OnceMap<DistributionId, Result<CachedDist, String>>,
}

View file

@ -0,0 +1,14 @@
//! Fundamental types shared across `uv` crates.
pub use build_options::*;
pub use config_settings::*;
pub use downloads::*;
pub use name_specifiers::*;
pub use package_options::*;
pub use traits::*;
mod build_options;
mod config_settings;
mod downloads;
mod name_specifiers;
mod package_options;
mod traits;

View file

@ -0,0 +1,63 @@
use std::str::FromStr;
use pep508_rs::PackageName;
#[derive(Debug, Clone)]
pub enum PackageNameSpecifier {
All,
None,
Package(PackageName),
}
impl FromStr for PackageNameSpecifier {
type Err = uv_normalize::InvalidNameError;
fn from_str(name: &str) -> Result<Self, Self::Err> {
match name {
":all:" => Ok(Self::All),
":none:" => Ok(Self::None),
_ => Ok(Self::Package(PackageName::from_str(name)?)),
}
}
}
/// Package name specification.
///
/// Consumes both package names and selection directives for compatibility with pip flags
/// such as `--no-binary`.
#[derive(Debug, Clone)]
pub enum PackageNameSpecifiers {
All,
None,
Packages(Vec<PackageName>),
}
impl PackageNameSpecifiers {
pub(crate) fn from_iter(specifiers: impl Iterator<Item = PackageNameSpecifier>) -> Self {
let mut packages = Vec::new();
let mut all: bool = false;
for specifier in specifiers {
match specifier {
PackageNameSpecifier::None => {
packages.clear();
all = false;
}
PackageNameSpecifier::All => {
all = true;
}
PackageNameSpecifier::Package(name) => {
packages.push(name);
}
}
}
if all {
Self::All
} else if packages.is_empty() {
Self::None
} else {
Self::Packages(packages)
}
}
}

View file

@ -0,0 +1,74 @@
use pep508_rs::PackageName;
use rustc_hash::FxHashSet;
/// Whether to reinstall packages.
#[derive(Debug, Clone)]
pub enum Reinstall {
/// Don't reinstall any packages; respect the existing installation.
None,
/// Reinstall all packages in the plan.
All,
/// Reinstall only the specified packages.
Packages(Vec<PackageName>),
}
impl Reinstall {
/// Determine the reinstall strategy to use.
pub fn from_args(reinstall: bool, reinstall_package: Vec<PackageName>) -> Self {
if reinstall {
Self::All
} else if !reinstall_package.is_empty() {
Self::Packages(reinstall_package)
} else {
Self::None
}
}
/// Returns `true` if no packages should be reinstalled.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
/// Returns `true` if all packages should be reinstalled.
pub fn is_all(&self) -> bool {
matches!(self, Self::All)
}
}
/// Whether to allow package upgrades.
#[derive(Debug)]
pub enum Upgrade {
/// Prefer pinned versions from the existing lockfile, if possible.
None,
/// Allow package upgrades for all packages, ignoring the existing lockfile.
All,
/// Allow package upgrades, but only for the specified packages.
Packages(FxHashSet<PackageName>),
}
impl Upgrade {
/// Determine the upgrade strategy from the command-line arguments.
pub fn from_args(upgrade: bool, upgrade_package: Vec<PackageName>) -> Self {
if upgrade {
Self::All
} else if !upgrade_package.is_empty() {
Self::Packages(upgrade_package.into_iter().collect())
} else {
Self::None
}
}
/// Returns `true` if no packages should be upgraded.
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
/// Returns `true` if all packages should be upgraded.
pub fn is_all(&self) -> bool {
matches!(self, Self::All)
}
}

View file

@ -0,0 +1,129 @@
use std::future::Future;
use std::path::{Path, PathBuf};
use anyhow::Result;
use distribution_types::{IndexLocations, Resolution, SourceDist};
use pep508_rs::Requirement;
use uv_cache::Cache;
use uv_interpreter::{Interpreter, PythonEnvironment};
use crate::{BuildIsolation, BuildKind, NoBinary, NoBuild, SetupPyStrategy};
/// Avoids cyclic crate dependencies between resolver, installer and builder.
///
/// To resolve the dependencies of a packages, we may need to build one or more source
/// distributions. To building a source distribution, we need to create a virtual environment from
/// the same base python as we use for the root resolution, resolve the build requirements
/// (potentially which nested source distributions, recursing a level deeper), installing
/// them and then build. The installer, the resolver and the source distribution builder are each in
/// their own crate. To avoid circular crate dependencies, this type dispatches between the three
/// crates with its three main methods ([`BuildContext::resolve`], [`BuildContext::install`] and
/// [`BuildContext::setup_build`]).
///
/// The overall main crate structure looks like this:
///
/// ```text
/// ┌────────────────┐
/// │ uv │
/// └───────▲────────┘
/// │
/// │
/// ┌───────┴────────┐
/// ┌─────────►│ uv-dispatch │◄─────────┐
/// │ └───────▲────────┘ │
/// │ │ │
/// │ │ │
/// ┌───────┴────────┐ ┌───────┴────────┐ ┌────────┴───────┐
/// │ uv-resolver │ │ uv-installer │ │ uv-build │
/// └───────▲────────┘ └───────▲────────┘ └────────▲───────┘
/// │ │ │
/// └─────────────┐ │ ┌──────────────┘
/// ┌──┴────┴────┴───┐
/// │ uv-types │
/// └────────────────┘
/// ```
///
/// Put in a different way, the types here allow `uv-resolver` to depend on `uv-build` and
/// `uv-build` to depend on `uv-resolver` which having actual crate dependencies between
/// them.
pub trait BuildContext: Sync {
type SourceDistBuilder: SourceBuildTrait + Send + Sync;
/// Return a reference to the cache.
fn cache(&self) -> &Cache;
/// All (potentially nested) source distribution builds use the same base python and can reuse
/// it's metadata (e.g. wheel compatibility tags).
fn interpreter(&self) -> &Interpreter;
/// Whether to enforce build isolation when building source distributions.
fn build_isolation(&self) -> BuildIsolation;
/// Whether source distribution building is disabled. This [`BuildContext::setup_build`] calls
/// will fail in this case. This method exists to avoid fetching source distributions if we know
/// we can't build them
fn no_build(&self) -> &NoBuild;
/// Whether using pre-built wheels is disabled.
fn no_binary(&self) -> &NoBinary;
/// The index locations being searched.
fn index_locations(&self) -> &IndexLocations;
/// The strategy to use when building source distributions that lack a `pyproject.toml`.
fn setup_py_strategy(&self) -> SetupPyStrategy;
/// Resolve the given requirements into a ready-to-install set of package versions.
fn resolve<'a>(
&'a self,
requirements: &'a [Requirement],
) -> impl Future<Output = Result<Resolution>> + Send + 'a;
/// Install the given set of package versions into the virtual environment. The environment must
/// use the same base Python as [`BuildContext::interpreter`]
fn install<'a>(
&'a self,
resolution: &'a Resolution,
venv: &'a PythonEnvironment,
) -> impl Future<Output = Result<()>> + Send + 'a;
/// Setup a source distribution build by installing the required dependencies. A wrapper for
/// `uv_build::SourceBuild::setup`.
///
/// For PEP 517 builds, this calls `get_requires_for_build_wheel`.
///
/// `package_id` is for error reporting only.
/// `dist` is for safety checks and may be null for editable builds.
fn setup_build<'a>(
&'a self,
source: &'a Path,
subdirectory: Option<&'a Path>,
package_id: &'a str,
dist: Option<&'a SourceDist>,
build_kind: BuildKind,
) -> impl Future<Output = Result<Self::SourceDistBuilder>> + Send + 'a;
}
/// A wrapper for `uv_build::SourceBuild` to avoid cyclical crate dependencies.
///
/// You can either call only `wheel()` to build the wheel directly, call only `metadata()` to get
/// the metadata without performing the actual or first call `metadata()` and then `wheel()`.
pub trait SourceBuildTrait {
/// A wrapper for `uv_build::SourceBuild::get_metadata_without_build`.
///
/// For PEP 517 builds, this calls `prepare_metadata_for_build_wheel`
///
/// Returns the metadata directory if we're having a PEP 517 build and the
/// `prepare_metadata_for_build_wheel` hook exists
fn metadata(&mut self) -> impl Future<Output = Result<Option<PathBuf>>> + Send;
/// A wrapper for `uv_build::SourceBuild::build`.
///
/// For PEP 517 builds, this calls `build_wheel`.
///
/// Returns the filename of the built wheel inside the given `wheel_dir`.
fn wheel<'a>(&'a self, wheel_dir: &'a Path)
-> impl Future<Output = Result<String>> + Send + 'a;
}

View file

@ -31,7 +31,7 @@ uv-interpreter = { workspace = true }
uv-normalize = { workspace = true }
uv-requirements = { workspace = true }
uv-resolver = { workspace = true, features = ["clap"] }
uv-traits = { workspace = true }
uv-types = { workspace = true }
uv-virtualenv = { workspace = true }
uv-warnings = { workspace = true }

View file

@ -28,15 +28,14 @@ use uv_installer::{Downloader, NoBinary};
use uv_interpreter::{find_best_python, PythonEnvironment, PythonVersion};
use uv_normalize::{ExtraName, PackageName};
use uv_requirements::{
upgrade::{read_lockfile, Upgrade},
ExtrasSpecification, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification,
SourceTreeResolver,
upgrade::read_lockfile, ExtrasSpecification, NamedRequirementsResolver, RequirementsSource,
RequirementsSpecification, SourceTreeResolver,
};
use uv_resolver::{
AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest,
OptionsBuilder, PreReleaseMode, PythonRequirement, ResolutionMode, Resolver,
};
use uv_traits::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy, Upgrade};
use uv_warnings::warn_user;
use crate::commands::reporters::{DownloadReporter, ResolverReporter};

View file

@ -34,14 +34,14 @@ use uv_installer::{
use uv_interpreter::{Interpreter, PythonEnvironment};
use uv_normalize::PackageName;
use uv_requirements::{
upgrade::Upgrade, ExtrasSpecification, NamedRequirementsResolver, RequirementsSource,
RequirementsSpecification, SourceTreeResolver,
ExtrasSpecification, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification,
SourceTreeResolver,
};
use uv_resolver::{
DependencyMode, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode, Preference,
ResolutionGraph, ResolutionMode, Resolver,
};
use uv_traits::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy, Upgrade};
use uv_warnings::warn_user;
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};

View file

@ -27,7 +27,7 @@ use uv_requirements::{
SourceTreeResolver,
};
use uv_resolver::InMemoryIndex;
use uv_traits::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_warnings::warn_user;
use crate::commands::reporters::{

View file

@ -21,7 +21,7 @@ use uv_fs::Simplified;
use uv_installer::NoBinary;
use uv_interpreter::{find_default_python, find_requested_python, Error};
use uv_resolver::{InMemoryIndex, OptionsBuilder};
use uv_traits::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use uv_types::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
use crate::commands::ExitStatus;
use crate::printer::Printer;

View file

@ -19,10 +19,10 @@ use uv_client::Connectivity;
use uv_installer::{NoBinary, Reinstall};
use uv_interpreter::PythonVersion;
use uv_normalize::{ExtraName, PackageName};
use uv_requirements::{upgrade::Upgrade, ExtrasSpecification, RequirementsSource};
use uv_requirements::{ExtrasSpecification, RequirementsSource};
use uv_resolver::{AnnotationStyle, DependencyMode, PreReleaseMode, ResolutionMode};
use uv_traits::{
ConfigSettingEntry, ConfigSettings, NoBuild, PackageNameSpecifier, SetupPyStrategy,
use uv_types::{
ConfigSettingEntry, ConfigSettings, NoBuild, PackageNameSpecifier, SetupPyStrategy, Upgrade,
};
use crate::commands::{extra_name_with_clap_error, ExitStatus, ListFormat, VersionFormat};