mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-26 20:19:08 +00:00
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:
parent
39769d82a0
commit
0b08ba1e67
44 changed files with 696 additions and 624 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
@ -4361,7 +4361,7 @@ dependencies = [
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-requirements",
|
"uv-requirements",
|
||||||
"uv-resolver",
|
"uv-resolver",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"uv-virtualenv",
|
"uv-virtualenv",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
]
|
]
|
||||||
|
@ -4411,7 +4411,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
"uv-interpreter",
|
"uv-interpreter",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"uv-virtualenv",
|
"uv-virtualenv",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4533,7 +4533,7 @@ dependencies = [
|
||||||
"uv-interpreter",
|
"uv-interpreter",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-resolver",
|
"uv-resolver",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4554,7 +4554,7 @@ dependencies = [
|
||||||
"uv-installer",
|
"uv-installer",
|
||||||
"uv-interpreter",
|
"uv-interpreter",
|
||||||
"uv-resolver",
|
"uv-resolver",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4590,7 +4590,7 @@ dependencies = [
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
"uv-git",
|
"uv-git",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4684,7 +4684,7 @@ dependencies = [
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
"uv-interpreter",
|
"uv-interpreter",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
@ -4757,7 +4757,7 @@ dependencies = [
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-resolver",
|
"uv-resolver",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4801,18 +4801,19 @@ dependencies = [
|
||||||
"uv-distribution",
|
"uv-distribution",
|
||||||
"uv-interpreter",
|
"uv-interpreter",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-traits",
|
"uv-types",
|
||||||
"uv-warnings",
|
"uv-warnings",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-traits"
|
name = "uv-types"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"distribution-types",
|
"distribution-types",
|
||||||
"once-map",
|
"once-map",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"uv-cache",
|
"uv-cache",
|
||||||
|
|
|
@ -43,7 +43,7 @@ uv-interpreter = { path = "crates/uv-interpreter" }
|
||||||
uv-normalize = { path = "crates/uv-normalize" }
|
uv-normalize = { path = "crates/uv-normalize" }
|
||||||
uv-requirements = { path = "crates/uv-requirements" }
|
uv-requirements = { path = "crates/uv-requirements" }
|
||||||
uv-resolver = { path = "crates/uv-resolver" }
|
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-trampoline = { path = "crates/uv-trampoline" }
|
||||||
uv-version = { path = "crates/uv-version" }
|
uv-version = { path = "crates/uv-version" }
|
||||||
uv-virtualenv = { path = "crates/uv-virtualenv" }
|
uv-virtualenv = { path = "crates/uv-virtualenv" }
|
||||||
|
|
|
@ -66,7 +66,7 @@ Development utilities for uv.
|
||||||
## [uv-dispatch](./uv-dispatch)
|
## [uv-dispatch](./uv-dispatch)
|
||||||
|
|
||||||
A centralized `struct` for resolving and building source distributions in isolated environments.
|
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)
|
## [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.
|
Functionality for resolving Python packages and their dependencies.
|
||||||
|
|
||||||
## [uv-traits](./uv-traits)
|
## [uv-types](./uv-types)
|
||||||
|
|
||||||
Shared traits for uv, to avoid circular dependencies.
|
Shared traits for uv, to avoid circular dependencies.
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ pep440_rs = { workspace = true }
|
||||||
pep508_rs = { workspace = true }
|
pep508_rs = { workspace = true }
|
||||||
uv-fs = { workspace = true }
|
uv-fs = { workspace = true }
|
||||||
uv-interpreter = { workspace = true }
|
uv-interpreter = { workspace = true }
|
||||||
uv-traits = { workspace = true, features = ["serde"] }
|
uv-types = { workspace = true, features = ["serde"] }
|
||||||
uv-virtualenv = { workspace = true }
|
uv-virtualenv = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -30,7 +30,7 @@ use pep440_rs::{Version, VersionSpecifiers};
|
||||||
use pep508_rs::{PackageName, Requirement};
|
use pep508_rs::{PackageName, Requirement};
|
||||||
use uv_fs::{PythonExt, Simplified};
|
use uv_fs::{PythonExt, Simplified};
|
||||||
use uv_interpreter::{Interpreter, PythonEnvironment};
|
use uv_interpreter::{Interpreter, PythonEnvironment};
|
||||||
use uv_traits::{
|
use uv_types::{
|
||||||
BuildContext, BuildIsolation, BuildKind, ConfigSettings, SetupPyStrategy, SourceBuildTrait,
|
BuildContext, BuildIsolation, BuildKind, ConfigSettings, SetupPyStrategy, SourceBuildTrait,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ uv-installer = { workspace = true }
|
||||||
uv-interpreter = { workspace = true }
|
uv-interpreter = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-resolver = { 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
|
# 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.
|
# dependencies, to ensure that we're forced to think twice before including them in other crates.
|
||||||
|
|
|
@ -14,7 +14,7 @@ use uv_dispatch::BuildDispatch;
|
||||||
use uv_installer::NoBinary;
|
use uv_installer::NoBinary;
|
||||||
use uv_interpreter::PythonEnvironment;
|
use uv_interpreter::PythonEnvironment;
|
||||||
use uv_resolver::InMemoryIndex;
|
use uv_resolver::InMemoryIndex;
|
||||||
use uv_traits::{
|
use uv_types::{
|
||||||
BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy,
|
BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ use uv_installer::{Downloader, NoBinary};
|
||||||
use uv_interpreter::PythonEnvironment;
|
use uv_interpreter::PythonEnvironment;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_resolver::{DistFinder, InMemoryIndex};
|
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)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct InstallManyArgs {
|
pub(crate) struct InstallManyArgs {
|
||||||
|
|
|
@ -17,7 +17,7 @@ use uv_dispatch::BuildDispatch;
|
||||||
use uv_installer::NoBinary;
|
use uv_installer::NoBinary;
|
||||||
use uv_interpreter::PythonEnvironment;
|
use uv_interpreter::PythonEnvironment;
|
||||||
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
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)]
|
#[derive(ValueEnum, Default, Clone)]
|
||||||
pub(crate) enum ResolveCliFormat {
|
pub(crate) enum ResolveCliFormat {
|
||||||
|
|
|
@ -20,7 +20,7 @@ use uv_installer::NoBinary;
|
||||||
use uv_interpreter::PythonEnvironment;
|
use uv_interpreter::PythonEnvironment;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_resolver::InMemoryIndex;
|
use uv_resolver::InMemoryIndex;
|
||||||
use uv_traits::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
use uv_types::{BuildContext, BuildIsolation, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct ResolveManyArgs {
|
pub(crate) struct ResolveManyArgs {
|
||||||
|
|
|
@ -22,7 +22,7 @@ uv-client = { workspace = true }
|
||||||
uv-installer = { workspace = true }
|
uv-installer = { workspace = true }
|
||||||
uv-interpreter = { workspace = true }
|
uv-interpreter = { workspace = true }
|
||||||
uv-resolver = { workspace = true }
|
uv-resolver = { workspace = true }
|
||||||
uv-traits = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
|
|
|
@ -20,7 +20,7 @@ use uv_client::{FlatIndex, RegistryClient};
|
||||||
use uv_installer::{Downloader, Installer, NoBinary, Plan, Planner, Reinstall, SitePackages};
|
use uv_installer::{Downloader, Installer, NoBinary, Plan, Planner, Reinstall, SitePackages};
|
||||||
use uv_interpreter::{Interpreter, PythonEnvironment};
|
use uv_interpreter::{Interpreter, PythonEnvironment};
|
||||||
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
||||||
use uv_traits::{
|
use uv_types::{
|
||||||
BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy,
|
BuildContext, BuildIsolation, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ uv-extract = { workspace = true }
|
||||||
uv-fs = { workspace = true, features = ["tokio"] }
|
uv-fs = { workspace = true, features = ["tokio"] }
|
||||||
uv-git = { workspace = true, features = ["vendored-openssl"] }
|
uv-git = { workspace = true, features = ["vendored-openssl"] }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-traits = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
pypi-types = { workspace = true }
|
pypi-types = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -19,7 +19,7 @@ use pypi_types::Metadata23;
|
||||||
use uv_cache::{ArchiveTarget, ArchiveTimestamp, Cache, CacheBucket, CacheEntry, WheelCache};
|
use uv_cache::{ArchiveTarget, ArchiveTimestamp, Cache, CacheBucket, CacheEntry, WheelCache};
|
||||||
use uv_client::{CacheControl, CachedClientError, Connectivity, RegistryClient};
|
use uv_client::{CacheControl, CachedClientError, Connectivity, RegistryClient};
|
||||||
use uv_git::GitSource;
|
use uv_git::GitSource;
|
||||||
use uv_traits::{BuildContext, NoBinary, NoBuild};
|
use uv_types::{BuildContext, NoBinary, NoBuild};
|
||||||
|
|
||||||
use crate::download::{BuiltWheel, UnzippedWheel};
|
use crate::download::{BuiltWheel, UnzippedWheel};
|
||||||
use crate::locks::Locks;
|
use crate::locks::Locks;
|
||||||
|
|
|
@ -33,7 +33,7 @@ use uv_client::{
|
||||||
};
|
};
|
||||||
use uv_fs::{write_atomic, LockedFile};
|
use uv_fs::{write_atomic, LockedFile};
|
||||||
use uv_git::{Fetch, GitSource};
|
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::error::Error;
|
||||||
use crate::reporter::Facade;
|
use crate::reporter::Facade;
|
||||||
|
|
|
@ -27,7 +27,7 @@ uv-extract = { workspace = true }
|
||||||
uv-fs = { workspace = true }
|
uv-fs = { workspace = true }
|
||||||
uv-interpreter = { workspace = true }
|
uv-interpreter = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-traits = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -15,7 +15,7 @@ use platform_tags::Tags;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::RegistryClient;
|
use uv_client::RegistryClient;
|
||||||
use uv_distribution::{DistributionDatabase, LocalWheel, Unzip};
|
use uv_distribution::{DistributionDatabase, LocalWheel, Unzip};
|
||||||
use uv_traits::{BuildContext, InFlight};
|
use uv_types::{BuildContext, InFlight};
|
||||||
|
|
||||||
use crate::editable::BuiltEditable;
|
use crate::editable::BuiltEditable;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub use installer::{Installer, Reporter as InstallReporter};
|
||||||
pub use plan::{Plan, Planner, Reinstall};
|
pub use plan::{Plan, Planner, Reinstall};
|
||||||
pub use site_packages::{Diagnostic, SitePackages};
|
pub use site_packages::{Diagnostic, SitePackages};
|
||||||
pub use uninstall::{uninstall, UninstallError};
|
pub use uninstall::{uninstall, UninstallError};
|
||||||
pub use uv_traits::NoBinary;
|
pub use uv_types::NoBinary;
|
||||||
|
|
||||||
mod compile;
|
mod compile;
|
||||||
mod downloader;
|
mod downloader;
|
||||||
|
|
|
@ -17,7 +17,7 @@ use uv_distribution::{BuiltWheelIndex, RegistryWheelIndex};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_interpreter::PythonEnvironment;
|
use uv_interpreter::PythonEnvironment;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_traits::NoBinary;
|
use uv_types::NoBinary;
|
||||||
|
|
||||||
use crate::{ResolvedEditable, SitePackages};
|
use crate::{ResolvedEditable, SitePackages};
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ uv-distribution = { workspace = true }
|
||||||
uv-fs = { workspace = true }
|
uv-fs = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-resolver = { workspace = true, features = ["clap"] }
|
uv-resolver = { workspace = true, features = ["clap"] }
|
||||||
uv-traits = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -20,7 +20,7 @@ use pypi_types::Metadata10;
|
||||||
use uv_client::RegistryClient;
|
use uv_client::RegistryClient;
|
||||||
use uv_distribution::{Reporter, SourceDistCachedBuilder};
|
use uv_distribution::{Reporter, SourceDistCachedBuilder};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_traits::BuildContext;
|
use uv_types::BuildContext;
|
||||||
|
|
||||||
/// Like [`RequirementsSpecification`], but with concrete names for all requirements.
|
/// Like [`RequirementsSpecification`], but with concrete names for all requirements.
|
||||||
pub struct NamedRequirementsResolver {
|
pub struct NamedRequirementsResolver {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use distribution_types::{BuildableSource, PathSourceUrl, SourceUrl};
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
use uv_client::RegistryClient;
|
use uv_client::RegistryClient;
|
||||||
use uv_distribution::{Reporter, SourceDistCachedBuilder};
|
use uv_distribution::{Reporter, SourceDistCachedBuilder};
|
||||||
use uv_traits::BuildContext;
|
use uv_types::BuildContext;
|
||||||
|
|
||||||
use crate::ExtrasSpecification;
|
use crate::ExtrasSpecification;
|
||||||
|
|
||||||
|
|
|
@ -1,48 +1,11 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
|
|
||||||
use requirements_txt::RequirementsTxt;
|
use requirements_txt::RequirementsTxt;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity};
|
use uv_client::{BaseClientBuilder, Connectivity};
|
||||||
use uv_normalize::PackageName;
|
|
||||||
use uv_resolver::{Preference, PreferenceError};
|
use uv_resolver::{Preference, PreferenceError};
|
||||||
|
use uv_types::Upgrade;
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
|
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
|
||||||
pub async fn read_lockfile(
|
pub async fn read_lockfile(
|
||||||
|
|
|
@ -27,7 +27,7 @@ uv-client = { workspace = true }
|
||||||
uv-distribution = { workspace = true }
|
uv-distribution = { workspace = true }
|
||||||
uv-interpreter = { workspace = true }
|
uv-interpreter = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-traits = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
|
|
||||||
anstream = { workspace = true }
|
anstream = { workspace = true }
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::{stream, Stream, StreamExt, TryStreamExt};
|
use futures::{stream, Stream, StreamExt, TryStreamExt};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use uv_traits::{NoBinary, NoBuild};
|
use uv_types::{NoBinary, NoBuild};
|
||||||
|
|
||||||
use distribution_filename::DistFilename;
|
use distribution_filename::DistFilename;
|
||||||
use distribution_types::{Dist, IndexUrl, Resolution};
|
use distribution_types::{Dist, IndexUrl, Resolution};
|
||||||
|
|
|
@ -31,7 +31,7 @@ use uv_client::{FlatIndex, RegistryClient};
|
||||||
use uv_distribution::DistributionDatabase;
|
use uv_distribution::DistributionDatabase;
|
||||||
use uv_interpreter::Interpreter;
|
use uv_interpreter::Interpreter;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_traits::BuildContext;
|
use uv_types::BuildContext;
|
||||||
|
|
||||||
use crate::candidate_selector::{CandidateDist, CandidateSelector};
|
use crate::candidate_selector::{CandidateDist, CandidateSelector};
|
||||||
use crate::constraints::Constraints;
|
use crate::constraints::Constraints;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use pypi_types::Metadata23;
|
||||||
use uv_client::{FlatIndex, RegistryClient};
|
use uv_client::{FlatIndex, RegistryClient};
|
||||||
use uv_distribution::DistributionDatabase;
|
use uv_distribution::DistributionDatabase;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_traits::{BuildContext, NoBinary, NoBuild};
|
use uv_types::{BuildContext, NoBinary, NoBuild};
|
||||||
|
|
||||||
use crate::python_requirement::PythonRequirement;
|
use crate::python_requirement::PythonRequirement;
|
||||||
use crate::version_map::VersionMap;
|
use crate::version_map::VersionMap;
|
||||||
|
|
|
@ -16,7 +16,7 @@ use pypi_types::{Hashes, Yanked};
|
||||||
use rkyv::{de::deserializers::SharedDeserializeMap, Deserialize};
|
use rkyv::{de::deserializers::SharedDeserializeMap, Deserialize};
|
||||||
use uv_client::{FlatDistributions, OwnedArchive, SimpleMetadata, VersionFiles};
|
use uv_client::{FlatDistributions, OwnedArchive, SimpleMetadata, VersionFiles};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_traits::{NoBinary, NoBuild};
|
use uv_types::{NoBinary, NoBuild};
|
||||||
use uv_warnings::warn_user_once;
|
use uv_warnings::warn_user_once;
|
||||||
|
|
||||||
use crate::{python_requirement::PythonRequirement, yanks::AllowedYanks};
|
use crate::{python_requirement::PythonRequirement, yanks::AllowedYanks};
|
||||||
|
|
|
@ -20,7 +20,7 @@ use uv_resolver::{
|
||||||
DisplayResolutionGraph, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode,
|
DisplayResolutionGraph, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode,
|
||||||
Preference, ResolutionGraph, ResolutionMode, Resolver,
|
Preference, ResolutionGraph, ResolutionMode, Resolver,
|
||||||
};
|
};
|
||||||
use uv_traits::{
|
use uv_types::{
|
||||||
BuildContext, BuildIsolation, BuildKind, NoBinary, NoBuild, SetupPyStrategy, SourceBuildTrait,
|
BuildContext, BuildIsolation, BuildKind, NoBinary, NoBuild, SetupPyStrategy, SourceBuildTrait,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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"}"#);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uv-traits"
|
name = "uv-types"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
@ -21,6 +21,7 @@ uv-interpreter = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
rustc-hash = { workspace = true }
|
||||||
serde = { workspace = true, optional = true }
|
serde = { workspace = true, optional = true }
|
||||||
serde_json = { workspace = true, optional = true }
|
serde_json = { workspace = true, optional = true }
|
||||||
|
|
179
crates/uv-types/src/build_options.rs
Normal file
179
crates/uv-types/src/build_options.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
176
crates/uv-types/src/config_settings.rs
Normal file
176
crates/uv-types/src/config_settings.rs
Normal 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"}"#);
|
||||||
|
}
|
||||||
|
}
|
8
crates/uv-types/src/downloads.rs
Normal file
8
crates/uv-types/src/downloads.rs
Normal 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>>,
|
||||||
|
}
|
14
crates/uv-types/src/lib.rs
Normal file
14
crates/uv-types/src/lib.rs
Normal 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;
|
63
crates/uv-types/src/name_specifiers.rs
Normal file
63
crates/uv-types/src/name_specifiers.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
crates/uv-types/src/package_options.rs
Normal file
74
crates/uv-types/src/package_options.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
129
crates/uv-types/src/traits.rs
Normal file
129
crates/uv-types/src/traits.rs
Normal 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;
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ uv-interpreter = { workspace = true }
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
uv-requirements = { workspace = true }
|
uv-requirements = { workspace = true }
|
||||||
uv-resolver = { workspace = true, features = ["clap"] }
|
uv-resolver = { workspace = true, features = ["clap"] }
|
||||||
uv-traits = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
uv-virtualenv = { workspace = true }
|
uv-virtualenv = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,14 @@ use uv_installer::{Downloader, NoBinary};
|
||||||
use uv_interpreter::{find_best_python, PythonEnvironment, PythonVersion};
|
use uv_interpreter::{find_best_python, PythonEnvironment, PythonVersion};
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
use uv_requirements::{
|
use uv_requirements::{
|
||||||
upgrade::{read_lockfile, Upgrade},
|
upgrade::read_lockfile, ExtrasSpecification, NamedRequirementsResolver, RequirementsSource,
|
||||||
ExtrasSpecification, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification,
|
RequirementsSpecification, SourceTreeResolver,
|
||||||
SourceTreeResolver,
|
|
||||||
};
|
};
|
||||||
use uv_resolver::{
|
use uv_resolver::{
|
||||||
AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest,
|
AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest,
|
||||||
OptionsBuilder, PreReleaseMode, PythonRequirement, ResolutionMode, Resolver,
|
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 uv_warnings::warn_user;
|
||||||
|
|
||||||
use crate::commands::reporters::{DownloadReporter, ResolverReporter};
|
use crate::commands::reporters::{DownloadReporter, ResolverReporter};
|
||||||
|
|
|
@ -34,14 +34,14 @@ use uv_installer::{
|
||||||
use uv_interpreter::{Interpreter, PythonEnvironment};
|
use uv_interpreter::{Interpreter, PythonEnvironment};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_requirements::{
|
use uv_requirements::{
|
||||||
upgrade::Upgrade, ExtrasSpecification, NamedRequirementsResolver, RequirementsSource,
|
ExtrasSpecification, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification,
|
||||||
RequirementsSpecification, SourceTreeResolver,
|
SourceTreeResolver,
|
||||||
};
|
};
|
||||||
use uv_resolver::{
|
use uv_resolver::{
|
||||||
DependencyMode, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode, Preference,
|
DependencyMode, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode, Preference,
|
||||||
ResolutionGraph, ResolutionMode, Resolver,
|
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 uv_warnings::warn_user;
|
||||||
|
|
||||||
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};
|
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};
|
||||||
|
|
|
@ -27,7 +27,7 @@ use uv_requirements::{
|
||||||
SourceTreeResolver,
|
SourceTreeResolver,
|
||||||
};
|
};
|
||||||
use uv_resolver::InMemoryIndex;
|
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 uv_warnings::warn_user;
|
||||||
|
|
||||||
use crate::commands::reporters::{
|
use crate::commands::reporters::{
|
||||||
|
|
|
@ -21,7 +21,7 @@ use uv_fs::Simplified;
|
||||||
use uv_installer::NoBinary;
|
use uv_installer::NoBinary;
|
||||||
use uv_interpreter::{find_default_python, find_requested_python, Error};
|
use uv_interpreter::{find_default_python, find_requested_python, Error};
|
||||||
use uv_resolver::{InMemoryIndex, OptionsBuilder};
|
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::commands::ExitStatus;
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
|
@ -19,10 +19,10 @@ use uv_client::Connectivity;
|
||||||
use uv_installer::{NoBinary, Reinstall};
|
use uv_installer::{NoBinary, Reinstall};
|
||||||
use uv_interpreter::PythonVersion;
|
use uv_interpreter::PythonVersion;
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
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_resolver::{AnnotationStyle, DependencyMode, PreReleaseMode, ResolutionMode};
|
||||||
use uv_traits::{
|
use uv_types::{
|
||||||
ConfigSettingEntry, ConfigSettings, NoBuild, PackageNameSpecifier, SetupPyStrategy,
|
ConfigSettingEntry, ConfigSettings, NoBuild, PackageNameSpecifier, SetupPyStrategy, Upgrade,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::commands::{extra_name_with_clap_error, ExitStatus, ListFormat, VersionFormat};
|
use crate::commands::{extra_name_with_clap_error, ExitStatus, ListFormat, VersionFormat};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue