From 57ea55d2185213236e68fef82135f1cfc1686043 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 4 Jun 2024 16:52:15 -0400 Subject: [PATCH] Add a hint for `Requires-Python` (#4021) ## Summary As requested in the originating PR. --- crates/uv-resolver/src/pubgrub/report.rs | 57 +++++++++++++++++++++--- crates/uv/tests/lock.rs | 2 + 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/crates/uv-resolver/src/pubgrub/report.rs b/crates/uv-resolver/src/pubgrub/report.rs index 67e1815f9..2f37f9bc0 100644 --- a/crates/uv-resolver/src/pubgrub/report.rs +++ b/crates/uv-resolver/src/pubgrub/report.rs @@ -4,19 +4,20 @@ use std::collections::{BTreeMap, BTreeSet}; use std::ops::Bound; use derivative::Derivative; -use distribution_types::IndexLocations; use indexmap::{IndexMap, IndexSet}; use owo_colors::OwoColorize; -use pep440_rs::Version; use pubgrub::range::Range; use pubgrub::report::{DerivationTree, Derived, External, ReportFormatter}; use pubgrub::term::Term; use pubgrub::type_aliases::Map; use rustc_hash::FxHashMap; + +use distribution_types::IndexLocations; +use pep440_rs::{Version, VersionSpecifiers}; use uv_normalize::PackageName; use crate::candidate_selector::CandidateSelector; -use crate::python_requirement::PythonRequirement; +use crate::python_requirement::{PythonRequirement, RequiresPython}; use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason}; use super::{PubGrubPackage, PubGrubPackageInner, PubGrubPython}; @@ -534,8 +535,27 @@ impl PubGrubReportFormatter<'_> { } } } + External::FromDependencyOf(package, package_set, dependency, dependency_set) => { + // Check for no versions due to `Requires-Python`. + if matches!( + &**dependency, + PubGrubPackageInner::Python(PubGrubPython::Target) + ) { + if let Some(python) = self.python_requirement { + if let Some(RequiresPython::Specifiers(specifiers)) = python.target() { + hints.insert(PubGrubHint::RequiresPython { + requires_python: specifiers.clone(), + package: package.clone(), + package_set: self + .simplify_set(package_set, package) + .into_owned(), + package_requires_python: dependency_set.clone(), + }); + } + } + } + } External::NotRoot(..) => {} - External::FromDependencyOf(..) => {} }, DerivationTree::Derived(derived) => { hints.extend(self.hints( @@ -618,6 +638,16 @@ pub(crate) enum PubGrubHint { #[derivative(PartialEq = "ignore", Hash = "ignore")] reason: String, }, + /// The `Requires-Python` requirement was not satisfied. + RequiresPython { + requires_python: VersionSpecifiers, + #[derivative(PartialEq = "ignore", Hash = "ignore")] + package: PubGrubPackage, + #[derivative(PartialEq = "ignore", Hash = "ignore")] + package_set: Range, + #[derivative(PartialEq = "ignore", Hash = "ignore")] + package_requires_python: Range, + }, } impl std::fmt::Display for PubGrubHint { @@ -709,7 +739,7 @@ impl std::fmt::Display for PubGrubHint { textwrap::indent(reason, " ") ) } - PubGrubHint::InconsistentVersionMetadata { + Self::InconsistentVersionMetadata { package, version, reason, @@ -724,6 +754,23 @@ impl std::fmt::Display for PubGrubHint { textwrap::indent(reason, " ") ) } + Self::RequiresPython { + requires_python, + package, + package_set, + package_requires_python, + } => { + write!( + f, + "{}{} The `Requires-Python` requirement ({}) defined in your `pyproject.toml` includes Python versions that are not supported by your dependencies (e.g., {} only supports {}). Consider using a more restrictive `Requires-Python` requirement (like {}).", + "hint".bold().cyan(), + ":".bold(), + requires_python.bold(), + PackageRange::compatibility(package, package_set).bold(), + package_requires_python.bold(), + package_requires_python.bold(), + ) + } } } } diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index a49cbdce2..795670a57 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -1004,6 +1004,8 @@ fn lock_requires_python() -> Result<()> { cannot be used, we can conclude that pygls>=1.1.0 cannot be used. And because project==0.1.0 depends on pygls>=1.1.0, we can conclude that project==0.1.0 cannot be used. And because only project==0.1.0 is available and project depends on project, we can conclude that the requirements are unsatisfiable. + + hint: The `Requires-Python` requirement (>=3.7) defined in your `pyproject.toml` includes Python versions that are not supported by your dependencies (e.g., pygls>=1.1.0,<=1.2.1 only supports >=3.7.9, <4). Consider using a more restrictive `Requires-Python` requirement (like >=3.7.9, <4). "###); // Require >=3.7, and allow locking to a version of `pygls` that is compatible.