Skip to content

Commit

Permalink
Only enforce when pre-releases are enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 22, 2024
1 parent 70112df commit 2be8dfe
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 54 deletions.
11 changes: 11 additions & 0 deletions crates/uv-resolver/src/prerelease_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,15 @@ impl PreReleaseStrategy {
),
}
}

/// Returns `true` if a [`PackageName`] is allowed to have pre-release versions.
pub(crate) fn allows(&self, package: &PackageName) -> bool {
match self {
Self::Disallow => false,
Self::Allow => true,
Self::IfNecessary => false,
Self::Explicit(packages) => packages.contains(package),
Self::IfNecessaryOrExplicit(packages) => packages.contains(package),
}
}
}
29 changes: 19 additions & 10 deletions crates/uv-resolver/src/pubgrub/dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use uv_normalize::{ExtraName, PackageName};

use crate::constraints::Constraints;
use crate::overrides::Overrides;
use crate::prerelease_mode::PreReleaseStrategy;
use crate::pubgrub::specifier::PubGrubSpecifier;
use crate::pubgrub::PubGrubPackage;
use crate::resolver::Urls;
Expand All @@ -19,13 +20,15 @@ pub struct PubGrubDependencies(Vec<(PubGrubPackage, Range<Version>)>);

impl PubGrubDependencies {
/// Generate a set of `PubGrub` dependencies from a set of requirements.
#[allow(clippy::too_many_arguments)]
pub(crate) fn from_requirements(
requirements: &[Requirement],
constraints: &Constraints,
overrides: &Overrides,
source_name: Option<&PackageName>,
source_extra: Option<&ExtraName>,
urls: &Urls,
prerelease: &PreReleaseStrategy,
env: &MarkerEnvironment,
) -> Result<Self, ResolveError> {
let mut dependencies = Vec::default();
Expand All @@ -44,12 +47,12 @@ impl PubGrubDependencies {
}

// Add the package, plus any extra variants.
for result in std::iter::once(to_pubgrub(requirement, None, urls)).chain(
for result in std::iter::once(to_pubgrub(requirement, None, urls, prerelease)).chain(
requirement
.extras
.clone()
.into_iter()
.map(|extra| to_pubgrub(requirement, Some(extra), urls)),
.map(|extra| to_pubgrub(requirement, Some(extra), urls, prerelease)),
) {
let (mut package, version) = result?;

Expand Down Expand Up @@ -80,13 +83,15 @@ impl PubGrubDependencies {
}

// Add the package, plus any extra variants.
for result in std::iter::once(to_pubgrub(constraint, None, urls)).chain(
constraint
.extras
.clone()
.into_iter()
.map(|extra| to_pubgrub(constraint, Some(extra), urls)),
) {
for result in std::iter::once(to_pubgrub(constraint, None, urls, prerelease))
.chain(
constraint
.extras
.clone()
.into_iter()
.map(|extra| to_pubgrub(constraint, Some(extra), urls, prerelease)),
)
{
let (mut package, version) = result?;

// Detect self-dependencies.
Expand Down Expand Up @@ -132,6 +137,7 @@ fn to_pubgrub(
requirement: &Requirement,
extra: Option<ExtraName>,
urls: &Urls,
prerelease: &PreReleaseStrategy,
) -> Result<(PubGrubPackage, Range<Version>), ResolveError> {
match requirement.version_or_url.as_ref() {
// The requirement has no specifier (e.g., `flask`).
Expand All @@ -142,9 +148,12 @@ fn to_pubgrub(

// The requirement has a specifier (e.g., `flask>=1.0`).
Some(VersionOrUrl::VersionSpecifier(specifiers)) => {
// If pre-releases are allowed for a given package, markers are interpreted slightly
// differently.
let allow_prerelease = prerelease.allows(&requirement.name);
let version = specifiers
.iter()
.map(PubGrubSpecifier::try_from)
.map(|specifier| PubGrubSpecifier::for_package(specifier, allow_prerelease))
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?;
Expand Down
24 changes: 4 additions & 20 deletions crates/uv-resolver/src/pubgrub/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use rustc_hash::FxHashMap;
use uv_normalize::PackageName;

use crate::candidate_selector::CandidateSelector;
use crate::prerelease_mode::PreReleaseStrategy;
use crate::python_requirement::PythonRequirement;
use crate::resolver::UnavailablePackage;

Expand Down Expand Up @@ -346,25 +345,10 @@ impl PubGrubReportFormatter<'_> {
) -> IndexSet<PubGrubHint> {
/// Returns `true` if pre-releases were allowed for a package.
fn allowed_prerelease(package: &PubGrubPackage, selector: &CandidateSelector) -> bool {
match selector.prerelease_strategy() {
PreReleaseStrategy::Disallow => false,
PreReleaseStrategy::Allow => true,
PreReleaseStrategy::IfNecessary => false,
PreReleaseStrategy::Explicit(packages) => {
if let PubGrubPackage::Package(package, ..) = package {
packages.contains(package)
} else {
false
}
}
PreReleaseStrategy::IfNecessaryOrExplicit(packages) => {
if let PubGrubPackage::Package(package, ..) = package {
packages.contains(package)
} else {
false
}
}
}
let PubGrubPackage::Package(package, ..) = package else {
return false;
};
selector.prerelease_strategy().allows(package)
}

let mut hints = IndexSet::default();
Expand Down
21 changes: 15 additions & 6 deletions crates/uv-resolver/src/pubgrub/specifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ impl From<PubGrubSpecifier> for Range<Version> {
}
}

impl TryFrom<&VersionSpecifier> for PubGrubSpecifier {
type Error = ResolveError;

/// Convert a PEP 508 specifier to a `PubGrub`-compatible version range.
fn try_from(specifier: &VersionSpecifier) -> Result<Self, ResolveError> {
impl PubGrubSpecifier {
pub(crate) fn for_package(
specifier: &VersionSpecifier,
allow_prerelease: bool,
) -> Result<Self, ResolveError> {
let ranges = match specifier.operator() {
Operator::Equal => {
let version = specifier.version().clone();
Expand All @@ -46,7 +46,7 @@ impl TryFrom<&VersionSpecifier> for PubGrubSpecifier {
}
Operator::LessThan => {
let version = specifier.version().clone();
if version.any_prerelease() {
if !allow_prerelease || version.any_prerelease() {
Range::strictly_lower_than(version)
} else {
// Per PEP 440: "The exclusive ordered comparison <V MUST NOT allow a
Expand Down Expand Up @@ -111,3 +111,12 @@ impl TryFrom<&VersionSpecifier> for PubGrubSpecifier {
Ok(Self(ranges))
}
}

impl TryFrom<&VersionSpecifier> for PubGrubSpecifier {
type Error = ResolveError;

/// Convert a PEP 508 specifier to a `PubGrub`-compatible version range.
fn try_from(specifier: &VersionSpecifier) -> Result<Self, ResolveError> {
Self::for_package(specifier, false)
}
}
3 changes: 3 additions & 0 deletions crates/uv-resolver/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
None,
None,
&self.urls,
self.selector.prerelease_strategy(),
self.markers,
);

Expand Down Expand Up @@ -823,6 +824,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
Some(package_name),
extra.as_ref(),
&self.urls,
self.selector.prerelease_strategy(),
self.markers,
)?;

Expand Down Expand Up @@ -879,6 +881,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
Some(package_name),
extra.as_ref(),
&self.urls,
self.selector.prerelease_strategy(),
self.markers,
)?;

Expand Down
25 changes: 7 additions & 18 deletions crates/uv/tests/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2075,17 +2075,12 @@ fn compile_yanked_version_indirect() -> Result<()> {
╰─▶ Because only the following versions of attrs are available:
attrs<=20.3.0
attrs==21.1.0
attrs>=21.2.0a0
attrs>=21.2.0
and attrs==21.1.0 is unusable because it was yanked (reason:
Installable but not importable on Python 3.4), we can conclude that
attrs>20.3.0,<21.2.0a0 cannot be used.
And because you require attrs>20.3.0,<21.2.0a0, we can conclude that the
attrs>20.3.0,<21.2.0 cannot be used.
And because you require attrs>20.3.0,<21.2.0, we can conclude that the
requirements are unsatisfiable.
hint: attrs was requested with a pre-release marker (e.g., any of:
attrs>20.3.0,<21.1.0
attrs>21.1.0,<21.2.0a0
), but pre-releases weren't enabled (try: `--prerelease=allow`)
"###
);

Expand Down Expand Up @@ -3862,11 +3857,8 @@ fn compile_constraints_incompatible_url() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only anyio>=4a0 is available and you require anyio<4a0, we can
╰─▶ Because only anyio>=4 is available and you require anyio<4, we can
conclude that the requirements are unsatisfiable.
hint: anyio was requested with a pre-release marker (e.g., anyio<4a0),
but pre-releases weren't enabled (try: `--prerelease=allow`)
"###
);

Expand All @@ -3889,11 +3881,8 @@ fn index_url_in_requirements() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because anyio<4a0 was not found in the package registry and you require
anyio<4a0, we can conclude that the requirements are unsatisfiable.
hint: anyio was requested with a pre-release marker (e.g., anyio<4a0),
but pre-releases weren't enabled (try: `--prerelease=allow`)
╰─▶ Because anyio<4 was not found in the package registry and you require
anyio<4, we can conclude that the requirements are unsatisfiable.
"###
);

Expand Down Expand Up @@ -4296,7 +4285,7 @@ fn override_with_incompatible_constraint() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because you require anyio>=3.0.0 and you require anyio<3.0.0a0, we can
╰─▶ Because you require anyio>=3.0.0 and you require anyio<3.0.0, we can
conclude that the requirements are unsatisfiable.
"###
);
Expand Down

0 comments on commit 2be8dfe

Please sign in to comment.