Enable first-class dependency exclusions (#16528)

## Summary

This PR adds an `exclude-dependencies` setting that allows users to omit
a dependency during resolution. It's effectively a formalized version of
the `flask ; python_version < '0'` hack that we've suggested to users in
various issues.

Closes #12616.
This commit is contained in:
Charlie Marsh 2025-10-31 10:14:12 -04:00 committed by GitHub
parent 7978122837
commit 5c71b5c124
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 711 additions and 21 deletions

View file

@ -1246,6 +1246,16 @@ pub struct PipCompileArgs {
#[arg(long, alias = "override", env = EnvVars::UV_OVERRIDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub overrides: Vec<Maybe<PathBuf>>,
/// Exclude packages from resolution using the given requirements files.
///
/// Excludes files are `requirements.txt`-like files that specify packages to exclude
/// from the resolution. When a package is excluded, it will be omitted from the
/// dependency list entirely and its own dependencies will be ignored during the resolution
/// phase. Excludes are unconditional in that requirement specifiers and markers are ignored;
/// any package listed in the provided file will be omitted from all resolved environments.
#[arg(long, alias = "exclude", env = EnvVars::UV_EXCLUDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub excludes: Vec<Maybe<PathBuf>>,
/// Constrain build dependencies using the given requirements files when building source
/// distributions.
///
@ -1897,6 +1907,16 @@ pub struct PipInstallArgs {
#[arg(long, alias = "override", env = EnvVars::UV_OVERRIDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub overrides: Vec<Maybe<PathBuf>>,
/// Exclude packages from resolution using the given requirements files.
///
/// Excludes files are `requirements.txt`-like files that specify packages to exclude
/// from the resolution. When a package is excluded, it will be omitted from the
/// dependency list entirely and its own dependencies will be ignored during the resolution
/// phase. Excludes are unconditional in that requirement specifiers and markers are ignored;
/// any package listed in the provided file will be omitted from all resolved environments.
#[arg(long, alias = "exclude", env = EnvVars::UV_EXCLUDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub excludes: Vec<Maybe<PathBuf>>,
/// Constrain build dependencies using the given requirements files when building source
/// distributions.
///
@ -4848,6 +4868,16 @@ pub struct ToolInstallArgs {
#[arg(long, alias = "override", env = EnvVars::UV_OVERRIDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub overrides: Vec<Maybe<PathBuf>>,
/// Exclude packages from resolution using the given requirements files.
///
/// Excludes files are `requirements.txt`-like files that specify packages to exclude
/// from the resolution. When a package is excluded, it will be omitted from the
/// dependency list entirely and its own dependencies will be ignored during the resolution
/// phase. Excludes are unconditional in that requirement specifiers and markers are ignored;
/// any package listed in the provided file will be omitted from all resolved environments.
#[arg(long, alias = "exclude", env = EnvVars::UV_EXCLUDE, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub excludes: Vec<Maybe<PathBuf>>,
/// Constrain build dependencies using the given requirements files when building source
/// distributions.
///

View file

@ -0,0 +1,25 @@
use rustc_hash::FxHashSet;
use uv_normalize::PackageName;
/// A set of packages to exclude from resolution.
#[derive(Debug, Default, Clone)]
pub struct Excludes(FxHashSet<PackageName>);
impl Excludes {
/// Return an iterator over all package names in the exclusion set.
pub fn iter(&self) -> impl Iterator<Item = &PackageName> {
self.0.iter()
}
/// Check if a package is excluded.
pub fn contains(&self, name: &PackageName) -> bool {
self.0.contains(name)
}
}
impl FromIterator<PackageName> for Excludes {
fn from_iter<I: IntoIterator<Item = PackageName>>(iter: I) -> Self {
Self(iter.into_iter().collect())
}
}

View file

@ -6,6 +6,7 @@ pub use dependency_groups::*;
pub use dry_run::*;
pub use editable::*;
pub use env_file::*;
pub use excludes::*;
pub use export_format::*;
pub use extras::*;
pub use hash::*;
@ -30,6 +31,7 @@ mod dependency_groups;
mod dry_run;
mod editable;
mod env_file;
mod excludes;
mod export_format;
mod extras;
mod hash;

View file

@ -61,6 +61,8 @@ pub struct RequirementsSpecification {
pub constraints: Vec<NameRequirementSpecification>,
/// The overrides for the project.
pub overrides: Vec<UnresolvedRequirementSpecification>,
/// The excludes for the project.
pub excludes: Vec<PackageName>,
/// The `pylock.toml` file from which to extract the resolution.
pub pylock: Option<PathBuf>,
/// The source trees from which to extract requirements.
@ -345,6 +347,7 @@ impl RequirementsSpecification {
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
excludes: &[RequirementsSource],
groups: Option<&GroupsSpecification>,
client_builder: &BaseClientBuilder<'_>,
) -> Result<Self> {
@ -378,6 +381,20 @@ impl RequirementsSpecification {
));
}
// Disallow `pylock.toml` files as excludes.
if let Some(pylock_toml) = excludes.iter().find_map(|source| {
if let RequirementsSource::PylockToml(path) = source {
Some(path)
} else {
None
}
}) {
return Err(anyhow::anyhow!(
"Cannot use `{}` as an exclude file",
pylock_toml.user_display()
));
}
// If we have a `pylock.toml`, don't allow additional requirements, constraints, or
// overrides.
if let Some(pylock_toml) = requirements.iter().find_map(|source| {
@ -582,6 +599,24 @@ impl RequirementsSpecification {
spec.no_build.extend(source.no_build);
}
// Collect excludes.
for source in excludes {
let source = Self::from_source(source, client_builder).await?;
for req_spec in source.requirements {
match req_spec.requirement {
UnresolvedRequirement::Named(requirement) => {
spec.excludes.push(requirement.name);
}
UnresolvedRequirement::Unnamed(requirement) => {
return Err(anyhow::anyhow!(
"Unnamed requirements are not allowed as exclusions (found: `{requirement}`)"
));
}
}
}
spec.excludes.extend(source.excludes.into_iter());
}
Ok(spec)
}
@ -597,7 +632,7 @@ impl RequirementsSpecification {
requirements: &[RequirementsSource],
client_builder: &BaseClientBuilder<'_>,
) -> Result<Self> {
Self::from_sources(requirements, &[], &[], None, client_builder).await
Self::from_sources(requirements, &[], &[], &[], None, client_builder).await
}
/// Initialize a [`RequirementsSpecification`] from a list of [`Requirement`].

View file

@ -1163,6 +1163,23 @@ impl Lock {
manifest_table.insert("overrides", value(overrides));
}
if !self.manifest.excludes.is_empty() {
let excludes = self
.manifest
.excludes
.iter()
.map(|name| {
serde::Serialize::serialize(&name, toml_edit::ser::ValueSerializer::new())
})
.collect::<Result<Vec<_>, _>>()?;
let excludes = match excludes.as_slice() {
[] => Array::new(),
[name] => Array::from_iter([name]),
excludes => each_element_on_its_line_array(excludes.iter()),
};
manifest_table.insert("excludes", value(excludes));
}
if !self.manifest.build_constraints.is_empty() {
let build_constraints = self
.manifest
@ -1447,6 +1464,7 @@ impl Lock {
requirements: &[Requirement],
constraints: &[Requirement],
overrides: &[Requirement],
excludes: &[PackageName],
build_constraints: &[Requirement],
dependency_groups: &BTreeMap<GroupName, Vec<Requirement>>,
dependency_metadata: &DependencyMetadata,
@ -1563,6 +1581,15 @@ impl Lock {
}
}
// Validate that the lockfile was generated with the same excludes.
{
let expected: BTreeSet<_> = excludes.iter().cloned().collect();
let actual: BTreeSet<_> = self.manifest.excludes.iter().cloned().collect();
if expected != actual {
return Ok(SatisfiesResult::MismatchedExcludes(expected, actual));
}
}
// Validate that the lockfile was generated with the same build constraints.
{
let expected: BTreeSet<_> = build_constraints
@ -2049,6 +2076,8 @@ pub enum SatisfiesResult<'lock> {
MismatchedConstraints(BTreeSet<Requirement>, BTreeSet<Requirement>),
/// The lockfile uses a different set of overrides.
MismatchedOverrides(BTreeSet<Requirement>, BTreeSet<Requirement>),
/// The lockfile uses a different set of excludes.
MismatchedExcludes(BTreeSet<PackageName>, BTreeSet<PackageName>),
/// The lockfile uses a different set of build constraints.
MismatchedBuildConstraints(BTreeSet<Requirement>, BTreeSet<Requirement>),
/// The lockfile uses a different set of dependency groups.
@ -2148,6 +2177,9 @@ pub struct ResolverManifest {
/// The overrides provided to the resolver.
#[serde(default)]
overrides: BTreeSet<Requirement>,
/// The excludes provided to the resolver.
#[serde(default)]
excludes: BTreeSet<PackageName>,
/// The build constraints provided to the resolver.
#[serde(default)]
build_constraints: BTreeSet<Requirement>,
@ -2164,6 +2196,7 @@ impl ResolverManifest {
requirements: impl IntoIterator<Item = Requirement>,
constraints: impl IntoIterator<Item = Requirement>,
overrides: impl IntoIterator<Item = Requirement>,
excludes: impl IntoIterator<Item = PackageName>,
build_constraints: impl IntoIterator<Item = Requirement>,
dependency_groups: impl IntoIterator<Item = (GroupName, Vec<Requirement>)>,
dependency_metadata: impl IntoIterator<Item = StaticMetadata>,
@ -2173,6 +2206,7 @@ impl ResolverManifest {
requirements: requirements.into_iter().collect(),
constraints: constraints.into_iter().collect(),
overrides: overrides.into_iter().collect(),
excludes: excludes.into_iter().collect(),
build_constraints: build_constraints.into_iter().collect(),
dependency_groups: dependency_groups
.into_iter()
@ -2201,6 +2235,7 @@ impl ResolverManifest {
.into_iter()
.map(|requirement| requirement.relative_to(root))
.collect::<Result<BTreeSet<_>, _>>()?,
excludes: self.excludes,
build_constraints: self
.build_constraints
.into_iter()

View file

@ -120,6 +120,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -127,6 +127,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -119,6 +119,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -198,6 +198,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -198,6 +198,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -223,6 +223,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -198,6 +198,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -98,6 +98,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -94,6 +94,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -84,6 +84,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -84,6 +84,7 @@ Ok(
dependency_groups: {},
constraints: {},
overrides: {},
excludes: {},
build_constraints: {},
dependency_metadata: {},
},

View file

@ -3,7 +3,7 @@ use std::collections::BTreeSet;
use either::Either;
use uv_configuration::{Constraints, Overrides};
use uv_configuration::{Constraints, Excludes, Overrides};
use uv_distribution_types::Requirement;
use uv_normalize::PackageName;
use uv_types::RequestedRequirements;
@ -23,6 +23,9 @@ pub struct Manifest {
/// The overrides for the project.
pub(crate) overrides: Overrides,
/// The dependency excludes for the project.
pub(crate) excludes: Excludes,
/// The preferences for the project.
///
/// These represent "preferred" versions of a given package. For example, they may be the
@ -55,6 +58,7 @@ impl Manifest {
requirements: Vec<Requirement>,
constraints: Constraints,
overrides: Overrides,
excludes: Excludes,
preferences: Preferences,
project: Option<PackageName>,
workspace_members: BTreeSet<PackageName>,
@ -65,6 +69,7 @@ impl Manifest {
requirements,
constraints,
overrides,
excludes,
preferences,
project,
workspace_members,
@ -78,6 +83,7 @@ impl Manifest {
requirements,
constraints: Constraints::default(),
overrides: Overrides::default(),
excludes: Excludes::default(),
preferences: Preferences::default(),
project: None,
exclusions: Exclusions::default(),
@ -122,6 +128,7 @@ impl Manifest {
.flat_map(move |lookahead| {
self.overrides
.apply(lookahead.requirements())
.filter(|requirement| !self.excludes.contains(&requirement.name))
.filter(move |requirement| {
requirement
.evaluate_markers(env.marker_environment(), lookahead.extras())
@ -130,6 +137,7 @@ impl Manifest {
.chain(
self.overrides
.apply(&self.requirements)
.filter(|requirement| !self.excludes.contains(&requirement.name))
.filter(move |requirement| {
requirement.evaluate_markers(env.marker_environment(), &[])
}),
@ -137,6 +145,7 @@ impl Manifest {
.chain(
self.constraints
.requirements()
.filter(|requirement| !self.excludes.contains(&requirement.name))
.filter(move |requirement| {
requirement.evaluate_markers(env.marker_environment(), &[])
})
@ -148,6 +157,7 @@ impl Manifest {
self.overrides
.apply(&self.requirements)
.chain(self.constraints.requirements().map(Cow::Borrowed))
.filter(|requirement| !self.excludes.contains(&requirement.name))
.filter(move |requirement| {
requirement.evaluate_markers(env.marker_environment(), &[])
}),
@ -166,6 +176,7 @@ impl Manifest {
DependencyMode::Transitive => Either::Left(
self.overrides
.requirements()
.filter(|requirement| !self.excludes.contains(&requirement.name))
.filter(move |requirement| {
requirement.evaluate_markers(env.marker_environment(), &[])
})
@ -175,6 +186,7 @@ impl Manifest {
DependencyMode::Direct => Either::Right(
self.overrides
.requirements()
.filter(|requirement| !self.excludes.contains(&requirement.name))
.filter(move |requirement| {
requirement.evaluate_markers(env.marker_environment(), &[])
})

View file

@ -20,7 +20,7 @@ use tokio::sync::oneshot;
use tokio_stream::wrappers::ReceiverStream;
use tracing::{Level, debug, info, instrument, trace, warn};
use uv_configuration::{Constraints, Overrides};
use uv_configuration::{Constraints, Excludes, Overrides};
use uv_distribution::{ArchiveMetadata, DistributionDatabase};
use uv_distribution_types::{
BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, DistributionMetadata,
@ -111,6 +111,7 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
requirements: Vec<Requirement>,
constraints: Constraints,
overrides: Overrides,
excludes: Excludes,
preferences: Preferences,
git: GitResolver,
capabilities: IndexCapabilities,
@ -240,6 +241,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
requirements: manifest.requirements,
constraints: manifest.constraints,
overrides: manifest.overrides,
excludes: manifest.excludes,
preferences: manifest.preferences,
exclusions: manifest.exclusions,
hasher: hasher.clone(),
@ -1866,6 +1868,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
);
requirements
.filter(|requirement| !self.excludes.contains(&requirement.name))
.flat_map(|requirement| {
PubGrubDependency::from_requirement(
&self.conflicts,

View file

@ -426,6 +426,7 @@ pub struct ToolUv {
#[serde(flatten)]
pub top_level: ResolverInstallerSchema,
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub exclude_dependencies: Option<Vec<uv_normalize::PackageName>>,
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub extra_build_dependencies: Option<BTreeMap<PackageName, Vec<ExtraBuildDependency>>>,

View file

@ -212,6 +212,7 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
pip: _,
cache_keys: _,
override_dependencies: _,
exclude_dependencies: _,
constraint_dependencies: _,
build_constraint_dependencies: _,
environments,
@ -352,6 +353,7 @@ fn warn_uv_toml_masked_fields(options: &Options) {
pip,
cache_keys,
override_dependencies,
exclude_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments: _,
@ -525,6 +527,9 @@ fn warn_uv_toml_masked_fields(options: &Options) {
if override_dependencies.is_some() {
masked_fields.push("override-dependencies");
}
if exclude_dependencies.is_some() {
masked_fields.push("exclude-dependencies");
}
if constraint_dependencies.is_some() {
masked_fields.push("constraint-dependencies");
}

View file

@ -115,6 +115,9 @@ pub struct Options {
#[cfg_attr(feature = "schemars", schemars(skip))]
pub override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
#[cfg_attr(feature = "schemars", schemars(skip))]
pub exclude_dependencies: Option<Vec<uv_normalize::PackageName>>,
#[cfg_attr(feature = "schemars", schemars(skip))]
pub constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
@ -2110,6 +2113,7 @@ pub struct OptionsWire {
// `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
// They're respected in both `pyproject.toml` and `uv.toml` files.
override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
exclude_dependencies: Option<Vec<uv_normalize::PackageName>>,
constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
environments: Option<SupportedEnvironments>,
@ -2180,6 +2184,7 @@ impl From<OptionsWire> for Options {
pip,
cache_keys,
override_dependencies,
exclude_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments,
@ -2254,6 +2259,7 @@ impl From<OptionsWire> for Options {
cache_keys,
build_backend,
override_dependencies,
exclude_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments,

View file

@ -120,21 +120,26 @@ impl EnvVars {
#[attr_added_in("0.1.34")]
pub const UV_REQUIRE_HASHES: &'static str = "UV_REQUIRE_HASHES";
/// Equivalent to the `--constraint` command-line argument. If set, uv will use this
/// Equivalent to the `--constraints` command-line argument. If set, uv will use this
/// file as the constraints file. Uses space-separated list of files.
#[attr_added_in("0.1.36")]
pub const UV_CONSTRAINT: &'static str = "UV_CONSTRAINT";
/// Equivalent to the `--build-constraint` command-line argument. If set, uv will use this file
/// Equivalent to the `--build-constraints` command-line argument. If set, uv will use this file
/// as constraints for any source distribution builds. Uses space-separated list of files.
#[attr_added_in("0.2.34")]
pub const UV_BUILD_CONSTRAINT: &'static str = "UV_BUILD_CONSTRAINT";
/// Equivalent to the `--override` command-line argument. If set, uv will use this file
/// Equivalent to the `--overrides` command-line argument. If set, uv will use this file
/// as the overrides file. Uses space-separated list of files.
#[attr_added_in("0.2.22")]
pub const UV_OVERRIDE: &'static str = "UV_OVERRIDE";
/// Equivalent to the `--excludes` command-line argument. If set, uv will use this
/// as the excludes file. Uses space-separated list of files.
#[attr_added_in("0.9.8")]
pub const UV_EXCLUDE: &'static str = "UV_EXCLUDE";
/// Equivalent to the `--link-mode` command-line argument. If set, uv will use this as
/// a link mode.
#[attr_added_in("0.1.40")]

View file

@ -489,6 +489,37 @@ pub struct ToolUv {
)]
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
/// Dependencies to exclude when resolving the project's dependencies.
///
/// Excludes are used to prevent a package from being selected during resolution,
/// regardless of whether it's requested by any other package. When a package is excluded,
/// it will be omitted from the dependency list entirely.
///
/// Including a package as an exclusion will prevent it from being installed, even if
/// it's requested by transitive dependencies. This can be useful for removing optional
/// dependencies or working around packages with broken dependencies.
///
/// !!! note
/// In `uv lock`, `uv sync`, and `uv run`, uv will only read `exclude-dependencies` from
/// the `pyproject.toml` at the workspace root, and will ignore any declarations in other
/// workspace members or `uv.toml` files.
#[cfg_attr(
feature = "schemars",
schemars(
with = "Option<Vec<String>>",
description = "Package names to exclude, e.g., `werkzeug`, `numpy`."
)
)]
#[option(
default = "[]",
value_type = "list[str]",
example = r#"
# Exclude Werkzeug from being installed, even if transitive dependencies request it.
exclude-dependencies = ["werkzeug"]
"#
)]
pub exclude_dependencies: Option<Vec<PackageName>>,
/// Constraints to apply when resolving the project's dependencies.
///
/// Constraints are used to restrict the versions of dependencies that are selected during

View file

@ -661,6 +661,20 @@ impl Workspace {
overrides.clone()
}
/// Returns the set of dependency exclusions for the workspace.
pub fn exclude_dependencies(&self) -> Vec<uv_normalize::PackageName> {
let Some(excludes) = self
.pyproject_toml
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.exclude_dependencies.as_ref())
else {
return vec![];
};
excludes.clone()
}
/// Returns the set of constraints for the workspace.
pub fn constraints(&self) -> Vec<uv_pep508::Requirement<VerbatimParsedUrl>> {
let Some(constraints) = self
@ -2059,6 +2073,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"exclude-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2159,6 +2174,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"exclude-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2372,6 +2388,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"exclude-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2481,6 +2498,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"exclude-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2603,6 +2621,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"exclude-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2699,6 +2718,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"exclude-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,

View file

@ -62,9 +62,11 @@ pub(crate) async fn pip_compile(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
excludes: &[RequirementsSource],
build_constraints: &[RequirementsSource],
constraints_from_workspace: Vec<Requirement>,
overrides_from_workspace: Vec<Requirement>,
excludes_from_workspace: Vec<uv_normalize::PackageName>,
build_constraints_from_workspace: Vec<Requirement>,
environments: SupportedEnvironments,
extras: ExtrasSpecification,
@ -202,6 +204,7 @@ pub(crate) async fn pip_compile(
requirements,
constraints,
overrides,
excludes,
pylock,
source_trees,
groups,
@ -216,6 +219,7 @@ pub(crate) async fn pip_compile(
requirements,
constraints,
overrides,
excludes,
Some(&groups),
&client_builder,
)
@ -248,6 +252,11 @@ pub(crate) async fn pip_compile(
)
.collect();
let excludes: Vec<PackageName> = excludes
.into_iter()
.chain(excludes_from_workspace)
.collect();
// Read build constraints.
let build_constraints: Vec<NameRequirementSpecification> =
operations::read_constraints(build_constraints, &client_builder)
@ -532,6 +541,7 @@ pub(crate) async fn pip_compile(
requirements,
constraints,
overrides,
excludes,
source_trees,
project,
BTreeSet::default(),

View file

@ -23,7 +23,7 @@ use uv_distribution_types::{
use uv_fs::Simplified;
use uv_install_wheel::LinkMode;
use uv_installer::{InstallationStrategy, SatisfiesResult, SitePackages};
use uv_normalize::{DefaultExtras, DefaultGroups};
use uv_normalize::{DefaultExtras, DefaultGroups, PackageName};
use uv_preview::{Preview, PreviewFeatures};
use uv_pypi_types::Conflicts;
use uv_python::{
@ -54,9 +54,11 @@ pub(crate) async fn pip_install(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
excludes: &[RequirementsSource],
build_constraints: &[RequirementsSource],
constraints_from_workspace: Vec<Requirement>,
overrides_from_workspace: Vec<Requirement>,
excludes_from_workspace: Vec<uv_normalize::PackageName>,
build_constraints_from_workspace: Vec<Requirement>,
extras: &ExtrasSpecification,
groups: &GroupsSpecification,
@ -118,6 +120,7 @@ pub(crate) async fn pip_install(
requirements,
constraints,
overrides,
excludes,
pylock,
source_trees,
groups,
@ -132,6 +135,7 @@ pub(crate) async fn pip_install(
requirements,
constraints,
overrides,
excludes,
extras,
Some(groups),
&client_builder,
@ -167,6 +171,11 @@ pub(crate) async fn pip_install(
)
.collect();
let excludes: Vec<PackageName> = excludes
.into_iter()
.chain(excludes_from_workspace)
.collect();
// Read build constraints.
let build_constraints: Vec<NameRequirementSpecification> =
operations::read_constraints(build_constraints, &client_builder)
@ -550,6 +559,7 @@ pub(crate) async fn pip_install(
requirements,
constraints,
overrides,
excludes,
source_trees,
project,
BTreeSet::default(),

View file

@ -13,8 +13,8 @@ use tracing::debug;
use uv_cache::Cache;
use uv_client::{BaseClientBuilder, RegistryClient};
use uv_configuration::{
BuildOptions, Concurrency, Constraints, DependencyGroups, DryRun, ExtrasSpecification,
Overrides, Reinstall, Upgrade,
BuildOptions, Concurrency, Constraints, DependencyGroups, DryRun, Excludes,
ExtrasSpecification, Overrides, Reinstall, Upgrade,
};
use uv_dispatch::BuildDispatch;
use uv_distribution::{DistributionDatabase, SourcedDependencyGroups};
@ -54,6 +54,7 @@ pub(crate) async fn read_requirements(
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
excludes: &[RequirementsSource],
extras: &ExtrasSpecification,
groups: Option<&GroupsSpecification>,
client_builder: &BaseClientBuilder<'_>,
@ -80,6 +81,7 @@ pub(crate) async fn read_requirements(
requirements,
constraints,
overrides,
excludes,
groups,
client_builder,
)
@ -92,7 +94,7 @@ pub(crate) async fn read_constraints(
client_builder: &BaseClientBuilder<'_>,
) -> Result<Vec<NameRequirementSpecification>, Error> {
Ok(
RequirementsSpecification::from_sources(&[], constraints, &[], None, client_builder)
RequirementsSpecification::from_sources(&[], constraints, &[], &[], None, client_builder)
.await?
.constraints,
)
@ -103,6 +105,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
requirements: Vec<UnresolvedRequirementSpecification>,
constraints: Vec<NameRequirementSpecification>,
overrides: Vec<UnresolvedRequirementSpecification>,
excludes: Vec<PackageName>,
source_trees: Vec<SourceTree>,
mut project: Option<PackageName>,
workspace_members: BTreeSet<PackageName>,
@ -282,7 +285,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
overrides
};
// Collect constraints and overrides.
// Collect constraints, overrides, and excludes.
let constraints = Constraints::from_requirements(
constraints
.into_iter()
@ -290,6 +293,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
.chain(upgrade.constraints().cloned()),
);
let overrides = Overrides::from_requirements(overrides);
let excludes = excludes.into_iter().collect::<Excludes>();
let preferences = Preferences::from_iter(preferences, &resolver_env);
// Determine any lookahead requirements.
@ -318,6 +322,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
requirements,
constraints,
overrides,
excludes,
preferences,
project,
workspace_members,

View file

@ -102,6 +102,7 @@ pub(crate) async fn pip_sync(
// Initialize a few defaults.
let overrides = &[];
let excludes = &[];
let upgrade = Upgrade::default();
let resolution_mode = ResolutionMode::default();
let prerelease_mode = PrereleaseMode::default();
@ -113,6 +114,7 @@ pub(crate) async fn pip_sync(
requirements,
constraints,
overrides,
excludes,
pylock,
source_trees,
groups,
@ -127,6 +129,7 @@ pub(crate) async fn pip_sync(
requirements,
constraints,
overrides,
excludes,
extras,
Some(groups),
&client_builder,
@ -461,6 +464,7 @@ pub(crate) async fn pip_sync(
requirements,
constraints,
overrides,
excludes,
source_trees,
project,
BTreeSet::default(),

View file

@ -354,6 +354,7 @@ pub(crate) async fn add(
&requirements,
&constraints,
&[],
&[],
None,
&client_builder,
)

View file

@ -485,6 +485,7 @@ async fn do_lock(
let required_members = target.required_members();
let requirements = target.requirements();
let overrides = target.overrides();
let excludes = target.exclude_dependencies();
let constraints = target.constraints();
let build_constraints = target.build_constraints();
let dependency_groups = target.dependency_groups()?;
@ -764,6 +765,7 @@ async fn do_lock(
&dependency_groups,
&constraints,
&overrides,
&excludes,
&build_constraints,
&conflicts,
environments,
@ -887,6 +889,7 @@ async fn do_lock(
.cloned()
.map(UnresolvedRequirementSpecification::from)
.collect(),
excludes.clone(),
source_trees,
// The root is always null in workspaces, it "depends on" the projects
None,
@ -925,6 +928,7 @@ async fn do_lock(
requirements,
constraints,
overrides,
excludes.clone(),
build_constraints,
dependency_groups,
dependency_metadata.values().cloned(),
@ -983,6 +987,7 @@ impl ValidatedLock {
dependency_groups: &BTreeMap<GroupName, Vec<Requirement>>,
constraints: &[Requirement],
overrides: &[Requirement],
excludes: &[PackageName],
build_constraints: &[Requirement],
conflicts: &Conflicts,
environments: Option<&SupportedEnvironments>,
@ -1213,6 +1218,7 @@ impl ValidatedLock {
requirements,
constraints,
overrides,
excludes,
build_constraints,
dependency_groups,
dependency_metadata,
@ -1305,6 +1311,13 @@ impl ValidatedLock {
);
Ok(Self::Preferable(lock))
}
SatisfiesResult::MismatchedExcludes(expected, actual) => {
debug!(
"Resolving despite existing lockfile due to mismatched excludes:\n Requested: {:?}\n Existing: {:?}",
expected, actual
);
Ok(Self::Preferable(lock))
}
SatisfiesResult::MismatchedBuildConstraints(expected, actual) => {
debug!(
"Resolving despite existing lockfile due to mismatched build constraints:\n Requested: {:?}\n Existing: {:?}",

View file

@ -62,6 +62,23 @@ impl<'lock> LockTarget<'lock> {
}
}
/// Returns the set of dependency exclusions for the [`LockTarget`].
pub(crate) fn exclude_dependencies(self) -> Vec<uv_normalize::PackageName> {
match self {
Self::Workspace(workspace) => workspace.exclude_dependencies(),
Self::Script(script) => script
.metadata
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.exclude_dependencies.as_ref())
.into_iter()
.flatten()
.cloned()
.collect(),
}
}
/// Returns the set of constraints for the [`LockTarget`].
pub(crate) fn constraints(self) -> Vec<uv_pep508::Requirement<VerbatimParsedUrl>> {
match self {

View file

@ -1878,6 +1878,7 @@ pub(crate) async fn resolve_environment(
requirements,
constraints,
overrides,
excludes,
source_trees,
..
} = spec.requirements;
@ -1997,6 +1998,7 @@ pub(crate) async fn resolve_environment(
requirements,
constraints,
overrides,
excludes,
source_trees,
project,
BTreeSet::default(),
@ -2234,6 +2236,7 @@ pub(crate) async fn update_environment(
requirements,
constraints,
overrides,
excludes,
source_trees,
..
} = spec;
@ -2366,6 +2369,7 @@ pub(crate) async fn update_environment(
requirements,
constraints,
overrides,
excludes,
source_trees,
project,
BTreeSet::default(),

View file

@ -53,6 +53,7 @@ pub(crate) async fn install(
with: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
excludes: &[RequirementsSource],
build_constraints: &[RequirementsSource],
entrypoints: &[PackageName],
python: Option<String>,
@ -251,6 +252,7 @@ pub(crate) async fn install(
with,
constraints,
overrides,
excludes,
None,
&client_builder,
)

View file

@ -871,8 +871,14 @@ async fn get_or_create_environment(
};
// Read the `--with` requirements.
let spec =
RequirementsSpecification::from_sources(with, constraints, overrides, None, client_builder)
let spec = RequirementsSpecification::from_sources(
with,
constraints,
overrides,
&[],
None,
client_builder,
)
.await?;
// Resolve the `--from` and `--with` requirements.

View file

@ -568,6 +568,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.into_iter()
.map(RequirementsSource::from_overrides_txt)
.collect::<Result<Vec<_>, _>>()?;
let excludes = args
.excludes
.into_iter()
.map(RequirementsSource::from_requirements_txt)
.collect::<Result<Vec<_>, _>>()?;
let build_constraints = args
.build_constraints
.into_iter()
@ -582,9 +587,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&requirements,
&constraints,
&overrides,
&excludes,
&build_constraints,
args.constraints_from_workspace,
args.overrides_from_workspace,
args.excludes_from_workspace,
args.build_constraints_from_workspace,
args.environments,
args.settings.extras,
@ -751,6 +758,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.into_iter()
.map(RequirementsSource::from_overrides_txt)
.collect::<Result<Vec<_>, _>>()?;
let excludes = args
.excludes
.into_iter()
.map(RequirementsSource::from_requirements_txt)
.collect::<Result<Vec<_>, _>>()?;
let build_constraints = args
.build_constraints
.into_iter()
@ -814,9 +826,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&requirements,
&constraints,
&overrides,
&excludes,
&build_constraints,
args.constraints_from_workspace,
args.overrides_from_workspace,
args.excludes_from_workspace,
args.build_constraints_from_workspace,
&args.settings.extras,
&groups,
@ -1376,6 +1390,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.into_iter()
.map(RequirementsSource::from_overrides_txt)
.collect::<Result<Vec<_>, _>>()?;
let excludes = args
.excludes
.into_iter()
.map(RequirementsSource::from_requirements_txt)
.collect::<Result<Vec<_>, _>>()?;
let build_constraints = args
.build_constraints
.into_iter()
@ -1389,6 +1408,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
&requirements,
&constraints,
&overrides,
&excludes,
&build_constraints,
&entrypoints,
args.python,

View file

@ -681,6 +681,7 @@ pub(crate) struct ToolInstallSettings {
pub(crate) with_editable: Vec<String>,
pub(crate) constraints: Vec<PathBuf>,
pub(crate) overrides: Vec<PathBuf>,
pub(crate) excludes: Vec<PathBuf>,
pub(crate) build_constraints: Vec<PathBuf>,
pub(crate) python: Option<String>,
pub(crate) python_platform: Option<TargetTriple>,
@ -710,6 +711,7 @@ impl ToolInstallSettings {
with_executables_from,
constraints,
overrides,
excludes,
build_constraints,
installer,
force,
@ -762,6 +764,10 @@ impl ToolInstallSettings {
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
excludes: excludes
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
build_constraints: build_constraints
.into_iter()
.filter_map(Maybe::into_option)
@ -2119,9 +2125,11 @@ pub(crate) struct PipCompileSettings {
pub(crate) src_file: Vec<PathBuf>,
pub(crate) constraints: Vec<PathBuf>,
pub(crate) overrides: Vec<PathBuf>,
pub(crate) excludes: Vec<PathBuf>,
pub(crate) build_constraints: Vec<PathBuf>,
pub(crate) constraints_from_workspace: Vec<Requirement>,
pub(crate) overrides_from_workspace: Vec<Requirement>,
pub(crate) excludes_from_workspace: Vec<uv_normalize::PackageName>,
pub(crate) build_constraints_from_workspace: Vec<Requirement>,
pub(crate) environments: SupportedEnvironments,
pub(crate) refresh: Refresh,
@ -2139,6 +2147,7 @@ impl PipCompileSettings {
src_file,
constraints,
overrides,
excludes,
extra,
all_extras,
no_all_extras,
@ -2216,6 +2225,15 @@ impl PipCompileSettings {
Vec::new()
};
let excludes_from_workspace = if let Some(configuration) = &filesystem {
configuration
.exclude_dependencies
.clone()
.unwrap_or_default()
} else {
Vec::new()
};
let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
configuration
.build_constraint_dependencies
@ -2251,8 +2269,13 @@ impl PipCompileSettings {
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
excludes: excludes
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
constraints_from_workspace,
overrides_from_workspace,
excludes_from_workspace,
build_constraints_from_workspace,
environments,
refresh: Refresh::from(refresh),
@ -2417,10 +2440,12 @@ pub(crate) struct PipInstallSettings {
pub(crate) editables: Vec<String>,
pub(crate) constraints: Vec<PathBuf>,
pub(crate) overrides: Vec<PathBuf>,
pub(crate) excludes: Vec<PathBuf>,
pub(crate) build_constraints: Vec<PathBuf>,
pub(crate) dry_run: DryRun,
pub(crate) constraints_from_workspace: Vec<Requirement>,
pub(crate) overrides_from_workspace: Vec<Requirement>,
pub(crate) excludes_from_workspace: Vec<uv_normalize::PackageName>,
pub(crate) build_constraints_from_workspace: Vec<Requirement>,
pub(crate) modifications: Modifications,
pub(crate) refresh: Refresh,
@ -2440,6 +2465,7 @@ impl PipInstallSettings {
editable,
constraints,
overrides,
excludes,
build_constraints,
extra,
all_extras,
@ -2503,6 +2529,15 @@ impl PipInstallSettings {
Vec::new()
};
let excludes_from_workspace = if let Some(configuration) = &filesystem {
configuration
.exclude_dependencies
.clone()
.unwrap_or_default()
} else {
Vec::new()
};
let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
configuration
.build_constraint_dependencies
@ -2529,6 +2564,10 @@ impl PipInstallSettings {
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
excludes: excludes
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
build_constraints: build_constraints
.into_iter()
.filter_map(Maybe::into_option)
@ -2536,6 +2575,7 @@ impl PipInstallSettings {
dry_run: DryRun::from_args(dry_run),
constraints_from_workspace,
overrides_from_workspace,
excludes_from_workspace,
build_constraints_from_workspace,
modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
Modifications::Exact

View file

@ -1774,6 +1774,203 @@ fn lock_project_with_override_sources() -> Result<()> {
Ok(())
}
/// Lock a project with `uv.tool.exclude-dependencies`.
#[test]
fn lock_project_with_excludes() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["flask==3.0.0"]
[tool.uv]
exclude-dependencies = ["werkzeug"]
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 8 packages in [TIME]
"###);
// Re-run with `--locked`.
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 8 packages in [TIME]
"###);
// Install the base dependencies from the lockfile.
uv_snapshot!(context.filters(), context.sync().arg("--frozen"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 6 packages in [TIME]
Installed 6 packages in [TIME]
+ blinker==1.7.0
+ click==8.1.7
+ flask==3.0.0
+ itsdangerous==2.1.2
+ jinja2==3.1.3
+ markupsafe==2.1.5
"###);
// Check the lockfile contains the excludes.
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock"))?;
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r#"
version = 1
revision = 3
requires-python = ">=3.12"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[manifest]
excludes = ["werkzeug"]
[[package]]
name = "blinker"
version = "1.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182", size = 28134, upload-time = "2023-11-01T22:06:01.588Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", size = 13068, upload-time = "2023-11-01T22:06:00.162Z" },
]
[[package]]
name = "click"
version = "8.1.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121, upload-time = "2023-08-17T17:29:11.868Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941, upload-time = "2023-08-17T17:29:10.08Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "flask"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blinker" },
{ name = "click" },
{ name = "itsdangerous" },
{ name = "jinja2" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/09/c1a7354d3925a3c6c8cfdebf4245bae67d633ffda1ba415add06ffc839c5/flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58", size = 674171, upload-time = "2023-09-30T14:36:12.918Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", size = 99724, upload-time = "2023-09-30T14:36:10.961Z" },
]
[[package]]
name = "itsdangerous"
version = "2.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143, upload-time = "2022-03-24T15:12:15.102Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749, upload-time = "2022-03-24T15:12:13.2Z" },
]
[[package]]
name = "jinja2"
version = "3.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b2/5e/3a21abf3cd467d7876045335e681d276ac32492febe6d98ad89562d1a7e1/Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90", size = 268261, upload-time = "2024-01-10T23:12:21.133Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/6d/6de6be2d02603ab56e72997708809e8a5b0fbfee080735109b40a3564843/Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", size = 133236, upload-time = "2024-01-10T23:12:19.504Z" },
]
[[package]]
name = "markupsafe"
version = "2.1.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384, upload-time = "2024-02-02T16:31:22.863Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215, upload-time = "2024-02-02T16:30:33.081Z" },
{ url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069, upload-time = "2024-02-02T16:30:34.148Z" },
{ url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452, upload-time = "2024-02-02T16:30:35.149Z" },
{ url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462, upload-time = "2024-02-02T16:30:36.166Z" },
{ url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869, upload-time = "2024-02-02T16:30:37.834Z" },
{ url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906, upload-time = "2024-02-02T16:30:39.366Z" },
{ url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296, upload-time = "2024-02-02T16:30:40.413Z" },
{ url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038, upload-time = "2024-02-02T16:30:42.243Z" },
{ url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572, upload-time = "2024-02-02T16:30:43.326Z" },
{ url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127, upload-time = "2024-02-02T16:30:44.418Z" },
]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "flask" },
]
[package.metadata]
requires-dist = [{ name = "flask", specifier = "==3.0.0" }]
"#
);
});
// Modify the excludes and verify that `--locked` fails.
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["flask==3.0.0"]
[tool.uv]
exclude-dependencies = ["jinja2"]
"#,
)?;
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
Resolved 8 packages in [TIME]
The lockfile at `uv.lock` needs to be updated, but `--locked` was provided. To update the lockfile, run `uv lock`.
");
Ok(())
}
/// Lock a project with `uv.tool.constraint-dependencies`.
#[test]
fn lock_project_with_constraints() -> Result<()> {

View file

@ -98,9 +98,11 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -299,9 +301,11 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -501,9 +505,11 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -735,9 +741,11 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -938,9 +946,11 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -1117,9 +1127,11 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -1345,9 +1357,11 @@ fn resolve_index_url() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -1581,9 +1595,11 @@ fn resolve_index_url() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -1875,9 +1891,11 @@ fn resolve_find_links() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -2100,9 +2118,11 @@ fn resolve_top_level() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -2284,9 +2304,11 @@ fn resolve_top_level() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -2518,9 +2540,11 @@ fn resolve_top_level() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -2775,9 +2799,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -2949,9 +2975,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -3123,9 +3151,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -3299,9 +3329,11 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -3496,6 +3528,7 @@ fn resolve_tool() -> anyhow::Result<()> {
with_editable: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
python: None,
python_platform: None,
@ -3681,9 +3714,11 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -3889,9 +3924,11 @@ fn resolve_both() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -4136,9 +4173,11 @@ fn resolve_both_special_fields() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -4462,9 +4501,11 @@ fn resolve_config_file() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -4642,7 +4683,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
|
1 | [project]
| ^^^^^^^
unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `extra-build-variables`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `config-settings-package`, `no-build-isolation`, `no-build-isolation-package`, `extra-build-dependencies`, `extra-build-variables`, `exclude-newer`, `exclude-newer-package`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `exclude-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
"
);
@ -4763,9 +4804,11 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -4940,9 +4983,11 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -5136,9 +5181,11 @@ fn allow_insecure_host() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -5324,9 +5371,11 @@ fn index_priority() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -5560,9 +5609,11 @@ fn index_priority() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -5802,9 +5853,11 @@ fn index_priority() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -6039,9 +6092,11 @@ fn index_priority() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -6283,9 +6338,11 @@ fn index_priority() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -6520,9 +6577,11 @@ fn index_priority() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -6771,10 +6830,12 @@ fn verify_hashes() -> anyhow::Result<()> {
editables: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
dry_run: Disabled,
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
modifications: Sufficient,
refresh: None(
@ -6938,10 +6999,12 @@ fn verify_hashes() -> anyhow::Result<()> {
editables: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
dry_run: Disabled,
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
modifications: Sufficient,
refresh: None(
@ -7103,10 +7166,12 @@ fn verify_hashes() -> anyhow::Result<()> {
editables: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
dry_run: Disabled,
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
modifications: Sufficient,
refresh: None(
@ -7270,10 +7335,12 @@ fn verify_hashes() -> anyhow::Result<()> {
editables: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
dry_run: Disabled,
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
modifications: Sufficient,
refresh: None(
@ -7435,10 +7502,12 @@ fn verify_hashes() -> anyhow::Result<()> {
editables: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
dry_run: Disabled,
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
modifications: Sufficient,
refresh: None(
@ -7601,10 +7670,12 @@ fn verify_hashes() -> anyhow::Result<()> {
editables: [],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
dry_run: Disabled,
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
modifications: Sufficient,
refresh: None(
@ -8488,9 +8559,11 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -8663,9 +8736,11 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -8861,9 +8936,11 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -9034,9 +9111,11 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -9201,9 +9280,11 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -9369,9 +9450,11 @@ fn upgrade_pip_cli_config_interaction() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -10367,9 +10450,11 @@ fn build_isolation_override() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],
@ -10537,9 +10622,11 @@ fn build_isolation_override() -> anyhow::Result<()> {
],
constraints: [],
overrides: [],
excludes: [],
build_constraints: [],
constraints_from_workspace: [],
overrides_from_workspace: [],
excludes_from_workspace: [],
build_constraints_from_workspace: [],
environments: SupportedEnvironments(
[],

View file

@ -2613,7 +2613,9 @@ uv tool install [OPTIONS] <PACKAGE>
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p></dd><dt id="uv-tool-install--exclude-newer-package"><a href="#uv-tool-install--exclude-newer-package"><code>--exclude-newer-package</code></a> <i>exclude-newer-package</i></dt><dd><p>Limit candidate packages for specific packages to those that were uploaded prior to the given date.</p>
<p>Accepts package-date pairs in the format <code>PACKAGE=DATE</code>, where <code>DATE</code> is an RFC 3339 timestamp (e.g., <code>2006-12-02T02:07:43Z</code>) or local date (e.g., <code>2006-12-02</code>) in your system's configured time zone.</p>
<p>Can be provided multiple times for different packages.</p>
</dd><dt id="uv-tool-install--extra-index-url"><a href="#uv-tool-install--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
</dd><dt id="uv-tool-install--excludes"><a href="#uv-tool-install--excludes"><code>--excludes</code></a>, <code>--exclude</code> <i>excludes</i></dt><dd><p>Exclude packages from resolution using the given requirements files.</p>
<p>Excludes files are <code>requirements.txt</code>-like files that specify packages to exclude from the resolution. When a package is excluded, it will be omitted from the dependency list entirely and its own dependencies will be ignored during the resolution phase. Excludes are unconditional in that requirement specifiers and markers are ignored; any package listed in the provided file will be omitted from all resolved environments.</p>
<p>May also be set with the <code>UV_EXCLUDE</code> environment variable.</p></dd><dt id="uv-tool-install--extra-index-url"><a href="#uv-tool-install--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
<p>All indexes provided via this flag take priority over the index specified by <code>--index-url</code> (which defaults to PyPI). When multiple <code>--extra-index-url</code> flags are provided, earlier values take priority.</p>
<p>May also be set with the <code>UV_EXTRA_INDEX_URL</code> environment variable.</p></dd><dt id="uv-tool-install--find-links"><a href="#uv-tool-install--find-links"><code>--find-links</code></a>, <code>-f</code> <i>find-links</i></dt><dd><p>Locations to search for candidate distributions, in addition to those found in the registry indexes.</p>
@ -4054,7 +4056,9 @@ uv pip compile [OPTIONS] <SRC_FILE|--group <GROUP>>
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p></dd><dt id="uv-pip-compile--exclude-newer-package"><a href="#uv-pip-compile--exclude-newer-package"><code>--exclude-newer-package</code></a> <i>exclude-newer-package</i></dt><dd><p>Limit candidate packages for a specific package to those that were uploaded prior to the given date.</p>
<p>Accepts package-date pairs in the format <code>PACKAGE=DATE</code>, where <code>DATE</code> is an RFC 3339 timestamp (e.g., <code>2006-12-02T02:07:43Z</code>) or local date (e.g., <code>2006-12-02</code>) in your system's configured time zone.</p>
<p>Can be provided multiple times for different packages.</p>
</dd><dt id="uv-pip-compile--extra"><a href="#uv-pip-compile--extra"><code>--extra</code></a> <i>extra</i></dt><dd><p>Include optional dependencies from the specified extra name; may be provided more than once.</p>
</dd><dt id="uv-pip-compile--excludes"><a href="#uv-pip-compile--excludes"><code>--excludes</code></a>, <code>--exclude</code> <i>excludes</i></dt><dd><p>Exclude packages from resolution using the given requirements files.</p>
<p>Excludes files are <code>requirements.txt</code>-like files that specify packages to exclude from the resolution. When a package is excluded, it will be omitted from the dependency list entirely and its own dependencies will be ignored during the resolution phase. Excludes are unconditional in that requirement specifiers and markers are ignored; any package listed in the provided file will be omitted from all resolved environments.</p>
<p>May also be set with the <code>UV_EXCLUDE</code> environment variable.</p></dd><dt id="uv-pip-compile--extra"><a href="#uv-pip-compile--extra"><code>--extra</code></a> <i>extra</i></dt><dd><p>Include optional dependencies from the specified extra name; may be provided more than once.</p>
<p>Only applies to <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
</dd><dt id="uv-pip-compile--extra-index-url"><a href="#uv-pip-compile--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
@ -4647,7 +4651,9 @@ uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDIT
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p></dd><dt id="uv-pip-install--exclude-newer-package"><a href="#uv-pip-install--exclude-newer-package"><code>--exclude-newer-package</code></a> <i>exclude-newer-package</i></dt><dd><p>Limit candidate packages for specific packages to those that were uploaded prior to the given date.</p>
<p>Accepts package-date pairs in the format <code>PACKAGE=DATE</code>, where <code>DATE</code> is an RFC 3339 timestamp (e.g., <code>2006-12-02T02:07:43Z</code>) or local date (e.g., <code>2006-12-02</code>) in your system's configured time zone.</p>
<p>Can be provided multiple times for different packages.</p>
</dd><dt id="uv-pip-install--extra"><a href="#uv-pip-install--extra"><code>--extra</code></a> <i>extra</i></dt><dd><p>Include optional dependencies from the specified extra name; may be provided more than once.</p>
</dd><dt id="uv-pip-install--excludes"><a href="#uv-pip-install--excludes"><code>--excludes</code></a>, <code>--exclude</code> <i>excludes</i></dt><dd><p>Exclude packages from resolution using the given requirements files.</p>
<p>Excludes files are <code>requirements.txt</code>-like files that specify packages to exclude from the resolution. When a package is excluded, it will be omitted from the dependency list entirely and its own dependencies will be ignored during the resolution phase. Excludes are unconditional in that requirement specifiers and markers are ignored; any package listed in the provided file will be omitted from all resolved environments.</p>
<p>May also be set with the <code>UV_EXCLUDE</code> environment variable.</p></dd><dt id="uv-pip-install--extra"><a href="#uv-pip-install--extra"><code>--extra</code></a> <i>extra</i></dt><dd><p>Include optional dependencies from the specified extra name; may be provided more than once.</p>
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
</dd><dt id="uv-pip-install--extra-index-url"><a href="#uv-pip-install--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>

View file

@ -15,7 +15,7 @@ Python can lead to unexpected behavior.
### `UV_BUILD_CONSTRAINT`
<small class="added-in">added in `0.2.34`</small>
Equivalent to the `--build-constraint` command-line argument. If set, uv will use this file
Equivalent to the `--build-constraints` command-line argument. If set, uv will use this file
as constraints for any source distribution builds. Uses space-separated list of files.
### `UV_CACHE_DIR`
@ -62,7 +62,7 @@ local `uv.toml` file to use as the configuration file.
### `UV_CONSTRAINT`
<small class="added-in">added in `0.1.36`</small>
Equivalent to the `--constraint` command-line argument. If set, uv will use this
Equivalent to the `--constraints` command-line argument. If set, uv will use this
file as the constraints file. Uses space-separated list of files.
### `UV_CREDENTIALS_DIR`
@ -103,6 +103,12 @@ compatibility.
`.env` files from which to load environment variables when executing `uv run` commands.
### `UV_EXCLUDE`
<small class="added-in">added in `0.9.8`</small>
Equivalent to the `--excludes` command-line argument. If set, uv will use this
as the excludes file. Uses space-separated list of files.
### `UV_EXCLUDE_NEWER`
<small class="added-in">added in `0.2.12`</small>
@ -405,7 +411,7 @@ Equivalent to the `--offline` command-line argument. If set, uv will disable net
### `UV_OVERRIDE`
<small class="added-in">added in `0.2.22`</small>
Equivalent to the `--override` command-line argument. If set, uv will use this file
Equivalent to the `--overrides` command-line argument. If set, uv will use this file
as the overrides file. Uses space-separated list of files.
### `UV_PRERELEASE`

View file

@ -202,6 +202,37 @@ environments = ["sys_platform == 'darwin'"]
---
### [`exclude-dependencies`](#exclude-dependencies) {: #exclude-dependencies }
Dependencies to exclude when resolving the project's dependencies.
Excludes are used to prevent a package from being selected during resolution,
regardless of whether it's requested by any other package. When a package is excluded,
it will be omitted from the dependency list entirely.
Including a package as an exclusion will prevent it from being installed, even if
it's requested by transitive dependencies. This can be useful for removing optional
dependencies or working around packages with broken dependencies.
!!! note
In `uv lock`, `uv sync`, and `uv run`, uv will only read `exclude-dependencies` from
the `pyproject.toml` at the workspace root, and will ignore any declarations in other
workspace members or `uv.toml` files.
**Default value**: `[]`
**Type**: `list[str]`
**Example usage**:
```toml title="pyproject.toml"
[tool.uv]
# Exclude Werkzeug from being installed, even if transitive dependencies request it.
exclude-dependencies = ["werkzeug"]
```
---
### [`index`](#index) {: #index }
The indexes to use when resolving dependencies.

10
uv.schema.json generated
View file

@ -203,6 +203,16 @@
"type": "string"
}
},
"exclude-dependencies": {
"description": "Package names to exclude, e.g., `werkzeug`, `numpy`.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"exclude-newer": {
"description": "Limit candidate packages to those that were uploaded prior to a given point in time.\n\nAccepts a superset of [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html) (e.g.,\n`2006-12-02T02:07:43Z`). A full timestamp is required to ensure that the resolver will\nbehave consistently across timezones.",
"anyOf": [