mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Add tool.uv.build-constraint-dependencies
to pyproject.toml
(#11585)
## Summary Resolves #6913. Add `tool.uv.build-constraint-dependencies` to pyproject.toml. The changes are analogous to the constraint-dependencies feature implemented in #5248. Add documentation for `build-constraint-dependencies` ## Test Plan Add tests for `uv lock`, `uv add`, `uv pip install` and `uv pip compile`. --------- Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
parent
8996d5c358
commit
8c3a6b2155
19 changed files with 879 additions and 8 deletions
|
@ -331,6 +331,7 @@ pub struct ToolUv {
|
|||
pub top_level: ResolverInstallerOptions,
|
||||
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
pub sources: Option<BTreeMap<PackageName, Sources>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,9 @@ pub struct Options {
|
|||
#[cfg_attr(feature = "schemars", schemars(skip))]
|
||||
pub constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
|
||||
|
||||
#[cfg_attr(feature = "schemars", schemars(skip))]
|
||||
pub build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
|
||||
|
||||
#[cfg_attr(feature = "schemars", schemars(skip))]
|
||||
pub environments: Option<SupportedEnvironments>,
|
||||
|
||||
|
@ -1792,6 +1795,7 @@ pub struct OptionsWire {
|
|||
// They're respected in both `pyproject.toml` and `uv.toml` files.
|
||||
override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
|
||||
constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
|
||||
build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
|
||||
environments: Option<SupportedEnvironments>,
|
||||
required_environments: Option<SupportedEnvironments>,
|
||||
|
||||
|
@ -1858,6 +1862,7 @@ impl From<OptionsWire> for Options {
|
|||
cache_keys,
|
||||
override_dependencies,
|
||||
constraint_dependencies,
|
||||
build_constraint_dependencies,
|
||||
environments,
|
||||
required_environments,
|
||||
conflicts,
|
||||
|
@ -1922,6 +1927,7 @@ impl From<OptionsWire> for Options {
|
|||
cache_keys,
|
||||
override_dependencies,
|
||||
constraint_dependencies,
|
||||
build_constraint_dependencies,
|
||||
environments,
|
||||
required_environments,
|
||||
install_mirrors: PythonInstallMirrors::resolve(
|
||||
|
|
|
@ -479,6 +479,37 @@ pub struct ToolUv {
|
|||
)]
|
||||
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
|
||||
/// Constraints to apply when solving build dependencies.
|
||||
///
|
||||
/// Build constraints are used to restrict the versions of build dependencies that are selected
|
||||
/// when building a package during resolution or installation.
|
||||
///
|
||||
/// Including a package as a constraint will _not_ trigger installation of the package during
|
||||
/// a build; instead, the package must be requested elsewhere in the project's build dependency
|
||||
/// graph.
|
||||
///
|
||||
/// !!! note
|
||||
/// In `uv lock`, `uv sync`, and `uv run`, uv will only read `build-constraint-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 = "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`."
|
||||
)
|
||||
)]
|
||||
#[option(
|
||||
default = "[]",
|
||||
value_type = "list[str]",
|
||||
example = r#"
|
||||
# Ensure that the setuptools v60.0.0 is used whenever a package has a build dependency
|
||||
# on setuptools.
|
||||
build-constraint-dependencies = ["setuptools==60.0.0"]
|
||||
"#
|
||||
)]
|
||||
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
|
||||
/// A list of supported environments against which to resolve dependencies.
|
||||
///
|
||||
/// By default, uv will resolve for all possible environments during a `uv lock` operation.
|
||||
|
|
|
@ -496,6 +496,20 @@ impl Workspace {
|
|||
constraints.clone()
|
||||
}
|
||||
|
||||
/// Returns the set of build constraints for the workspace.
|
||||
pub fn build_constraints(&self) -> Vec<uv_pep508::Requirement<VerbatimParsedUrl>> {
|
||||
let Some(build_constraints) = self
|
||||
.pyproject_toml
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.build_constraint_dependencies.as_ref())
|
||||
else {
|
||||
return vec![];
|
||||
};
|
||||
build_constraints.clone()
|
||||
}
|
||||
|
||||
/// The path to the workspace root, the directory containing the top level `pyproject.toml` with
|
||||
/// the `uv.tool.workspace`, or the `pyproject.toml` in an implicit single workspace project.
|
||||
pub fn install_path(&self) -> &PathBuf {
|
||||
|
@ -1725,6 +1739,7 @@ mod tests {
|
|||
"dev-dependencies": null,
|
||||
"override-dependencies": null,
|
||||
"constraint-dependencies": null,
|
||||
"build-constraint-dependencies": null,
|
||||
"environments": null,
|
||||
"required-environments": null,
|
||||
"conflicts": null
|
||||
|
@ -1818,6 +1833,7 @@ mod tests {
|
|||
"dev-dependencies": null,
|
||||
"override-dependencies": null,
|
||||
"constraint-dependencies": null,
|
||||
"build-constraint-dependencies": null,
|
||||
"environments": null,
|
||||
"required-environments": null,
|
||||
"conflicts": null
|
||||
|
@ -2025,6 +2041,7 @@ mod tests {
|
|||
"dev-dependencies": null,
|
||||
"override-dependencies": null,
|
||||
"constraint-dependencies": null,
|
||||
"build-constraint-dependencies": null,
|
||||
"environments": null,
|
||||
"required-environments": null,
|
||||
"conflicts": null
|
||||
|
@ -2130,6 +2147,7 @@ mod tests {
|
|||
"dev-dependencies": null,
|
||||
"override-dependencies": null,
|
||||
"constraint-dependencies": null,
|
||||
"build-constraint-dependencies": null,
|
||||
"environments": null,
|
||||
"required-environments": null,
|
||||
"conflicts": null
|
||||
|
@ -2248,6 +2266,7 @@ mod tests {
|
|||
"dev-dependencies": null,
|
||||
"override-dependencies": null,
|
||||
"constraint-dependencies": null,
|
||||
"build-constraint-dependencies": null,
|
||||
"environments": null,
|
||||
"required-environments": null,
|
||||
"conflicts": null
|
||||
|
@ -2340,6 +2359,7 @@ mod tests {
|
|||
"dev-dependencies": null,
|
||||
"override-dependencies": null,
|
||||
"constraint-dependencies": null,
|
||||
"build-constraint-dependencies": null,
|
||||
"environments": null,
|
||||
"required-environments": null,
|
||||
"conflicts": null
|
||||
|
|
|
@ -56,6 +56,7 @@ pub(crate) async fn pip_compile(
|
|||
build_constraints: &[RequirementsSource],
|
||||
constraints_from_workspace: Vec<Requirement>,
|
||||
overrides_from_workspace: Vec<Requirement>,
|
||||
build_constraints_from_workspace: Vec<Requirement>,
|
||||
environments: SupportedEnvironments,
|
||||
extras: ExtrasSpecification,
|
||||
groups: DevGroupsSpecification,
|
||||
|
@ -184,8 +185,17 @@ pub(crate) async fn pip_compile(
|
|||
.collect();
|
||||
|
||||
// Read build constraints.
|
||||
let build_constraints =
|
||||
operations::read_constraints(build_constraints, &client_builder).await?;
|
||||
let build_constraints: Vec<NameRequirementSpecification> =
|
||||
operations::read_constraints(build_constraints, &client_builder)
|
||||
.await?
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(
|
||||
build_constraints_from_workspace
|
||||
.into_iter()
|
||||
.map(NameRequirementSpecification::from),
|
||||
)
|
||||
.collect();
|
||||
|
||||
// If all the metadata could be statically resolved, validate that every extra was used. If we
|
||||
// need to resolve metadata via PEP 517, we don't know which extras are used until much later.
|
||||
|
|
|
@ -51,6 +51,7 @@ pub(crate) async fn pip_install(
|
|||
build_constraints: &[RequirementsSource],
|
||||
constraints_from_workspace: Vec<Requirement>,
|
||||
overrides_from_workspace: Vec<Requirement>,
|
||||
build_constraints_from_workspace: Vec<Requirement>,
|
||||
extras: &ExtrasSpecification,
|
||||
groups: &DevGroupsSpecification,
|
||||
resolution_mode: ResolutionMode,
|
||||
|
@ -123,10 +124,6 @@ pub(crate) async fn pip_install(
|
|||
)
|
||||
.await?;
|
||||
|
||||
// Read build constraints.
|
||||
let build_constraints =
|
||||
operations::read_constraints(build_constraints, &client_builder).await?;
|
||||
|
||||
let constraints: Vec<NameRequirementSpecification> = constraints
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -147,6 +144,20 @@ pub(crate) async fn pip_install(
|
|||
)
|
||||
.collect();
|
||||
|
||||
// Read build constraints.
|
||||
let build_constraints: Vec<NameRequirementSpecification> =
|
||||
operations::read_constraints(build_constraints, &client_builder)
|
||||
.await?
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(
|
||||
build_constraints_from_workspace
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(NameRequirementSpecification::from),
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Detect the current Python interpreter.
|
||||
let environment = if target.is_some() || prefix.is_some() {
|
||||
let installation = PythonInstallation::find(
|
||||
|
|
|
@ -366,6 +366,7 @@ async fn do_lock(
|
|||
let requirements = target.requirements();
|
||||
let overrides = target.overrides();
|
||||
let constraints = target.constraints();
|
||||
let build_constraints = target.build_constraints();
|
||||
let dependency_groups = target.dependency_groups()?;
|
||||
let source_trees = vec![];
|
||||
|
||||
|
@ -373,6 +374,7 @@ async fn do_lock(
|
|||
let requirements = target.lower(requirements, index_locations, sources)?;
|
||||
let overrides = target.lower(overrides, index_locations, sources)?;
|
||||
let constraints = target.lower(constraints, index_locations, sources)?;
|
||||
let build_constraints = target.lower(build_constraints, index_locations, sources)?;
|
||||
let dependency_groups = dependency_groups
|
||||
.into_iter()
|
||||
.map(|(name, requirements)| {
|
||||
|
@ -556,9 +558,10 @@ async fn do_lock(
|
|||
.build();
|
||||
let hasher = HashStrategy::Generate(HashGeneration::Url);
|
||||
|
||||
let build_constraints = Constraints::from_requirements(build_constraints.iter().cloned());
|
||||
|
||||
// TODO(charlie): These are all default values. We should consider whether we want to make them
|
||||
// optional on the downstream APIs.
|
||||
let build_constraints = Constraints::default();
|
||||
let build_hasher = HashStrategy::default();
|
||||
let extras = ExtrasSpecification::default();
|
||||
let groups = DevGroupsSpecification::default();
|
||||
|
|
|
@ -79,6 +79,23 @@ impl<'lock> LockTarget<'lock> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the set of build constraints for the [`LockTarget`].
|
||||
pub(crate) fn build_constraints(self) -> Vec<uv_pep508::Requirement<VerbatimParsedUrl>> {
|
||||
match self {
|
||||
Self::Workspace(workspace) => workspace.build_constraints(),
|
||||
Self::Script(script) => script
|
||||
.metadata
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.build_constraint_dependencies.as_ref())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the dependency groups that are attached to the target directly, as opposed to being
|
||||
/// attached to any members within the target.
|
||||
pub(crate) fn dependency_groups(
|
||||
|
|
|
@ -389,6 +389,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
&build_constraints,
|
||||
args.constraints_from_workspace,
|
||||
args.overrides_from_workspace,
|
||||
args.build_constraints_from_workspace,
|
||||
args.environments,
|
||||
args.settings.extras,
|
||||
args.settings.groups,
|
||||
|
@ -561,6 +562,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
&build_constraints,
|
||||
args.constraints_from_workspace,
|
||||
args.overrides_from_workspace,
|
||||
args.build_constraints_from_workspace,
|
||||
&args.settings.extras,
|
||||
&args.settings.groups,
|
||||
args.settings.resolution,
|
||||
|
|
|
@ -1540,6 +1540,7 @@ pub(crate) struct PipCompileSettings {
|
|||
pub(crate) build_constraints: Vec<PathBuf>,
|
||||
pub(crate) constraints_from_workspace: Vec<Requirement>,
|
||||
pub(crate) overrides_from_workspace: Vec<Requirement>,
|
||||
pub(crate) build_constraints_from_workspace: Vec<Requirement>,
|
||||
pub(crate) environments: SupportedEnvironments,
|
||||
pub(crate) refresh: Refresh,
|
||||
pub(crate) settings: PipSettings,
|
||||
|
@ -1626,6 +1627,20 @@ impl PipCompileSettings {
|
|||
Vec::new()
|
||||
};
|
||||
|
||||
let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
|
||||
configuration
|
||||
.build_constraint_dependencies
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|requirement| {
|
||||
Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let environments = if let Some(configuration) = &filesystem {
|
||||
configuration.environments.clone().unwrap_or_default()
|
||||
} else {
|
||||
|
@ -1648,6 +1663,7 @@ impl PipCompileSettings {
|
|||
.collect(),
|
||||
constraints_from_workspace,
|
||||
overrides_from_workspace,
|
||||
build_constraints_from_workspace,
|
||||
environments,
|
||||
refresh: Refresh::from(refresh),
|
||||
settings: PipSettings::combine(
|
||||
|
@ -1783,6 +1799,7 @@ pub(crate) struct PipInstallSettings {
|
|||
pub(crate) dry_run: DryRun,
|
||||
pub(crate) constraints_from_workspace: Vec<Requirement>,
|
||||
pub(crate) overrides_from_workspace: Vec<Requirement>,
|
||||
pub(crate) build_constraints_from_workspace: Vec<Requirement>,
|
||||
pub(crate) modifications: Modifications,
|
||||
pub(crate) refresh: Refresh,
|
||||
pub(crate) settings: PipSettings,
|
||||
|
@ -1858,6 +1875,20 @@ impl PipInstallSettings {
|
|||
Vec::new()
|
||||
};
|
||||
|
||||
let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
|
||||
configuration
|
||||
.build_constraint_dependencies
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|requirement| {
|
||||
Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Self {
|
||||
package,
|
||||
requirements,
|
||||
|
@ -1877,6 +1908,7 @@ impl PipInstallSettings {
|
|||
dry_run: DryRun::from_args(dry_run),
|
||||
constraints_from_workspace,
|
||||
overrides_from_workspace,
|
||||
build_constraints_from_workspace,
|
||||
modifications: if flag(exact, inexact).unwrap_or(false) {
|
||||
Modifications::Exact
|
||||
} else {
|
||||
|
|
|
@ -9714,3 +9714,59 @@ fn repeated_index_cli_reversed() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_with_build_constraints() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = []
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = ["setuptools==1"]
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.add().arg("requests==1.2"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable.
|
||||
help: `requests` (v1.2.0) was included because `project` (v0.1.0) depends on `requests==1.2`
|
||||
");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = []
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = ["setuptools>=40"]
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.add().arg("requests==1.2"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ requests==1.2.0
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1883,6 +1883,146 @@ fn lock_project_with_constraint_sources() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Lock a project with `uv.tool.build-constraint-dependencies`.
|
||||
#[test]
|
||||
fn lock_project_with_build_constraints() -> 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 = ["anyio==3.7.0"]
|
||||
|
||||
# This should be ignored because none of the dependencies requires `setuptools`
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = ["setuptools==1"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock(), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 4 packages in [TIME]
|
||||
"###);
|
||||
|
||||
// Re-run with `--locked`.
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 4 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 3 packages in [TIME]
|
||||
Installed 3 packages in [TIME]
|
||||
+ anyio==3.7.0
|
||||
+ idna==3.6
|
||||
+ sniffio==1.3.1
|
||||
"###);
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["requests==1.2"]
|
||||
|
||||
# This should fail the operation, since `requests` depends on `setuptools` at build time.
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = ["setuptools==1"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock(), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable.
|
||||
help: `requests` (v1.2.0) was included because `project` (v0.1.0) depends on `requests==1.2`
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lock a project with `uv.tool.build-constraint-dependencies` that reference `tool.uv.sources`.
|
||||
#[test]
|
||||
fn lock_project_with_build_constraint_sources() -> Result<()> {
|
||||
let context = TestContext::new("3.9");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["requests==1.2"]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = ["setuptools==75.8.0"]
|
||||
|
||||
[tool.uv.sources]
|
||||
setuptools = { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl" }
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock(), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
"###);
|
||||
|
||||
// Re-run with `--locked`.
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 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 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ requests==1.2.0
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lock a project with a dependency that has an extra.
|
||||
#[test]
|
||||
fn lock_dependency_extra() -> Result<()> {
|
||||
|
|
|
@ -13572,6 +13572,259 @@ fn compatible_build_constraint() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Include `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint_in_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests==1.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools==1",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("pyproject.toml"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include `build-constraint-dependencies` in pyproject.toml with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint_in_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests==1.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools>=40",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("pyproject.toml"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] pyproject.toml
|
||||
requests==1.2.0
|
||||
# via project (pyproject.toml)
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
// incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests==1.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools==1",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("pyproject.toml")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools>=40 and setuptools==1, we can conclude that your requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
// compatible setuptools version in pyproject.toml, incompatible in build_constraints.txt
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools==1")?;
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests==1.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools>=40",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("pyproject.toml")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools==1 and setuptools>=40, we can conclude that your requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Merge CLI args `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
// incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests==1.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools>=1",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("pyproject.toml")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] pyproject.toml --build-constraint build_constraints.txt
|
||||
requests==1.2.0
|
||||
# via project (pyproject.toml)
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
"
|
||||
);
|
||||
|
||||
// compatible setuptools version in pyproject.toml, incompatible in build_constraints.txt
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=1")?;
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests==1.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools>=40",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_compile()
|
||||
.arg("pyproject.toml")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --cache-dir [CACHE_DIR] pyproject.toml --build-constraint build_constraints.txt
|
||||
requests==1.2.0
|
||||
# via project (pyproject.toml)
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure that we treat invalid extra markers as `false`, i.e., in projects that define
|
||||
/// non-spec-compliant extras.
|
||||
#[test]
|
||||
|
|
|
@ -7797,6 +7797,166 @@ fn compatible_build_constraint() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Include `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint_in_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools==1",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Include a `build_constraints.txt` file with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint_in_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools==40.8.0",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ requests==1.2.0
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with an incompatible constraint.
|
||||
#[test]
|
||||
fn incompatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
// Incompatible setuptools version in pyproject.toml, compatible in build_constraints.txt.
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools==1",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools>=40 and setuptools==1, we can conclude that your requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
// Compatible setuptools version in pyproject.toml, incompatible in build_constraints.txt.
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools==1")?;
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools>=40",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
× Failed to download and build `requests==1.2.0`
|
||||
├─▶ Failed to resolve requirements from `setup.py` build
|
||||
├─▶ No solution found when resolving: `setuptools>=40.8.0`
|
||||
╰─▶ Because you require setuptools==1 and setuptools>=40, we can conclude that your requirements are unsatisfiable.
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Merge `build_constraints.txt` with `build-constraint-dependencies` in pyproject.toml with a compatible constraint.
|
||||
#[test]
|
||||
fn compatible_build_constraint_merged_with_pyproject_toml() -> Result<()> {
|
||||
let context = TestContext::new("3.8");
|
||||
|
||||
let constraints_txt = context.temp_dir.child("build_constraints.txt");
|
||||
constraints_txt.write_str("setuptools>=40")?;
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"[tool.uv]
|
||||
build-constraint-dependencies = [
|
||||
"setuptools>=1",
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.pip_install()
|
||||
.arg("requests==1.2")
|
||||
.arg("--build-constraint")
|
||||
.arg("build_constraints.txt"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 1 package in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ requests==1.2.0
|
||||
"###
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn install_build_isolation_package() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
|
|
@ -90,6 +90,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -261,6 +262,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -433,6 +435,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -637,6 +640,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -810,6 +814,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -962,6 +967,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -1158,6 +1164,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -1360,6 +1367,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -1616,6 +1624,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -1811,6 +1820,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -1969,6 +1979,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -2169,6 +2180,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -2393,6 +2405,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -2541,6 +2554,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -2689,6 +2703,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -2839,6 +2854,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -3167,6 +3183,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -3343,6 +3360,7 @@ fn resolve_both() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -3637,6 +3655,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -3793,7 +3812,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`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `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`, `publish-url`, `trusted-publishing`, `check-url`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-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`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `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`, `publish-url`, `trusted-publishing`, `check-url`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dev-dependencies`, `build-backend`
|
||||
"###
|
||||
);
|
||||
|
||||
|
@ -3909,6 +3928,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -4060,6 +4080,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -4230,6 +4251,7 @@ fn allow_insecure_host() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -4392,6 +4414,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -4594,6 +4617,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -4802,6 +4826,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -5005,6 +5030,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -5215,6 +5241,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -5418,6 +5445,7 @@ fn index_priority() -> anyhow::Result<()> {
|
|||
build_constraints: [],
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
environments: SupportedEnvironments(
|
||||
[],
|
||||
),
|
||||
|
@ -5637,6 +5665,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||
dry_run: Disabled,
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
modifications: Sufficient,
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
|
@ -5779,6 +5808,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||
dry_run: Disabled,
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
modifications: Sufficient,
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
|
@ -5919,6 +5949,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||
dry_run: Disabled,
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
modifications: Sufficient,
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
|
@ -6061,6 +6092,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||
dry_run: Disabled,
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
modifications: Sufficient,
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
|
@ -6201,6 +6233,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||
dry_run: Disabled,
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
modifications: Sufficient,
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
|
@ -6342,6 +6375,7 @@ fn verify_hashes() -> anyhow::Result<()> {
|
|||
dry_run: Disabled,
|
||||
constraints_from_workspace: [],
|
||||
overrides_from_workspace: [],
|
||||
build_constraints_from_workspace: [],
|
||||
modifications: Sufficient,
|
||||
refresh: None(
|
||||
Timestamp(
|
||||
|
|
|
@ -125,6 +125,38 @@ $ uv pip compile requirements.in --constraint constraints.txt
|
|||
|
||||
Note that multiple constraints can be defined in each file and multiple files can be used.
|
||||
|
||||
uv will also read `constraint-dependencies` from the `pyproject.toml` at the workspace root, and
|
||||
append them to those specified in the constraints file.
|
||||
|
||||
## Adding build constraints
|
||||
|
||||
Similar to `constraints`, but specifically for build-time dependencies, including those required
|
||||
when building runtime dependencies.
|
||||
|
||||
Build constraint files are `requirements.txt`-like files that only control the _version_ of a
|
||||
build-time requirement. However, including a package in a build constraints file will _not_ trigger
|
||||
its installation at build time; instead, constraints apply only when the package is required as a
|
||||
direct or transitive build-time dependency. Build constraints can be used to add bounds to
|
||||
dependencies that are not explicitly declared as build-time dependencies of the current project.
|
||||
|
||||
For example, if a package defines its build dependencies as follows:
|
||||
|
||||
```toml title="pyproject.toml"
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
```
|
||||
|
||||
Build constraints could be used to ensure that a specific version of `setuptools` is used for every
|
||||
package in the workspace:
|
||||
|
||||
```python title="build-constraints.txt"
|
||||
setuptools==75.0.0
|
||||
```
|
||||
|
||||
uv will also read `build-constraint-dependencies` from the `pyproject.toml` at the workspace root,
|
||||
and append them to those specified in the build constraints file.
|
||||
|
||||
## Overriding dependency versions
|
||||
|
||||
Overrides files are `requirements.txt`-like files that force a specific version of a requirement to
|
||||
|
|
|
@ -1,4 +1,35 @@
|
|||
## Project metadata
|
||||
### [`build-constraint-dependencies`](#build-constraint-dependencies) {: #build-constraint-dependencies }
|
||||
|
||||
Constraints to apply when solving build dependencies.
|
||||
|
||||
Build constraints are used to restrict the versions of build dependencies that are selected
|
||||
when building a package during resolution or installation.
|
||||
|
||||
Including a package as a constraint will _not_ trigger installation of the package during
|
||||
a build; instead, the package must be requested elsewhere in the project's build dependency
|
||||
graph.
|
||||
|
||||
!!! note
|
||||
In `uv lock`, `uv sync`, and `uv run`, uv will only read `build-constraint-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]
|
||||
# Ensure that the setuptools v60.0.0 is used whenever a package has a build dependency
|
||||
# on setuptools.
|
||||
build-constraint-dependencies = ["setuptools==60.0.0"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### [`conflicts`](#conflicts) {: #conflicts }
|
||||
|
||||
Declare collections of extras or dependency groups that are conflicting
|
||||
|
|
|
@ -288,6 +288,28 @@ uv will avoid using an old version of `apache-beam`.
|
|||
Constraints can also be defined for indirect dependencies using `constraints.txt` files or the
|
||||
[`constraint-dependencies`](../settings.md#constraint-dependencies) setting.
|
||||
|
||||
### Old Version of a build dependency is used
|
||||
|
||||
If a package fails to build because `uv` selects an incompatible or outdated version of a build-time
|
||||
dependency, you can enforce constraints specifically for build dependencies. The
|
||||
[`build-constraint-dependencies`](../settings.md#build-constraint-dependencies) setting (or an
|
||||
analogous `build-constraints.txt` file) can be used to ensure that `uv` selects an appropriate
|
||||
version of a given build requirements.
|
||||
|
||||
For example, the issue described in
|
||||
[#5551](https://github.com/astral-sh/uv/issues/5551#issuecomment-2256055975) could be addressed by
|
||||
specifying a build constraint that excludes `setuptools` version `72.0.0`:
|
||||
|
||||
```toml title="pyproject.toml"
|
||||
[tool.uv]
|
||||
# Prevent setuptools version 72.0.0 from being used as a build dependency.
|
||||
build-constraint-dependencies = ["setuptools!=72.0.0"]
|
||||
```
|
||||
|
||||
The build constraint will thus ensure that any package requiring `setuptools` during the build
|
||||
process will avoid using the problematic version, preventing build failures caused by incompatible
|
||||
build dependencies.
|
||||
|
||||
### Package is only needed for an unused platform
|
||||
|
||||
If locking fails due to building a package from a platform you do not need to support, consider
|
||||
|
|
10
uv.schema.json
generated
10
uv.schema.json
generated
|
@ -14,6 +14,16 @@
|
|||
"$ref": "#/definitions/TrustedHost"
|
||||
}
|
||||
},
|
||||
"build-constraint-dependencies": {
|
||||
"description": "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cache-dir": {
|
||||
"description": "Path to the cache directory.\n\nDefaults to `$HOME/Library/Caches/uv` on macOS, `$XDG_CACHE_HOME/uv` or `$HOME/.cache/uv` on Linux, and `%LOCALAPPDATA%\\uv\\cache` on Windows.",
|
||||
"type": [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue