Allow incompatible requires-python for source distributions with static metadata (#8768)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86_64 (push) Blocked by required conditions
CI / check system | python3.10 on windows (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions

## Summary

At present, when we have a Python requirement and we see a wheel, we
verify that the Python requirement is compatible with the wheel. For
source distributions, though, we verify that both the Python requirement
_and_ the currently-installed version are compatible, because we assume
that we'll need to build the source distribution in order to get
metadata. However, we can often extract source distribution metadata
_without_ building (e.g., if there's a `pyproject.toml` with no dynamic
keys).

This PR thus modifies the source distribution handling to defer that
incompatibility ("We couldn't get metadata for this project, because it
has no static metadata and requires a higher Python version to run /
build") until we actually try to build the package. As a result, you can
now resolve source distribution-only packages using Python versions
below their `requires-python`, as long as they include static metadata.

Closes https://github.com/astral-sh/uv/issues/8767.
This commit is contained in:
Charlie Marsh 2024-11-03 14:03:55 -05:00 committed by GitHub
parent 647494b998
commit bf79d985ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 279 additions and 128 deletions

View file

@ -132,6 +132,10 @@ impl<'a> BuildDispatch<'a> {
impl<'a> BuildContext for BuildDispatch<'a> {
type SourceDistBuilder = SourceBuild;
fn interpreter(&self) -> &Interpreter {
self.interpreter
}
fn cache(&self) -> &Cache {
self.cache
}

View file

@ -4,7 +4,7 @@ use std::path::Path;
use url::Url;
use uv_distribution_filename::SourceDistExtension;
use uv_git::GitUrl;
use uv_pep440::Version;
use uv_pep440::{Version, VersionSpecifiers};
use uv_pep508::VerbatimUrl;
use uv_normalize::PackageName;
@ -64,6 +64,14 @@ impl BuildableSource<'_> {
Self::Url(url) => matches!(url, SourceUrl::Directory(_)),
}
}
/// Return the Python version specifier required by the source, if available.
pub fn requires_python(&self) -> Option<&VersionSpecifiers> {
let Self::Dist(SourceDist::Registry(dist)) = self else {
return None;
};
dist.file.requires_python.as_ref()
}
}
impl std::fmt::Display for BuildableSource<'_> {

View file

@ -10,7 +10,7 @@ use uv_client::WrappedReqwestError;
use uv_distribution_filename::WheelFilenameError;
use uv_fs::Simplified;
use uv_normalize::PackageName;
use uv_pep440::Version;
use uv_pep440::{Version, VersionSpecifiers};
use uv_pypi_types::{HashDigest, ParsedUrlError};
#[derive(Debug, thiserror::Error)]
@ -96,6 +96,8 @@ pub enum Error {
NotFound(Url),
#[error("Attempted to re-extract the source distribution for `{0}`, but the hashes didn't match. Run `{}` to clear the cache.", "uv cache clean".green())]
CacheHeal(String),
#[error("The source distribution requires Python {0}, but {1} is installed")]
RequiresPython(VersionSpecifiers, Version),
/// A generic request middleware error happened while making a request.
/// Refer to the error message for more details.

View file

@ -1,10 +1,18 @@
//! Fetch and build source distributions from remote sources.
use std::borrow::Cow;
use std::ops::Bound;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use crate::distribution_database::ManagedClient;
use crate::error::Error;
use crate::metadata::{ArchiveMetadata, GitWorkspaceMember, Metadata};
use crate::reporter::Facade;
use crate::source::built_wheel_metadata::BuiltWheelMetadata;
use crate::source::revision::Revision;
use crate::{Reporter, RequiresDist};
use fs_err::tokio as fs;
use futures::{FutureExt, TryStreamExt};
use reqwest::Response;
@ -26,19 +34,12 @@ use uv_distribution_types::{
use uv_extract::hash::Hasher;
use uv_fs::{rename_with_retry, write_atomic, LockedFile};
use uv_metadata::read_archive_metadata;
use uv_pep440::release_specifiers_to_ranges;
use uv_platform_tags::Tags;
use uv_pypi_types::{HashDigest, Metadata12, RequiresTxt, ResolutionMetadata};
use uv_types::{BuildContext, SourceBuildTrait};
use zip::ZipArchive;
use crate::distribution_database::ManagedClient;
use crate::error::Error;
use crate::metadata::{ArchiveMetadata, GitWorkspaceMember, Metadata};
use crate::reporter::Facade;
use crate::source::built_wheel_metadata::BuiltWheelMetadata;
use crate::source::revision::Revision;
use crate::{Reporter, RequiresDist};
mod built_wheel_metadata;
mod revision;
@ -1798,6 +1799,27 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
) -> Result<Option<ResolutionMetadata>, Error> {
debug!("Preparing metadata for: {source}");
// Ensure that the _installed_ Python version is compatible with the `requires-python`
// specifier.
if let Some(requires_python) = source.requires_python() {
let installed = self.build_context.interpreter().python_version();
let target = release_specifiers_to_ranges(requires_python.clone())
.bounding_range()
.map(|bounding_range| bounding_range.0.cloned())
.unwrap_or(Bound::Unbounded);
let is_compatible = match target {
Bound::Included(target) => *installed >= target,
Bound::Excluded(target) => *installed > target,
Bound::Unbounded => true,
};
if !is_compatible {
return Err(Error::RequiresPython(
requires_python.clone(),
installed.clone(),
));
}
}
// Set up the builder.
let mut builder = self
.build_context

View file

@ -11,7 +11,7 @@ use rustc_hash::FxHashMap;
use uv_configuration::IndexStrategy;
use uv_distribution_types::{Index, IndexCapabilities, IndexLocations, IndexUrl};
use uv_normalize::PackageName;
use uv_pep440::Version;
use uv_pep440::{Version, VersionSpecifiers};
use crate::candidate_selector::CandidateSelector;
use crate::error::ErrorTree;
@ -699,6 +699,14 @@ impl PubGrubReportFormatter<'_> {
reason: reason.clone(),
});
}
IncompletePackage::RequiresPython(requires_python, python_version) => {
hints.insert(PubGrubHint::IncompatibleBuildRequirement {
package: package.clone(),
version: version.clone(),
requires_python: requires_python.clone(),
python_version: python_version.clone(),
});
}
}
break;
}
@ -862,6 +870,17 @@ pub(crate) enum PubGrubHint {
// excluded from `PartialEq` and `Hash`
reason: String,
},
/// The source distribution has a `requires-python` requirement that is not met by the installed
/// Python version (and static metadata is not available).
IncompatibleBuildRequirement {
package: PubGrubPackage,
// excluded from `PartialEq` and `Hash`
version: Version,
// excluded from `PartialEq` and `Hash`
requires_python: VersionSpecifiers,
// excluded from `PartialEq` and `Hash`
python_version: Version,
},
/// The `Requires-Python` requirement was not satisfied.
RequiresPython {
source: PythonRequirementSource,
@ -932,6 +951,9 @@ enum PubGrubHintCore {
InvalidVersionStructure {
package: PubGrubPackage,
},
IncompatibleBuildRequirement {
package: PubGrubPackage,
},
RequiresPython {
source: PythonRequirementSource,
requires_python: RequiresPython,
@ -985,6 +1007,9 @@ impl From<PubGrubHint> for PubGrubHintCore {
PubGrubHint::InvalidVersionStructure { package, .. } => {
Self::InvalidVersionStructure { package }
}
PubGrubHint::IncompatibleBuildRequirement { package, .. } => {
Self::IncompatibleBuildRequirement { package }
}
PubGrubHint::RequiresPython {
source,
requires_python,
@ -1187,6 +1212,23 @@ impl std::fmt::Display for PubGrubHint {
package_requires_python.bold(),
)
}
Self::IncompatibleBuildRequirement {
package,
version,
requires_python,
python_version,
} => {
write!(
f,
"{}{} The source distribution for {}=={} does not include static metadata. Generating metadata for this package requires Python {}, but Python {} is installed.",
"hint".bold().cyan(),
":".bold(),
package.bold(),
version.bold(),
requires_python.bold(),
python_version.bold(),
)
}
Self::RequiresPython {
source: PythonRequirementSource::Interpreter,
requires_python: _,

View file

@ -1,7 +1,7 @@
use std::fmt::{Display, Formatter};
use uv_distribution_types::IncompatibleDist;
use uv_pep440::Version;
use uv_pep440::{Version, VersionSpecifiers};
/// The reason why a package or a version cannot be used.
#[derive(Debug, Clone, Eq, PartialEq)]
@ -40,6 +40,9 @@ pub(crate) enum UnavailableVersion {
InvalidStructure,
/// The wheel metadata was not found in the cache and the network is not available.
Offline,
/// The source distribution has a `requires-python` requirement that is not met by the installed
/// Python version (and static metadata is not available).
RequiresPython(VersionSpecifiers),
}
impl UnavailableVersion {
@ -51,6 +54,9 @@ impl UnavailableVersion {
UnavailableVersion::InconsistentMetadata => "inconsistent metadata".into(),
UnavailableVersion::InvalidStructure => "an invalid package format".into(),
UnavailableVersion::Offline => "to be downloaded from a registry".into(),
UnavailableVersion::RequiresPython(requires_python) => {
format!("Python {requires_python}")
}
}
}
@ -62,6 +68,7 @@ impl UnavailableVersion {
UnavailableVersion::InconsistentMetadata => format!("has {self}"),
UnavailableVersion::InvalidStructure => format!("has {self}"),
UnavailableVersion::Offline => format!("needs {self}"),
UnavailableVersion::RequiresPython(..) => format!("requires {self}"),
}
}
@ -73,6 +80,7 @@ impl UnavailableVersion {
UnavailableVersion::InconsistentMetadata => format!("have {self}"),
UnavailableVersion::InvalidStructure => format!("have {self}"),
UnavailableVersion::Offline => format!("need {self}"),
UnavailableVersion::RequiresPython(..) => format!("require {self}"),
}
}
}
@ -143,6 +151,9 @@ pub(crate) enum IncompletePackage {
InconsistentMetadata(String),
/// The wheel has an invalid structure.
InvalidStructure(String),
/// The source distribution has a `requires-python` requirement that is not met by the installed
/// Python version (and static metadata is not available).
RequiresPython(VersionSpecifiers, Version),
}
#[derive(Debug, Clone)]

View file

@ -958,6 +958,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
);
return Ok(None);
}
MetadataResponse::RequiresPython(..) => {
unreachable!("`requires-python` is only known upfront for registry distributions")
}
};
let version = &metadata.version;
@ -1074,72 +1077,54 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
}
};
let incompatibility = match dist {
// Validate the Python requirement.
let requires_python = match dist {
CompatibleDist::InstalledDist(_) => None,
CompatibleDist::SourceDist { sdist, .. }
| CompatibleDist::IncompatibleWheel { sdist, .. } => {
// Source distributions must meet both the _target_ Python version and the
// _installed_ Python version (to build successfully).
sdist
.file
.requires_python
.as_ref()
.and_then(|requires_python| {
if !python_requirement
.installed()
.is_contained_by(requires_python)
{
return Some(IncompatibleDist::Source(
IncompatibleSource::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Installed,
),
));
}
if !python_requirement.target().is_contained_by(requires_python) {
return Some(IncompatibleDist::Source(
IncompatibleSource::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Target,
),
));
}
None
})
}
CompatibleDist::CompatibleWheel { wheel, .. } => {
// Wheels must meet the _target_ Python version.
wheel
.file
.requires_python
.as_ref()
.and_then(|requires_python| {
if python_requirement.installed() == python_requirement.target() {
if !python_requirement
.installed()
.is_contained_by(requires_python)
{
return Some(IncompatibleDist::Wheel(
IncompatibleWheel::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Installed,
),
));
}
} else {
if !python_requirement.target().is_contained_by(requires_python) {
return Some(IncompatibleDist::Wheel(
IncompatibleWheel::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Target,
),
));
}
}
None
})
sdist.file.requires_python.as_ref()
}
CompatibleDist::CompatibleWheel { wheel, .. } => wheel.file.requires_python.as_ref(),
};
let incompatibility = requires_python.and_then(|requires_python| {
if python_requirement.installed() == python_requirement.target() {
if !python_requirement
.installed()
.is_contained_by(requires_python)
{
return if matches!(dist, CompatibleDist::CompatibleWheel { .. }) {
Some(IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Installed,
)))
} else {
Some(IncompatibleDist::Source(
IncompatibleSource::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Installed,
),
))
};
}
} else {
if !python_requirement.target().is_contained_by(requires_python) {
return if matches!(dist, CompatibleDist::CompatibleWheel { .. }) {
Some(IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Target,
)))
} else {
Some(IncompatibleDist::Source(
IncompatibleSource::RequiresPython(
requires_python.clone(),
PythonRequirementKind::Target,
),
))
};
}
}
None
});
// The version is incompatible due to its Python requirement.
if let Some(incompatibility) = incompatibility {
@ -1342,6 +1327,28 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
UnavailableVersion::InvalidStructure,
));
}
MetadataResponse::RequiresPython(requires_python, python_version) => {
warn!(
"Unable to extract metadata for {name}: {}",
uv_distribution::Error::RequiresPython(
requires_python.clone(),
python_version.clone()
)
);
self.incomplete_packages
.entry(name.clone())
.or_default()
.insert(
version.clone(),
IncompletePackage::RequiresPython(
requires_python.clone(),
python_version.clone(),
),
);
return Ok(Dependencies::Unavailable(
UnavailableVersion::RequiresPython(requires_python.clone()),
));
}
};
let requirements = self.flatten_requirements(
@ -1888,33 +1895,22 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
}
}
match dist {
CompatibleDist::InstalledDist(_) => {}
// Validate the Python requirement.
let requires_python = match dist {
CompatibleDist::InstalledDist(_) => None,
CompatibleDist::SourceDist { sdist, .. }
| CompatibleDist::IncompatibleWheel { sdist, .. } => {
// Source distributions must meet both the _target_ Python version and the
// _installed_ Python version (to build successfully).
if let Some(requires_python) = sdist.file.requires_python.as_ref() {
if !python_requirement
.installed()
.is_contained_by(requires_python)
{
return Ok(None);
}
if !python_requirement.target().is_contained_by(requires_python) {
return Ok(None);
}
}
sdist.file.requires_python.as_ref()
}
CompatibleDist::CompatibleWheel { wheel, .. } => {
// Wheels must meet the _target_ Python version.
if let Some(requires_python) = wheel.file.requires_python.as_ref() {
if !python_requirement.target().is_contained_by(requires_python) {
return Ok(None);
}
}
wheel.file.requires_python.as_ref()
}
};
if let Some(requires_python) = requires_python.as_ref() {
if !python_requirement.target().is_contained_by(requires_python) {
return Ok(None);
}
}
// Emit a request to fetch the metadata for this version.
if self.index.distributions().register(candidate.version_id()) {

View file

@ -4,6 +4,7 @@ use uv_configuration::BuildOptions;
use uv_distribution::{ArchiveMetadata, DistributionDatabase};
use uv_distribution_types::{Dist, IndexCapabilities, IndexUrl};
use uv_normalize::PackageName;
use uv_pep440::{Version, VersionSpecifiers};
use uv_platform_tags::Tags;
use uv_types::{BuildContext, HashStrategy};
@ -42,6 +43,9 @@ pub enum MetadataResponse {
InvalidStructure(Box<uv_metadata::Error>),
/// The wheel metadata was not found in the cache and the network is not available.
Offline,
/// The source distribution has a `requires-python` requirement that is not met by the installed
/// Python version (and static metadata is not available).
RequiresPython(VersionSpecifiers, Version),
}
pub trait ResolverProvider {
@ -203,6 +207,9 @@ impl<'a, Context: BuildContext> ResolverProvider for DefaultResolverProvider<'a,
uv_distribution::Error::WheelMetadata(_, err) => {
Ok(MetadataResponse::InvalidStructure(err))
}
uv_distribution::Error::RequiresPython(requires_python, version) => {
Ok(MetadataResponse::RequiresPython(requires_python, version))
}
err => Err(err),
},
}

View file

@ -14,7 +14,7 @@ use uv_distribution_types::{
use uv_git::GitResolver;
use uv_pep508::PackageName;
use uv_pypi_types::Requirement;
use uv_python::PythonEnvironment;
use uv_python::{Interpreter, PythonEnvironment};
/// Avoids cyclic crate dependencies between resolver, installer and builder.
///
@ -56,6 +56,9 @@ use uv_python::PythonEnvironment;
pub trait BuildContext {
type SourceDistBuilder: SourceBuildTrait;
/// Return a reference to the interpreter.
fn interpreter(&self) -> &Interpreter;
/// Return a reference to the cache.
fn cache(&self) -> &Cache;

View file

@ -12661,3 +12661,62 @@ fn prune_unreachable() -> Result<()> {
Ok(())
}
/// Allow resolving a package that requires a Python version that is not available, as long as it
/// includes static metadata.
///
/// See: <https://github.com/astral-sh/uv/issues/8767>
#[test]
fn unsupported_requires_python_static_metadata() -> Result<()> {
let context = TestContext::new("3.11");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("interpreters-pep-734 <= 0.4.1 ; python_version >= '3.13'")?;
uv_snapshot!(context.filters(), context
.pip_compile()
.arg("--universal")
.arg("requirements.in")
.env(EnvVars::UV_EXCLUDE_NEWER, "2024-11-04T00:00:00Z"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --universal requirements.in
interpreters-pep-734==0.4.1 ; python_full_version >= '3.13'
# via -r requirements.in
----- stderr -----
Resolved 1 package in [TIME]
"###);
Ok(())
}
/// Disallow resolving a package that requires a Python version that is not available, if it uses
/// dynamic metadata.
///
/// See: <https://github.com/astral-sh/uv/issues/8767>
#[test]
fn unsupported_requires_python_dynamic_metadata() -> Result<()> {
let context = TestContext::new("3.8");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("source-distribution==0.0.3 ; python_version >= '3.10'")?;
uv_snapshot!(context.filters(), context
.pip_compile()
.arg("--universal")
.arg("requirements.in")
.env(EnvVars::UV_EXCLUDE_NEWER, "2024-11-04T00:00:00Z"), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No solution found when resolving dependencies for split (python_full_version >= '3.10'):
Because source-distribution{python_full_version >= '3.10'}==0.0.3 requires Python >=3.10 and you require source-distribution{python_full_version >= '3.10'}==0.0.3, we can conclude that your requirements are unsatisfiable.
hint: The source distribution for source-distribution{python_full_version >= '3.10'}==0.0.3 does not include static metadata. Generating metadata for this package requires Python >=3.10, but Python 3.8.[X] is installed.
"###);
Ok(())
}

View file

@ -175,20 +175,22 @@ fn incompatible_python_compatible_override_unavailable_no_wheels() -> Result<()>
// dependencies.
let output = uv_snapshot!(filters, command(&context, python_versions)
.arg("--python-version=3.11")
, @r#"
success: false
exit_code: 1
, @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11
package-a==1.0.0
# via -r requirements.in
----- stderr -----
warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead.
× No solution found when resolving dependencies:
Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used.
And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable.
"#
Resolved 1 package in [TIME]
"###
);
output.assert().failure();
output.assert().success();
Ok(())
}
@ -287,20 +289,22 @@ fn incompatible_python_compatible_override_no_compatible_wheels() -> Result<()>
// determine its dependencies.
let output = uv_snapshot!(filters, command(&context, python_versions)
.arg("--python-version=3.11")
, @r#"
success: false
exit_code: 1
, @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11
package-a==1.0.0
# via -r requirements.in
----- stderr -----
warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead.
× No solution found when resolving dependencies:
Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used.
And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable.
"#
Resolved 1 package in [TIME]
"###
);
output.assert().failure();
output.assert().success();
Ok(())
}
@ -345,29 +349,22 @@ fn incompatible_python_compatible_override_other_wheel() -> Result<()> {
// available, but is not compatible with the target version and cannot be used.
let output = uv_snapshot!(filters, command(&context, python_versions)
.arg("--python-version=3.11")
, @r#"
success: false
exit_code: 1
, @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11
package-a==1.0.0
# via -r requirements.in
----- stderr -----
warning: The requested Python version 3.11 is not available; 3.9.[X] will be used to build dependencies instead.
× No solution found when resolving dependencies:
Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used.
And because only the following versions of package-a are available:
package-a==1.0.0
package-a==2.0.0
we can conclude that package-a<2.0.0 cannot be used. (1)
Because the requested Python version (>=3.11.0) does not satisfy Python>=3.12 and package-a==2.0.0 depends on Python>=3.12, we can conclude that package-a==2.0.0 cannot be used.
And because we know from (1) that package-a<2.0.0 cannot be used, we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that your requirements are unsatisfiable.
hint: The `--python-version` value (>=3.11.0) includes Python versions that are not supported by your dependencies (e.g., package-a==2.0.0 only supports >=3.12). Consider using a higher `--python-version` value.
"#
Resolved 1 package in [TIME]
"###
);
output.assert().failure();
output.assert().success();
Ok(())
}