mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Warn when missing minimal bounds when using tool.uv.sources (#3452)
When using `tool.uv.sources`, we warn that requirements have a bound, i.e. at least a lower version constraint. When using a library, the symbols you import were introduced in different versions, creating an implicit lower bound. This warning makes this explicit. This is crucial to prevent backtracking resolvers from selecting an ancient versions that is not compatible (or worse, doesn't build), and a performance optimization on top. This feature is gated to `tool.uv.sources` (as it should have been to begin with for #3263/#3443) to not unnecessarily break legacy workflows. It is also helpful specifically when using a `tool.uv.sources` section that contains constraints that are not published to pypi, e.g. for workspace dependencies. We can adjust those later to e.g. not constrain workspace dependencies with `publish = false`, but i think it's the right setting to start with.
This commit is contained in:
parent
7c6632114b
commit
7c7c9e2189
2 changed files with 118 additions and 5 deletions
|
|
@ -27,6 +27,7 @@ use uv_configuration::PreviewMode;
|
|||
use uv_fs::Simplified;
|
||||
use uv_git::GitReference;
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::ExtrasSpecification;
|
||||
|
||||
|
|
@ -280,6 +281,7 @@ impl Pep621Metadata {
|
|||
let requirements = lower_requirements(
|
||||
&project.dependencies.unwrap_or_default(),
|
||||
&project.optional_dependencies.unwrap_or_default(),
|
||||
&project.name,
|
||||
project_dir,
|
||||
&project_sources.unwrap_or_default(),
|
||||
workspace_sources,
|
||||
|
|
@ -318,6 +320,7 @@ impl Pep621Metadata {
|
|||
pub(crate) fn lower_requirements(
|
||||
dependencies: &[String],
|
||||
optional_dependencies: &IndexMap<ExtraName, Vec<String>>,
|
||||
project_name: &PackageName,
|
||||
project_dir: &Path,
|
||||
project_sources: &HashMap<PackageName, Source>,
|
||||
workspace_sources: &HashMap<PackageName, Source>,
|
||||
|
|
@ -331,6 +334,7 @@ pub(crate) fn lower_requirements(
|
|||
let name = requirement.name.clone();
|
||||
lower_requirement(
|
||||
requirement,
|
||||
project_name,
|
||||
project_dir,
|
||||
project_sources,
|
||||
workspace_sources,
|
||||
|
|
@ -350,6 +354,7 @@ pub(crate) fn lower_requirements(
|
|||
let name = requirement.name.clone();
|
||||
lower_requirement(
|
||||
requirement,
|
||||
project_name,
|
||||
project_dir,
|
||||
project_sources,
|
||||
workspace_sources,
|
||||
|
|
@ -371,6 +376,7 @@ pub(crate) fn lower_requirements(
|
|||
/// Combine `project.dependencies` or `project.optional-dependencies` with `tool.uv.sources`.
|
||||
pub(crate) fn lower_requirement(
|
||||
requirement: pep508_rs::Requirement,
|
||||
project_name: &PackageName,
|
||||
project_dir: &Path,
|
||||
project_sources: &HashMap<PackageName, Source>,
|
||||
workspace_sources: &HashMap<PackageName, Source>,
|
||||
|
|
@ -395,7 +401,15 @@ pub(crate) fn lower_requirement(
|
|||
}
|
||||
|
||||
let Some(source) = source else {
|
||||
let has_sources = !project_sources.is_empty() || !workspace_sources.is_empty();
|
||||
// Support recursive editable inclusions.
|
||||
if has_sources && requirement.version_or_url.is_none() && &requirement.name != project_name
|
||||
{
|
||||
warn_user_once!(
|
||||
"Missing version constraint (e.g., a lower bound) for `{}`",
|
||||
requirement.name
|
||||
);
|
||||
}
|
||||
return Ok(Requirement::from_pep508(requirement)?);
|
||||
};
|
||||
|
||||
|
|
@ -476,10 +490,16 @@ pub(crate) fn lower_requirement(
|
|||
path_source(path, project_dir, editable)?
|
||||
}
|
||||
Source::Registry { index } => match requirement.version_or_url {
|
||||
None => RequirementSource::Registry {
|
||||
specifier: VersionSpecifiers::empty(),
|
||||
index: Some(index),
|
||||
},
|
||||
None => {
|
||||
warn_user_once!(
|
||||
"Missing version constraint (e.g., a lower bound) for `{}`",
|
||||
requirement.name
|
||||
);
|
||||
RequirementSource::Registry {
|
||||
specifier: VersionSpecifiers::empty(),
|
||||
index: Some(index),
|
||||
}
|
||||
}
|
||||
Some(VersionOrUrl::VersionSpecifier(version)) => RequirementSource::Registry {
|
||||
specifier: version,
|
||||
index: Some(index),
|
||||
|
|
@ -630,8 +650,8 @@ mod test {
|
|||
use anyhow::Context;
|
||||
use indoc::indoc;
|
||||
use insta::assert_snapshot;
|
||||
use uv_configuration::PreviewMode;
|
||||
|
||||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::Simplified;
|
||||
|
||||
use crate::{ExtrasSpecification, RequirementsSpecification};
|
||||
|
|
@ -756,6 +776,20 @@ mod test {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_constraint() {
|
||||
let input = indoc! {r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"tqdm",
|
||||
]
|
||||
"#};
|
||||
|
||||
assert!(from_source(input, "pyproject.toml", &ExtrasSpecification::None).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_syntax() {
|
||||
let input = indoc! {r#"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue