mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-12 08:51:16 +00:00
Warn about unconstrained direct deps in lowest resolution (#5142)
Warn when there is a direct dependency without a lower bound and `--resolution lowest` is set. --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
parent
4f73004f95
commit
fbb00f701c
2 changed files with 25 additions and 1 deletions
|
|
@ -38,6 +38,7 @@ use uv_distribution::{ArchiveMetadata, DistributionDatabase};
|
||||||
use uv_git::GitResolver;
|
use uv_git::GitResolver;
|
||||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||||
use uv_types::{BuildContext, HashStrategy, InstalledPackagesProvider};
|
use uv_types::{BuildContext, HashStrategy, InstalledPackagesProvider};
|
||||||
|
use uv_warnings::warn_user_once;
|
||||||
|
|
||||||
use crate::candidate_selector::{CandidateDist, CandidateSelector};
|
use crate::candidate_selector::{CandidateDist, CandidateSelector};
|
||||||
use crate::dependency_provider::UvDependencyProvider;
|
use crate::dependency_provider::UvDependencyProvider;
|
||||||
|
|
@ -53,6 +54,7 @@ use crate::pubgrub::{
|
||||||
};
|
};
|
||||||
use crate::python_requirement::PythonRequirement;
|
use crate::python_requirement::PythonRequirement;
|
||||||
use crate::resolution::ResolutionGraph;
|
use crate::resolution::ResolutionGraph;
|
||||||
|
use crate::resolution_mode::ResolutionStrategy;
|
||||||
pub(crate) use crate::resolver::availability::{
|
pub(crate) use crate::resolver::availability::{
|
||||||
IncompletePackage, ResolverVersion, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
IncompletePackage, ResolverVersion, UnavailablePackage, UnavailableReason, UnavailableVersion,
|
||||||
};
|
};
|
||||||
|
|
@ -512,6 +514,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
&self.urls,
|
&self.urls,
|
||||||
dependencies.clone(),
|
dependencies.clone(),
|
||||||
&self.git,
|
&self.git,
|
||||||
|
self.selector.resolution_strategy(),
|
||||||
)?;
|
)?;
|
||||||
// Emit a request to fetch the metadata for each registry package.
|
// Emit a request to fetch the metadata for each registry package.
|
||||||
for dependency in &dependencies {
|
for dependency in &dependencies {
|
||||||
|
|
@ -585,6 +588,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
&self.urls,
|
&self.urls,
|
||||||
fork.dependencies.clone(),
|
fork.dependencies.clone(),
|
||||||
&self.git,
|
&self.git,
|
||||||
|
self.selector.resolution_strategy(),
|
||||||
)?;
|
)?;
|
||||||
// Emit a request to fetch the metadata for each registry package.
|
// Emit a request to fetch the metadata for each registry package.
|
||||||
for dependency in &fork.dependencies {
|
for dependency in &fork.dependencies {
|
||||||
|
|
@ -2031,6 +2035,7 @@ impl ForkState {
|
||||||
urls: &Urls,
|
urls: &Urls,
|
||||||
dependencies: Vec<PubGrubDependency>,
|
dependencies: Vec<PubGrubDependency>,
|
||||||
git: &GitResolver,
|
git: &GitResolver,
|
||||||
|
resolution_strategy: &ResolutionStrategy,
|
||||||
) -> Result<(), ResolveError> {
|
) -> Result<(), ResolveError> {
|
||||||
for dependency in &dependencies {
|
for dependency in &dependencies {
|
||||||
let PubGrubDependency {
|
let PubGrubDependency {
|
||||||
|
|
@ -2040,6 +2045,7 @@ impl ForkState {
|
||||||
local,
|
local,
|
||||||
} = dependency;
|
} = dependency;
|
||||||
|
|
||||||
|
let mut has_url = false;
|
||||||
if let Some(name) = package.name() {
|
if let Some(name) = package.name() {
|
||||||
// From the [`Requirement`] to [`PubGrubDependency`] conversion, we get a URL if the
|
// From the [`Requirement`] to [`PubGrubDependency`] conversion, we get a URL if the
|
||||||
// requirement was a URL requirement. `Urls` applies canonicalization to this and
|
// requirement was a URL requirement. `Urls` applies canonicalization to this and
|
||||||
|
|
@ -2047,6 +2053,7 @@ impl ForkState {
|
||||||
// conflicts using [`ForkUrl`].
|
// conflicts using [`ForkUrl`].
|
||||||
if let Some(url) = urls.get_url(name, url.as_ref(), git)? {
|
if let Some(url) = urls.get_url(name, url.as_ref(), git)? {
|
||||||
self.fork_urls.insert(name, url, &self.markers)?;
|
self.fork_urls.insert(name, url, &self.markers)?;
|
||||||
|
has_url = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// `PubGrubDependency` also gives us a local version if specified by the user.
|
// `PubGrubDependency` also gives us a local version if specified by the user.
|
||||||
|
|
@ -2062,6 +2069,19 @@ impl ForkState {
|
||||||
} else {
|
} else {
|
||||||
// A dependency from the root package or requirements.txt.
|
// A dependency from the root package or requirements.txt.
|
||||||
debug!("Adding direct dependency: {package}{version}");
|
debug!("Adding direct dependency: {package}{version}");
|
||||||
|
|
||||||
|
// Warn the user if the direct dependency lacks a lower bound in lowest resolution.
|
||||||
|
let missing_lower_bound = version
|
||||||
|
.bounding_range()
|
||||||
|
.map(|(lowest, _highest)| lowest == Bound::Unbounded)
|
||||||
|
.unwrap_or(true);
|
||||||
|
let strategy_lowest = matches!(
|
||||||
|
resolution_strategy,
|
||||||
|
ResolutionStrategy::Lowest | ResolutionStrategy::LowestDirect(..)
|
||||||
|
);
|
||||||
|
if !has_url && missing_lower_bound && strategy_lowest {
|
||||||
|
warn_user_once!("The direct dependency `{package}` is unpinned. Consider setting a lower bound when using `--resolution-strategy lowest` to avoid using outdated versions.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the package priorities.
|
// Update the package priorities.
|
||||||
|
|
|
||||||
|
|
@ -9520,12 +9520,15 @@ fn compile_index_url_unsafe_highest() -> Result<()> {
|
||||||
/// In this case, anyio 3.5.0 is hosted on the "extra" index, but older versions are available on
|
/// In this case, anyio 3.5.0 is hosted on the "extra" index, but older versions are available on
|
||||||
/// the "primary" index. We should prefer the older version from the "primary" index, despite the
|
/// the "primary" index. We should prefer the older version from the "primary" index, despite the
|
||||||
/// "extra" index being the preferred index.
|
/// "extra" index being the preferred index.
|
||||||
|
///
|
||||||
|
/// We also test here that a warning is raised for missing lower bounds on direct dependencies with
|
||||||
|
/// `--resolution lowest`.
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_index_url_unsafe_lowest() -> Result<()> {
|
fn compile_index_url_unsafe_lowest() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
let requirements_in = context.temp_dir.child("requirements.in");
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
requirements_in.write_str("anyio")?;
|
requirements_in.write_str("anyio<100")?;
|
||||||
|
|
||||||
uv_snapshot!(context.pip_compile()
|
uv_snapshot!(context.pip_compile()
|
||||||
.arg("--resolution")
|
.arg("--resolution")
|
||||||
|
|
@ -9547,6 +9550,7 @@ fn compile_index_url_unsafe_lowest() -> Result<()> {
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
|
warning: The direct dependency `anyio` is unpinned. Consider setting a lower bound when using `--resolution-strategy lowest` to avoid using outdated versions.
|
||||||
Resolved 1 package in [TIME]
|
Resolved 1 package in [TIME]
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue