mirror of https://github.com/astral-sh/uv
Allow overrides in all satisfies checks (#11995)
## Summary This PR adds support for `SitePackages::satisfies` with unnamed overrides and requirements. The main challenge here was cases like: you have a `requirements.in` with `git+https://github.com/pallets/flask` in it, and an `overrides.txt` with `flask==2.0.0` in it. You _need_ to include `flask==2.0.0`, but you can't know that without resolving the unnamed URL requirement (since overrides only take effect when the package is included, like constraints). We now make the assumption that any unnamed overrides _are_ relevant, for the purpose of the satisfies check. This is conservative, but this whole check is an optimization anyway.
This commit is contained in:
parent
b955211698
commit
40dce4e009
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::iter::Flatten;
|
use std::iter::Flatten;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
@ -14,6 +15,7 @@ use uv_distribution_types::{
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_pep440::{Version, VersionSpecifiers};
|
use uv_pep440::{Version, VersionSpecifiers};
|
||||||
|
use uv_pep508::VersionOrUrl;
|
||||||
use uv_pypi_types::{Requirement, ResolverMarkerEnvironment, VerbatimParsedUrl};
|
use uv_pypi_types::{Requirement, ResolverMarkerEnvironment, VerbatimParsedUrl};
|
||||||
use uv_python::{Interpreter, PythonEnvironment};
|
use uv_python::{Interpreter, PythonEnvironment};
|
||||||
use uv_types::InstalledPackagesProvider;
|
use uv_types::InstalledPackagesProvider;
|
||||||
|
|
@ -281,179 +283,164 @@ impl SitePackages {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if the installed packages satisfy the given requirements.
|
/// Returns if the installed packages satisfy the given requirements.
|
||||||
pub fn satisfies(
|
pub fn satisfies_spec(
|
||||||
&self,
|
&self,
|
||||||
requirements: &[UnresolvedRequirementSpecification],
|
requirements: &[UnresolvedRequirementSpecification],
|
||||||
constraints: &[NameRequirementSpecification],
|
constraints: &[NameRequirementSpecification],
|
||||||
|
overrides: &[UnresolvedRequirementSpecification],
|
||||||
markers: &ResolverMarkerEnvironment,
|
markers: &ResolverMarkerEnvironment,
|
||||||
) -> Result<SatisfiesResult> {
|
) -> Result<SatisfiesResult> {
|
||||||
// Collect the constraints, filtering them by their marker environment.
|
// First, map all unnamed requirements to named requirements.
|
||||||
let constraints: FxHashMap<&PackageName, Vec<&Requirement>> = constraints
|
let requirements = {
|
||||||
.iter()
|
let mut named = Vec::with_capacity(requirements.len());
|
||||||
.filter(|constraint| constraint.requirement.evaluate_markers(Some(markers), &[]))
|
for requirement in requirements {
|
||||||
.fold(FxHashMap::default(), |mut constraints, constraint| {
|
match &requirement.requirement {
|
||||||
|
UnresolvedRequirement::Named(requirement) => {
|
||||||
|
named.push(Cow::Borrowed(requirement));
|
||||||
|
}
|
||||||
|
UnresolvedRequirement::Unnamed(requirement) => {
|
||||||
|
match self.get_urls(requirement.url.verbatim.raw()).as_slice() {
|
||||||
|
[] => {
|
||||||
|
return Ok(SatisfiesResult::Unsatisfied(
|
||||||
|
requirement.url.verbatim.raw().to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
[distribution] => {
|
||||||
|
let requirement = uv_pep508::Requirement {
|
||||||
|
name: distribution.name().clone(),
|
||||||
|
version_or_url: Some(VersionOrUrl::Url(
|
||||||
|
requirement.url.clone(),
|
||||||
|
)),
|
||||||
|
marker: requirement.marker,
|
||||||
|
extras: requirement.extras.clone(),
|
||||||
|
origin: requirement.origin.clone(),
|
||||||
|
};
|
||||||
|
named.push(Cow::Owned(Requirement::from(requirement)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok(SatisfiesResult::Unsatisfied(
|
||||||
|
requirement.url.verbatim.raw().to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
named
|
||||||
|
};
|
||||||
|
|
||||||
|
// Second, map all overrides to named requirements. We assume that all overrides are
|
||||||
|
// relevant.
|
||||||
|
let overrides = {
|
||||||
|
let mut named = Vec::with_capacity(overrides.len());
|
||||||
|
for requirement in overrides {
|
||||||
|
match &requirement.requirement {
|
||||||
|
UnresolvedRequirement::Named(requirement) => {
|
||||||
|
named.push(Cow::Borrowed(requirement));
|
||||||
|
}
|
||||||
|
UnresolvedRequirement::Unnamed(requirement) => {
|
||||||
|
match self.get_urls(requirement.url.verbatim.raw()).as_slice() {
|
||||||
|
[] => {
|
||||||
|
return Ok(SatisfiesResult::Unsatisfied(
|
||||||
|
requirement.url.verbatim.raw().to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
[distribution] => {
|
||||||
|
let requirement = uv_pep508::Requirement {
|
||||||
|
name: distribution.name().clone(),
|
||||||
|
version_or_url: Some(VersionOrUrl::Url(
|
||||||
|
requirement.url.clone(),
|
||||||
|
)),
|
||||||
|
marker: requirement.marker,
|
||||||
|
extras: requirement.extras.clone(),
|
||||||
|
origin: requirement.origin.clone(),
|
||||||
|
};
|
||||||
|
named.push(Cow::Owned(Requirement::from(requirement)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok(SatisfiesResult::Unsatisfied(
|
||||||
|
requirement.url.verbatim.raw().to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
named
|
||||||
|
};
|
||||||
|
|
||||||
|
self.satisfies_requirements(
|
||||||
|
requirements.iter().map(Cow::as_ref),
|
||||||
|
constraints.iter().map(|constraint| &constraint.requirement),
|
||||||
|
overrides.iter().map(Cow::as_ref),
|
||||||
|
markers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [`SitePackages::satisfies_spec`], but with resolved names for all requirements.
|
||||||
|
pub fn satisfies_requirements<'a>(
|
||||||
|
&self,
|
||||||
|
requirements: impl ExactSizeIterator<Item = &'a Requirement>,
|
||||||
|
constraints: impl Iterator<Item = &'a Requirement>,
|
||||||
|
overrides: impl Iterator<Item = &'a Requirement>,
|
||||||
|
markers: &ResolverMarkerEnvironment,
|
||||||
|
) -> Result<SatisfiesResult> {
|
||||||
|
// Collect the constraints and overrides by package name.
|
||||||
|
let constraints: FxHashMap<&PackageName, Vec<&Requirement>> =
|
||||||
|
constraints.fold(FxHashMap::default(), |mut constraints, constraint| {
|
||||||
constraints
|
constraints
|
||||||
.entry(&constraint.requirement.name)
|
.entry(&constraint.name)
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(&constraint.requirement);
|
.push(constraint);
|
||||||
constraints
|
constraints
|
||||||
});
|
});
|
||||||
|
let overrides: FxHashMap<&PackageName, Vec<&Requirement>> =
|
||||||
|
overrides.fold(FxHashMap::default(), |mut overrides, r#override| {
|
||||||
|
overrides
|
||||||
|
.entry(&r#override.name)
|
||||||
|
.or_default()
|
||||||
|
.push(r#override);
|
||||||
|
overrides
|
||||||
|
});
|
||||||
|
|
||||||
let mut stack = Vec::with_capacity(requirements.len());
|
let mut stack = Vec::with_capacity(requirements.len());
|
||||||
let mut seen = FxHashSet::with_capacity_and_hasher(requirements.len(), FxBuildHasher);
|
let mut seen = FxHashSet::with_capacity_and_hasher(requirements.len(), FxBuildHasher);
|
||||||
|
|
||||||
// Add the direct requirements to the queue.
|
// Add the direct requirements to the queue.
|
||||||
for entry in requirements {
|
for requirement in requirements {
|
||||||
if entry.requirement.evaluate_markers(Some(markers), &[]) {
|
if let Some(r#overrides) = overrides.get(&requirement.name) {
|
||||||
if seen.insert(entry.clone()) {
|
for dependency in r#overrides {
|
||||||
stack.push(entry.clone());
|
if dependency.evaluate_markers(Some(markers), &[]) {
|
||||||
}
|
if seen.insert((*dependency).clone()) {
|
||||||
}
|
stack.push(Cow::Borrowed(*dependency));
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that all non-editable requirements are met.
|
|
||||||
while let Some(entry) = stack.pop() {
|
|
||||||
let installed = match &entry.requirement {
|
|
||||||
UnresolvedRequirement::Named(requirement) => self.get_packages(&requirement.name),
|
|
||||||
UnresolvedRequirement::Unnamed(requirement) => {
|
|
||||||
self.get_urls(requirement.url.verbatim.raw())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match installed.as_slice() {
|
|
||||||
[] => {
|
|
||||||
// The package isn't installed.
|
|
||||||
return Ok(SatisfiesResult::Unsatisfied(entry.requirement.to_string()));
|
|
||||||
}
|
|
||||||
[distribution] => {
|
|
||||||
match RequirementSatisfaction::check(
|
|
||||||
distribution,
|
|
||||||
entry.requirement.source().as_ref(),
|
|
||||||
)? {
|
|
||||||
RequirementSatisfaction::Mismatch | RequirementSatisfaction::OutOfDate => {
|
|
||||||
return Ok(SatisfiesResult::Unsatisfied(entry.requirement.to_string()))
|
|
||||||
}
|
|
||||||
RequirementSatisfaction::Satisfied => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that the installed version satisfies the constraints.
|
|
||||||
for constraint in constraints.get(&distribution.name()).into_iter().flatten() {
|
|
||||||
match RequirementSatisfaction::check(distribution, &constraint.source)? {
|
|
||||||
RequirementSatisfaction::Mismatch
|
|
||||||
| RequirementSatisfaction::OutOfDate => {
|
|
||||||
return Ok(SatisfiesResult::Unsatisfied(
|
|
||||||
entry.requirement.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
RequirementSatisfaction::Satisfied => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse into the dependencies.
|
|
||||||
let metadata = distribution
|
|
||||||
.metadata()
|
|
||||||
.with_context(|| format!("Failed to read metadata for: {distribution}"))?;
|
|
||||||
|
|
||||||
// Add the dependencies to the queue.
|
|
||||||
for dependency in metadata.requires_dist {
|
|
||||||
if dependency.evaluate_markers(markers, entry.requirement.extras()) {
|
|
||||||
let dependency = UnresolvedRequirementSpecification {
|
|
||||||
requirement: UnresolvedRequirement::Named(Requirement::from(
|
|
||||||
dependency,
|
|
||||||
)),
|
|
||||||
hashes: vec![],
|
|
||||||
};
|
|
||||||
if seen.insert(dependency.clone()) {
|
|
||||||
stack.push(dependency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// There are multiple installed distributions for the same package.
|
|
||||||
return Ok(SatisfiesResult::Unsatisfied(entry.requirement.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(SatisfiesResult::Fresh {
|
|
||||||
recursive_requirements: seen,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like [`SitePackages::satisfies`], but with resolved names for all requirements.
|
|
||||||
pub fn satisfies_names(
|
|
||||||
&self,
|
|
||||||
requirements: &[NameRequirementSpecification],
|
|
||||||
constraints: &[NameRequirementSpecification],
|
|
||||||
overrides: &[NameRequirementSpecification],
|
|
||||||
markers: &ResolverMarkerEnvironment,
|
|
||||||
) -> Result<SatisfiesResult> {
|
|
||||||
// Collect the constraints and overrides by package name.
|
|
||||||
let constraints: FxHashMap<&PackageName, Vec<&Requirement>> =
|
|
||||||
constraints
|
|
||||||
.iter()
|
|
||||||
.fold(FxHashMap::default(), |mut constraints, constraint| {
|
|
||||||
constraints
|
|
||||||
.entry(&constraint.requirement.name)
|
|
||||||
.or_default()
|
|
||||||
.push(&constraint.requirement);
|
|
||||||
constraints
|
|
||||||
});
|
|
||||||
let overrides: FxHashMap<&PackageName, Vec<&Requirement>> =
|
|
||||||
overrides
|
|
||||||
.iter()
|
|
||||||
.fold(FxHashMap::default(), |mut overrides, r#override| {
|
|
||||||
overrides
|
|
||||||
.entry(&r#override.requirement.name)
|
|
||||||
.or_default()
|
|
||||||
.push(&r#override.requirement);
|
|
||||||
overrides
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut stack = Vec::with_capacity(requirements.len());
|
|
||||||
let mut seen = FxHashSet::with_capacity_and_hasher(requirements.len(), FxBuildHasher);
|
|
||||||
|
|
||||||
// Add the direct requirements to the queue.
|
|
||||||
for entry in requirements {
|
|
||||||
if let Some(r#overrides) = overrides.get(&entry.requirement.name) {
|
|
||||||
for r#override in r#overrides {
|
|
||||||
if r#override.evaluate_markers(Some(markers), &[]) {
|
|
||||||
let entry = NameRequirementSpecification::from((*r#override).clone());
|
|
||||||
if seen.insert(entry.clone()) {
|
|
||||||
stack.push(entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if entry.requirement.evaluate_markers(Some(markers), &[]) {
|
if requirement.evaluate_markers(Some(markers), &[]) {
|
||||||
if seen.insert(entry.clone()) {
|
if seen.insert(requirement.clone()) {
|
||||||
stack.push(entry.clone());
|
stack.push(Cow::Borrowed(requirement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that all non-editable requirements are met.
|
// Verify that all non-editable requirements are met.
|
||||||
while let Some(entry) = stack.pop() {
|
while let Some(requirement) = stack.pop() {
|
||||||
let name = &entry.requirement.name;
|
let name = &requirement.name;
|
||||||
let installed = self.get_packages(name);
|
let installed = self.get_packages(name);
|
||||||
match installed.as_slice() {
|
match installed.as_slice() {
|
||||||
[] => {
|
[] => {
|
||||||
// The package isn't installed.
|
// The package isn't installed.
|
||||||
return Ok(SatisfiesResult::Unsatisfied(entry.requirement.to_string()));
|
return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
|
||||||
}
|
}
|
||||||
[distribution] => {
|
[distribution] => {
|
||||||
// Validate that the requirement is satisfied.
|
// Validate that the requirement is satisfied.
|
||||||
if entry.requirement.evaluate_markers(Some(markers), &[]) {
|
if requirement.evaluate_markers(Some(markers), &[]) {
|
||||||
match RequirementSatisfaction::check(
|
match RequirementSatisfaction::check(distribution, &requirement.source)? {
|
||||||
distribution,
|
|
||||||
&entry.requirement.source,
|
|
||||||
)? {
|
|
||||||
RequirementSatisfaction::Mismatch
|
RequirementSatisfaction::Mismatch
|
||||||
| RequirementSatisfaction::OutOfDate => {
|
| RequirementSatisfaction::OutOfDate => {
|
||||||
return Ok(SatisfiesResult::Unsatisfied(
|
return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()))
|
||||||
entry.requirement.to_string(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
RequirementSatisfaction::Satisfied => {}
|
RequirementSatisfaction::Satisfied => {}
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +454,7 @@ impl SitePackages {
|
||||||
RequirementSatisfaction::Mismatch
|
RequirementSatisfaction::Mismatch
|
||||||
| RequirementSatisfaction::OutOfDate => {
|
| RequirementSatisfaction::OutOfDate => {
|
||||||
return Ok(SatisfiesResult::Unsatisfied(
|
return Ok(SatisfiesResult::Unsatisfied(
|
||||||
entry.requirement.to_string(),
|
requirement.to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
RequirementSatisfaction::Satisfied => {}
|
RequirementSatisfaction::Satisfied => {}
|
||||||
|
|
@ -485,22 +472,16 @@ impl SitePackages {
|
||||||
let dependency = Requirement::from(dependency);
|
let dependency = Requirement::from(dependency);
|
||||||
if let Some(r#overrides) = overrides.get(&dependency.name) {
|
if let Some(r#overrides) = overrides.get(&dependency.name) {
|
||||||
for dependency in r#overrides {
|
for dependency in r#overrides {
|
||||||
if dependency
|
if dependency.evaluate_markers(Some(markers), &requirement.extras) {
|
||||||
.evaluate_markers(Some(markers), &entry.requirement.extras)
|
if seen.insert((*dependency).clone()) {
|
||||||
{
|
stack.push(Cow::Borrowed(*dependency));
|
||||||
let dependency =
|
|
||||||
NameRequirementSpecification::from((*dependency).clone());
|
|
||||||
if seen.insert(dependency.clone()) {
|
|
||||||
stack.push(dependency);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if dependency.evaluate_markers(Some(markers), &entry.requirement.extras)
|
if dependency.evaluate_markers(Some(markers), &requirement.extras) {
|
||||||
{
|
|
||||||
let dependency = NameRequirementSpecification::from(dependency);
|
|
||||||
if seen.insert(dependency.clone()) {
|
if seen.insert(dependency.clone()) {
|
||||||
stack.push(dependency);
|
stack.push(Cow::Owned(dependency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -508,13 +489,13 @@ impl SitePackages {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// There are multiple installed distributions for the same package.
|
// There are multiple installed distributions for the same package.
|
||||||
return Ok(SatisfiesResult::Unsatisfied(entry.requirement.to_string()));
|
return Ok(SatisfiesResult::Unsatisfied(requirement.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SatisfiesResult::Fresh {
|
Ok(SatisfiesResult::Fresh {
|
||||||
recursive_requirements: FxHashSet::default(),
|
recursive_requirements: seen,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -525,7 +506,7 @@ pub enum SatisfiesResult {
|
||||||
/// All requirements are recursively satisfied.
|
/// All requirements are recursively satisfied.
|
||||||
Fresh {
|
Fresh {
|
||||||
/// The flattened set (transitive closure) of all requirements checked.
|
/// The flattened set (transitive closure) of all requirements checked.
|
||||||
recursive_requirements: FxHashSet<UnresolvedRequirementSpecification>,
|
recursive_requirements: FxHashSet<Requirement>,
|
||||||
},
|
},
|
||||||
/// We found an unsatisfied requirement. Since we exit early, we only know about the first
|
/// We found an unsatisfied requirement. Since we exit early, we only know about the first
|
||||||
/// unsatisfied requirement.
|
/// unsatisfied requirement.
|
||||||
|
|
|
||||||
|
|
@ -239,10 +239,9 @@ pub(crate) async fn pip_install(
|
||||||
if reinstall.is_none()
|
if reinstall.is_none()
|
||||||
&& upgrade.is_none()
|
&& upgrade.is_none()
|
||||||
&& source_trees.is_empty()
|
&& source_trees.is_empty()
|
||||||
&& overrides.is_empty()
|
|
||||||
&& matches!(modifications, Modifications::Sufficient)
|
&& matches!(modifications, Modifications::Sufficient)
|
||||||
{
|
{
|
||||||
match site_packages.satisfies(&requirements, &constraints, &marker_env)? {
|
match site_packages.satisfies_spec(&requirements, &constraints, &overrides, &marker_env)? {
|
||||||
// If the requirements are already satisfied, we're done.
|
// If the requirements are already satisfied, we're done.
|
||||||
SatisfiesResult::Fresh {
|
SatisfiesResult::Fresh {
|
||||||
recursive_requirements,
|
recursive_requirements,
|
||||||
|
|
@ -250,7 +249,7 @@ pub(crate) async fn pip_install(
|
||||||
if enabled!(Level::DEBUG) {
|
if enabled!(Level::DEBUG) {
|
||||||
for requirement in recursive_requirements
|
for requirement in recursive_requirements
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| entry.requirement.to_string())
|
.map(ToString::to_string)
|
||||||
.sorted()
|
.sorted()
|
||||||
{
|
{
|
||||||
debug!("Requirement satisfied: {requirement}");
|
debug!("Requirement satisfied: {requirement}");
|
||||||
|
|
|
||||||
|
|
@ -2009,8 +2009,8 @@ pub(crate) async fn update_environment(
|
||||||
|
|
||||||
// Check if the current environment satisfies the requirements
|
// Check if the current environment satisfies the requirements
|
||||||
let site_packages = SitePackages::from_environment(&venv)?;
|
let site_packages = SitePackages::from_environment(&venv)?;
|
||||||
if source_trees.is_empty() && reinstall.is_none() && upgrade.is_none() && overrides.is_empty() {
|
if source_trees.is_empty() && reinstall.is_none() && upgrade.is_none() {
|
||||||
match site_packages.satisfies(&requirements, &constraints, &marker_env)? {
|
match site_packages.satisfies_spec(&requirements, &constraints, &overrides, &marker_env)? {
|
||||||
// If the requirements are already satisfied, we're done.
|
// If the requirements are already satisfied, we're done.
|
||||||
SatisfiesResult::Fresh {
|
SatisfiesResult::Fresh {
|
||||||
recursive_requirements,
|
recursive_requirements,
|
||||||
|
|
@ -2022,7 +2022,7 @@ pub(crate) async fn update_environment(
|
||||||
"All requirements satisfied: {}",
|
"All requirements satisfied: {}",
|
||||||
recursive_requirements
|
recursive_requirements
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| entry.requirement.to_string())
|
.map(ToString::to_string)
|
||||||
.sorted()
|
.sorted()
|
||||||
.join(" | ")
|
.join(" | ")
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1084,9 +1084,10 @@ fn can_skip_ephemeral(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match site_packages.satisfies(
|
match site_packages.satisfies_spec(
|
||||||
&spec.requirements,
|
&spec.requirements,
|
||||||
&spec.constraints,
|
&spec.constraints,
|
||||||
|
&spec.overrides,
|
||||||
&base_interpreter.resolver_marker_environment(),
|
&base_interpreter.resolver_marker_environment(),
|
||||||
) {
|
) {
|
||||||
// If the requirements are already satisfied, we're done.
|
// If the requirements are already satisfied, we're done.
|
||||||
|
|
@ -1097,7 +1098,7 @@ fn can_skip_ephemeral(
|
||||||
"Base environment satisfies requirements: {}",
|
"Base environment satisfies requirements: {}",
|
||||||
recursive_requirements
|
recursive_requirements
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entry| entry.requirement.to_string())
|
.map(ToString::to_string)
|
||||||
.sorted()
|
.sorted()
|
||||||
.join(" | ")
|
.join(" | ")
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -814,28 +814,11 @@ async fn get_or_create_environment(
|
||||||
{
|
{
|
||||||
// Check if the installed packages meet the requirements.
|
// Check if the installed packages meet the requirements.
|
||||||
let site_packages = SitePackages::from_environment(&environment)?;
|
let site_packages = SitePackages::from_environment(&environment)?;
|
||||||
|
|
||||||
let requirements = requirements
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(NameRequirementSpecification::from)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let constraints = constraints
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(NameRequirementSpecification::from)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let overrides = overrides
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(NameRequirementSpecification::from)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
site_packages.satisfies_names(
|
site_packages.satisfies_requirements(
|
||||||
&requirements,
|
requirements.iter(),
|
||||||
&constraints,
|
constraints.iter(),
|
||||||
&overrides,
|
overrides.iter(),
|
||||||
&interpreter.resolver_marker_environment()
|
&interpreter.resolver_marker_environment()
|
||||||
),
|
),
|
||||||
Ok(SatisfiesResult::Fresh { .. })
|
Ok(SatisfiesResult::Fresh { .. })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue