mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-31 20:09:09 +00:00
Remove special-casing for editable requirements (#3869)
## Summary There are a few behavior changes in here: - We now enforce `--require-hashes` for editables, like pip. So if you use `--require-hashes` with an editable requirement, we'll reject it. I could change this if it seems off. - We now treat source tree requirements, editable or not (e.g., both `-e ./black` and `./black`) as if `--refresh` is always enabled. This doesn't mean that we _always_ rebuild them; but if you pass `--reinstall`, then yes, we always rebuild them. I think this is an improvement and is close to how editables work today. Closes #3844. Closes #2695.
This commit is contained in:
parent
063a0a4384
commit
1fc6a59707
64 changed files with 583 additions and 1813 deletions
|
|
@ -1,44 +0,0 @@
|
|||
use rustc_hash::FxHashMap;
|
||||
|
||||
use distribution_types::{LocalEditable, Requirements};
|
||||
use pypi_types::Metadata23;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
/// A built editable for which we know its dependencies and other static metadata.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BuiltEditableMetadata {
|
||||
pub built: LocalEditable,
|
||||
pub metadata: Metadata23,
|
||||
pub requirements: Requirements,
|
||||
}
|
||||
|
||||
/// A set of editable packages, indexed by package name.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct Editables(FxHashMap<PackageName, BuiltEditableMetadata>);
|
||||
|
||||
impl Editables {
|
||||
/// Create a new set of editables from a set of requirements.
|
||||
pub(crate) fn from_requirements(requirements: Vec<BuiltEditableMetadata>) -> Self {
|
||||
Self(
|
||||
requirements
|
||||
.into_iter()
|
||||
.map(|editable| (editable.metadata.name.clone(), editable))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the editable for a package.
|
||||
pub(crate) fn get(&self, name: &PackageName) -> Option<&BuiltEditableMetadata> {
|
||||
self.0.get(name)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given package is editable.
|
||||
pub(crate) fn contains(&self, name: &PackageName) -> bool {
|
||||
self.0.contains_key(name)
|
||||
}
|
||||
|
||||
/// Iterate over all editables.
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &BuiltEditableMetadata> {
|
||||
self.0.values()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
pub use dependency_mode::DependencyMode;
|
||||
pub use editables::BuiltEditableMetadata;
|
||||
pub use error::ResolveError;
|
||||
pub use exclude_newer::ExcludeNewer;
|
||||
pub use exclusions::Exclusions;
|
||||
|
|
@ -25,7 +24,6 @@ mod candidate_selector;
|
|||
|
||||
mod dependency_mode;
|
||||
mod dependency_provider;
|
||||
mod editables;
|
||||
mod error;
|
||||
mod exclude_newer;
|
||||
mod exclusions;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use uv_configuration::{Constraints, Overrides};
|
|||
use uv_normalize::PackageName;
|
||||
use uv_types::RequestedRequirements;
|
||||
|
||||
use crate::editables::BuiltEditableMetadata;
|
||||
use crate::{preferences::Preference, DependencyMode, Exclusions};
|
||||
|
||||
/// A manifest of requirements, constraints, and preferences.
|
||||
|
|
@ -31,12 +30,6 @@ pub struct Manifest {
|
|||
/// The name of the project.
|
||||
pub(crate) project: Option<PackageName>,
|
||||
|
||||
/// The editable requirements for the project, which are built in advance.
|
||||
///
|
||||
/// The requirements of the editables should be included in resolution as if they were
|
||||
/// direct requirements in their own right.
|
||||
pub(crate) editables: Vec<BuiltEditableMetadata>,
|
||||
|
||||
/// The installed packages to exclude from consideration during resolution.
|
||||
///
|
||||
/// These typically represent packages that are being upgraded or reinstalled
|
||||
|
|
@ -59,7 +52,6 @@ impl Manifest {
|
|||
overrides: Overrides,
|
||||
preferences: Vec<Preference>,
|
||||
project: Option<PackageName>,
|
||||
editables: Vec<BuiltEditableMetadata>,
|
||||
exclusions: Exclusions,
|
||||
lookaheads: Vec<RequestedRequirements>,
|
||||
) -> Self {
|
||||
|
|
@ -69,7 +61,6 @@ impl Manifest {
|
|||
overrides,
|
||||
preferences,
|
||||
project,
|
||||
editables,
|
||||
exclusions,
|
||||
lookaheads,
|
||||
}
|
||||
|
|
@ -82,7 +73,6 @@ impl Manifest {
|
|||
overrides: Overrides::default(),
|
||||
preferences: Vec::new(),
|
||||
project: None,
|
||||
editables: Vec::new(),
|
||||
exclusions: Exclusions::default(),
|
||||
lookaheads: Vec::new(),
|
||||
}
|
||||
|
|
@ -113,13 +103,6 @@ impl Manifest {
|
|||
requirement.evaluate_markers(markers, lookahead.extras())
|
||||
})
|
||||
})
|
||||
.chain(self.editables.iter().flat_map(move |editable| {
|
||||
self.overrides
|
||||
.apply(&editable.requirements.dependencies)
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.built.extras)
|
||||
})
|
||||
}))
|
||||
.chain(
|
||||
self.overrides
|
||||
.apply(&self.requirements)
|
||||
|
|
@ -175,13 +158,6 @@ impl Manifest {
|
|||
requirement.evaluate_markers(markers, lookahead.extras())
|
||||
})
|
||||
})
|
||||
.chain(self.editables.iter().flat_map(move |editable| {
|
||||
self.overrides
|
||||
.apply(&editable.requirements.dependencies)
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.built.extras)
|
||||
})
|
||||
}))
|
||||
.chain(
|
||||
self.overrides
|
||||
.apply(&self.requirements)
|
||||
|
|
@ -213,6 +189,6 @@ impl Manifest {
|
|||
|
||||
/// Returns the number of input requirements.
|
||||
pub fn num_requirements(&self) -> usize {
|
||||
self.requirements.len() + self.editables.len()
|
||||
self.requirements.len()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,6 @@ impl PubGrubDependencies {
|
|||
Ok(Self(dependencies))
|
||||
}
|
||||
|
||||
/// Add a [`PubGrubPackage`] and [`PubGrubVersion`] range into the dependencies.
|
||||
pub(crate) fn push(&mut self, package: PubGrubPackage, version: Range<Version>) {
|
||||
self.0.push((package, version));
|
||||
}
|
||||
|
||||
/// Iterate over the dependencies.
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &(PubGrubPackage, Range<Version>)> {
|
||||
self.0.iter()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
pub(crate) use crate::pubgrub::dependencies::{PubGrubDependencies, PubGrubRequirement};
|
||||
pub(crate) use crate::pubgrub::dependencies::PubGrubDependencies;
|
||||
pub(crate) use crate::pubgrub::distribution::PubGrubDistribution;
|
||||
pub(crate) use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
|
||||
pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use owo_colors::OwoColorize;
|
||||
use petgraph::visit::EdgeRef;
|
||||
use petgraph::Direction;
|
||||
|
||||
use distribution_types::{IndexUrl, LocalEditable, Name, SourceAnnotations, Verbatim};
|
||||
use pypi_types::HashDigest;
|
||||
use distribution_types::{Name, SourceAnnotations};
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::resolution::AnnotatedDist;
|
||||
use crate::ResolutionGraph;
|
||||
|
||||
/// A [`std::fmt::Display`] implementation for the resolution graph.
|
||||
|
|
@ -77,48 +74,6 @@ impl<'a> DisplayResolutionGraph<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Node<'a> {
|
||||
/// A node linked to an editable distribution.
|
||||
Editable(&'a LocalEditable),
|
||||
/// A node linked to a non-editable distribution.
|
||||
Distribution(&'a AnnotatedDist),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum NodeKey<'a> {
|
||||
/// A node linked to an editable distribution, sorted by verbatim representation.
|
||||
Editable(Cow<'a, str>),
|
||||
/// A node linked to a non-editable distribution, sorted by package name.
|
||||
Distribution(&'a PackageName),
|
||||
}
|
||||
|
||||
impl<'a> Node<'a> {
|
||||
/// Return a comparable key for the node.
|
||||
fn key(&self) -> NodeKey<'a> {
|
||||
match self {
|
||||
Node::Editable(editable) => NodeKey::Editable(editable.verbatim()),
|
||||
Node::Distribution(annotated) => NodeKey::Distribution(annotated.name()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`IndexUrl`] of the distribution, if any.
|
||||
fn index(&self) -> Option<&IndexUrl> {
|
||||
match self {
|
||||
Node::Editable(_) => None,
|
||||
Node::Distribution(annotated) => annotated.dist.index(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the hashes of the distribution.
|
||||
fn hashes(&self) -> &[HashDigest] {
|
||||
match self {
|
||||
Node::Editable(_) => &[],
|
||||
Node::Distribution(annotated) => &annotated.hashes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the graph in the `{name}=={version}` format of requirements.txt that pip uses.
|
||||
impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
@ -134,32 +89,22 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let node = if let Some(editable) = self.resolution.editables.get(name) {
|
||||
Node::Editable(&editable.built)
|
||||
} else {
|
||||
Node::Distribution(dist)
|
||||
};
|
||||
Some((index, node))
|
||||
Some((index, dist))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort the nodes by name, but with editable packages first.
|
||||
nodes.sort_unstable_by_key(|(index, node)| (node.key(), *index));
|
||||
nodes.sort_unstable_by_key(|(index, node)| (node.to_comparator(), *index));
|
||||
|
||||
// Print out the dependency graph.
|
||||
for (index, node) in nodes {
|
||||
// Display the node itself.
|
||||
let mut line = match node {
|
||||
Node::Editable(editable) => format!("-e {}", editable.verbatim()),
|
||||
Node::Distribution(dist) => {
|
||||
dist.to_requirements_txt(self.include_extras).to_string()
|
||||
}
|
||||
};
|
||||
let mut line = node.to_requirements_txt(self.include_extras).to_string();
|
||||
|
||||
// Display the distribution hashes, if any.
|
||||
let mut has_hashes = false;
|
||||
if self.show_hashes {
|
||||
for hash in node.hashes() {
|
||||
for hash in &node.hashes {
|
||||
has_hashes = true;
|
||||
line.push_str(" \\\n");
|
||||
line.push_str(" --hash=");
|
||||
|
|
@ -184,12 +129,7 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
|
||||
// Include all external sources (e.g., requirements files).
|
||||
let default = BTreeSet::default();
|
||||
let source = match node {
|
||||
Node::Editable(editable) => {
|
||||
self.sources.get_editable(&editable.url).unwrap_or(&default)
|
||||
}
|
||||
Node::Distribution(dist) => self.sources.get(dist.name()).unwrap_or(&default),
|
||||
};
|
||||
let source = self.sources.get(node.name()).unwrap_or(&default);
|
||||
|
||||
match self.annotation_style {
|
||||
AnnotationStyle::Line => match edges.as_slice() {
|
||||
|
|
@ -261,7 +201,7 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
// If enabled, include indexes to indicate which index was used for each package (e.g.,
|
||||
// `# from https://pypi.org/simple`).
|
||||
if self.include_index_annotation {
|
||||
if let Some(index) = node.index() {
|
||||
if let Some(index) = node.dist.index() {
|
||||
let url = index.redacted();
|
||||
writeln!(f, "{}", format!(" # from {url}").green())?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use pypi_types::{ParsedUrlError, Yanked};
|
|||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::dependency_provider::UvDependencyProvider;
|
||||
use crate::editables::Editables;
|
||||
use crate::pins::FilePins;
|
||||
use crate::preferences::Preferences;
|
||||
use crate::pubgrub::{PubGrubDistribution, PubGrubPackageInner};
|
||||
|
|
@ -34,8 +33,6 @@ use crate::{
|
|||
pub struct ResolutionGraph {
|
||||
/// The underlying graph.
|
||||
pub(crate) petgraph: petgraph::graph::Graph<AnnotatedDist, Range<Version>, petgraph::Directed>,
|
||||
/// The set of editable requirements in this resolution.
|
||||
pub(crate) editables: Editables,
|
||||
/// Any diagnostics that were encountered while building the graph.
|
||||
pub(crate) diagnostics: Vec<ResolutionDiagnostic>,
|
||||
}
|
||||
|
|
@ -50,7 +47,6 @@ impl ResolutionGraph {
|
|||
distributions: &FxOnceMap<VersionId, Arc<MetadataResponse>>,
|
||||
state: &State<UvDependencyProvider>,
|
||||
preferences: &Preferences,
|
||||
editables: Editables,
|
||||
) -> anyhow::Result<Self, ResolveError> {
|
||||
// Collect and validate the extras.
|
||||
let mut extras = FxHashMap::default();
|
||||
|
|
@ -102,50 +98,34 @@ impl ResolutionGraph {
|
|||
marker: None,
|
||||
url: Some(url),
|
||||
} => {
|
||||
if let Some(editable) = editables.get(name) {
|
||||
if editable.metadata.provides_extras.contains(extra) {
|
||||
extras
|
||||
.entry(name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(extra.clone());
|
||||
} else {
|
||||
let dist = Dist::from_editable(name.clone(), editable.built.clone())?;
|
||||
let dist = PubGrubDistribution::from_url(name, url);
|
||||
|
||||
diagnostics.push(ResolutionDiagnostic::MissingExtra {
|
||||
dist: dist.into(),
|
||||
extra: extra.clone(),
|
||||
});
|
||||
}
|
||||
let response = distributions.get(&dist.version_id()).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
});
|
||||
|
||||
let MetadataResponse::Found(archive) = &*response else {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
};
|
||||
|
||||
if archive.metadata.provides_extras.contains(extra) {
|
||||
extras
|
||||
.entry(name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(extra.clone());
|
||||
} else {
|
||||
let dist = PubGrubDistribution::from_url(name, url);
|
||||
let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?;
|
||||
|
||||
let response = distributions.get(&dist.version_id()).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
diagnostics.push(ResolutionDiagnostic::MissingExtra {
|
||||
dist: dist.into(),
|
||||
extra: extra.clone(),
|
||||
});
|
||||
|
||||
let MetadataResponse::Found(archive) = &*response else {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
};
|
||||
|
||||
if archive.metadata.provides_extras.contains(extra) {
|
||||
extras
|
||||
.entry(name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(extra.clone());
|
||||
} else {
|
||||
let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?;
|
||||
|
||||
diagnostics.push(ResolutionDiagnostic::MissingExtra {
|
||||
dist: dist.into(),
|
||||
extra: extra.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -254,75 +234,60 @@ impl ResolutionGraph {
|
|||
url: Some(url),
|
||||
} => {
|
||||
// Create the distribution.
|
||||
if let Some(editable) = editables.get(name) {
|
||||
let dist = Dist::from_editable(name.clone(), editable.built.clone())?;
|
||||
|
||||
// Add the distribution to the graph.
|
||||
let index = petgraph.add_node(AnnotatedDist {
|
||||
dist: dist.into(),
|
||||
extras: editable.built.extras.clone(),
|
||||
hashes: vec![],
|
||||
metadata: editable.metadata.clone(),
|
||||
});
|
||||
inverse.insert(name, index);
|
||||
} else {
|
||||
let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?;
|
||||
let dist = Dist::from_url(name.clone(), url_to_precise(url.clone()))?;
|
||||
|
||||
// Extract the hashes, preserving those that were already present in the
|
||||
// lockfile if necessary.
|
||||
let hashes = if let Some(digests) = preferences
|
||||
.match_hashes(name, version)
|
||||
.filter(|digests| !digests.is_empty())
|
||||
{
|
||||
digests.to_vec()
|
||||
} else if let Some(metadata_response) =
|
||||
distributions.get(&dist.version_id())
|
||||
{
|
||||
if let MetadataResponse::Found(ref archive) = *metadata_response {
|
||||
let mut digests = archive.hashes.clone();
|
||||
digests.sort_unstable();
|
||||
digests
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
// Extract the hashes, preserving those that were already present in the
|
||||
// lockfile if necessary.
|
||||
let hashes = if let Some(digests) = preferences
|
||||
.match_hashes(name, version)
|
||||
.filter(|digests| !digests.is_empty())
|
||||
{
|
||||
digests.to_vec()
|
||||
} else if let Some(metadata_response) = distributions.get(&dist.version_id()) {
|
||||
if let MetadataResponse::Found(ref archive) = *metadata_response {
|
||||
let mut digests = archive.hashes.clone();
|
||||
digests.sort_unstable();
|
||||
digests
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// Extract the metadata.
|
||||
let metadata = {
|
||||
let dist = PubGrubDistribution::from_url(name, url);
|
||||
|
||||
let response =
|
||||
distributions.get(&dist.version_id()).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
});
|
||||
|
||||
let MetadataResponse::Found(archive) = &*response else {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
};
|
||||
|
||||
archive.metadata.clone()
|
||||
};
|
||||
|
||||
// Extract the extras.
|
||||
let extras = extras.get(name).cloned().unwrap_or_default();
|
||||
|
||||
// Add the distribution to the graph.
|
||||
let index = petgraph.add_node(AnnotatedDist {
|
||||
dist: dist.into(),
|
||||
extras,
|
||||
hashes,
|
||||
metadata,
|
||||
});
|
||||
inverse.insert(name, index);
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// Extract the metadata.
|
||||
let metadata = {
|
||||
let dist = PubGrubDistribution::from_url(name, url);
|
||||
|
||||
let response = distributions.get(&dist.version_id()).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
});
|
||||
|
||||
let MetadataResponse::Found(archive) = &*response else {
|
||||
panic!(
|
||||
"Every package should have metadata: {:?}",
|
||||
dist.version_id()
|
||||
)
|
||||
};
|
||||
|
||||
archive.metadata.clone()
|
||||
};
|
||||
|
||||
// Extract the extras.
|
||||
let extras = extras.get(name).cloned().unwrap_or_default();
|
||||
|
||||
// Add the distribution to the graph.
|
||||
let index = petgraph.add_node(AnnotatedDist {
|
||||
dist: dist.into(),
|
||||
extras,
|
||||
hashes,
|
||||
metadata,
|
||||
});
|
||||
inverse.insert(name, index);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
|
@ -380,7 +345,6 @@ impl ResolutionGraph {
|
|||
|
||||
Ok(Self {
|
||||
petgraph,
|
||||
editables,
|
||||
diagnostics,
|
||||
})
|
||||
}
|
||||
|
|
@ -523,13 +487,7 @@ impl ResolutionGraph {
|
|||
}
|
||||
|
||||
// Ensure that we consider markers from direct dependencies.
|
||||
let direct_reqs = manifest.requirements.iter().chain(
|
||||
manifest
|
||||
.editables
|
||||
.iter()
|
||||
.flat_map(|editable| &editable.requirements.dependencies),
|
||||
);
|
||||
for direct_req in manifest.apply(direct_reqs) {
|
||||
for direct_req in manifest.apply(manifest.requirements.iter()) {
|
||||
let Some(ref marker_tree) = direct_req.marker else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,6 +34,14 @@ impl AnnotatedDist {
|
|||
/// unnamed requirement for relative paths, which can't be represented with PEP 508 (but are
|
||||
/// supported in `requirements.txt`).
|
||||
pub(crate) fn to_requirements_txt(&self, include_extras: bool) -> Cow<str> {
|
||||
// If the URL is editable, write it as an editable requirement.
|
||||
if self.dist.is_editable() {
|
||||
if let VersionOrUrlRef::Url(url) = self.dist.version_or_url() {
|
||||
let given = url.verbatim();
|
||||
return Cow::Owned(format!("-e {given}"));
|
||||
}
|
||||
}
|
||||
|
||||
// If the URL is not _definitively_ an absolute `file://` URL, write it as a relative path.
|
||||
if self.dist.is_local() {
|
||||
if let VersionOrUrlRef::Url(url) = self.dist.version_or_url() {
|
||||
|
|
@ -94,6 +102,22 @@ impl AnnotatedDist {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_comparator(&self) -> RequirementsTxtComparator {
|
||||
if self.dist.is_editable() {
|
||||
if let VersionOrUrlRef::Url(url) = self.dist.version_or_url() {
|
||||
return RequirementsTxtComparator::Url(url.verbatim());
|
||||
}
|
||||
}
|
||||
|
||||
RequirementsTxtComparator::Name(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum RequirementsTxtComparator<'a> {
|
||||
Url(Cow<'a, str>),
|
||||
Name(&'a PackageName),
|
||||
}
|
||||
|
||||
impl Name for AnnotatedDist {
|
||||
|
|
|
|||
|
|
@ -37,14 +37,13 @@ use uv_types::{BuildContext, HashStrategy, InstalledPackagesProvider};
|
|||
|
||||
use crate::candidate_selector::{CandidateDist, CandidateSelector};
|
||||
use crate::dependency_provider::UvDependencyProvider;
|
||||
use crate::editables::Editables;
|
||||
use crate::error::ResolveError;
|
||||
use crate::manifest::Manifest;
|
||||
use crate::pins::FilePins;
|
||||
use crate::preferences::Preferences;
|
||||
use crate::pubgrub::{
|
||||
PubGrubDependencies, PubGrubDistribution, PubGrubPackage, PubGrubPackageInner,
|
||||
PubGrubPriorities, PubGrubPython, PubGrubRequirement, PubGrubSpecifier,
|
||||
PubGrubPriorities, PubGrubPython, PubGrubSpecifier,
|
||||
};
|
||||
use crate::python_requirement::PythonRequirement;
|
||||
use crate::resolution::ResolutionGraph;
|
||||
|
|
@ -85,7 +84,6 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
|
|||
overrides: Overrides,
|
||||
preferences: Preferences,
|
||||
exclusions: Exclusions,
|
||||
editables: Editables,
|
||||
urls: Urls,
|
||||
locals: Locals,
|
||||
dependency_mode: DependencyMode,
|
||||
|
|
@ -192,7 +190,6 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
overrides: manifest.overrides,
|
||||
preferences: Preferences::from_iter(manifest.preferences, markers),
|
||||
exclusions: manifest.exclusions,
|
||||
editables: Editables::from_requirements(manifest.editables),
|
||||
hasher: hasher.clone(),
|
||||
markers: markers.cloned(),
|
||||
python_requirement: python_requirement.clone(),
|
||||
|
|
@ -343,7 +340,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
self.index.distributions(),
|
||||
&state.pubgrub,
|
||||
&self.preferences,
|
||||
self.editables.clone(),
|
||||
);
|
||||
};
|
||||
state.next = highest_priority_pkg;
|
||||
|
|
@ -560,11 +556,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
return Err(ResolveError::UnhashedPackage(name.clone()));
|
||||
}
|
||||
|
||||
// If the package is an editable, we don't need to fetch metadata.
|
||||
if self.editables.contains(name) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Emit a request to fetch the metadata for this distribution.
|
||||
let dist = Dist::from_url(name.clone(), url.clone())?;
|
||||
if self.index.distributions().register(dist.version_id()) {
|
||||
|
|
@ -649,31 +640,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
url.verbatim
|
||||
);
|
||||
|
||||
// If the dist is an editable, return the version from the editable metadata.
|
||||
if let Some(editable) = self.editables.get(name) {
|
||||
let version = &editable.metadata.version;
|
||||
|
||||
// The version is incompatible with the requirement.
|
||||
if !range.contains(version) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// The version is incompatible due to its Python requirement.
|
||||
if let Some(requires_python) = editable.metadata.requires_python.as_ref() {
|
||||
let target = self.python_requirement.target();
|
||||
if !requires_python.contains(target) {
|
||||
return Ok(Some(ResolverVersion::Unavailable(
|
||||
version.clone(),
|
||||
UnavailableVersion::IncompatibleDist(IncompatibleDist::Source(
|
||||
IncompatibleSource::RequiresPython(requires_python.clone()),
|
||||
)),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Some(ResolverVersion::Available(version.clone())));
|
||||
}
|
||||
|
||||
let dist = PubGrubDistribution::from_url(name, url);
|
||||
let response = self
|
||||
.index
|
||||
|
|
@ -853,7 +819,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
self.markers.as_ref(),
|
||||
);
|
||||
|
||||
let mut dependencies = match dependencies {
|
||||
let dependencies = match dependencies {
|
||||
Ok(dependencies) => dependencies,
|
||||
Err(err) => {
|
||||
return Ok(Dependencies::Unavailable(
|
||||
|
|
@ -872,54 +838,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
self.visit_package(package, request_sink)?;
|
||||
}
|
||||
|
||||
// Add a dependency on each editable.
|
||||
for editable in self.editables.iter() {
|
||||
let package = PubGrubPackage::from_package(
|
||||
editable.metadata.name.clone(),
|
||||
None,
|
||||
None,
|
||||
&self.urls,
|
||||
);
|
||||
let version = Range::singleton(editable.metadata.version.clone());
|
||||
|
||||
// Update the package priorities.
|
||||
priorities.insert(&package, &version);
|
||||
|
||||
// Add the editable as a direct dependency.
|
||||
dependencies.push(package, version);
|
||||
|
||||
// Add a dependency on each extra.
|
||||
for extra in &editable.built.extras {
|
||||
dependencies.push(
|
||||
PubGrubPackage::from_package(
|
||||
editable.metadata.name.clone(),
|
||||
Some(extra.clone()),
|
||||
None,
|
||||
&self.urls,
|
||||
),
|
||||
Range::singleton(editable.metadata.version.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
// Add any constraints.
|
||||
for constraint in self
|
||||
.constraints
|
||||
.get(&editable.metadata.name)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
if constraint.evaluate_markers(self.markers.as_ref(), &[]) {
|
||||
let PubGrubRequirement { package, version } =
|
||||
PubGrubRequirement::from_constraint(
|
||||
constraint,
|
||||
&self.urls,
|
||||
&self.locals,
|
||||
)?;
|
||||
dependencies.push(package, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Dependencies::Available(dependencies.into()))
|
||||
}
|
||||
|
||||
|
|
@ -935,57 +853,22 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
if self.dependency_mode.is_direct() {
|
||||
// If an extra is provided, wait for the metadata to be available, since it's
|
||||
// still required for generating the lock file.
|
||||
if !self.editables.contains(name) {
|
||||
// Determine the distribution to lookup.
|
||||
let dist = match url {
|
||||
Some(url) => PubGrubDistribution::from_url(name, url),
|
||||
None => PubGrubDistribution::from_registry(name, version),
|
||||
};
|
||||
let version_id = dist.version_id();
|
||||
|
||||
// Wait for the metadata to be available.
|
||||
self.index
|
||||
.distributions()
|
||||
.wait_blocking(&version_id)
|
||||
.ok_or(ResolveError::Unregistered)?;
|
||||
}
|
||||
let dist = match url {
|
||||
Some(url) => PubGrubDistribution::from_url(name, url),
|
||||
None => PubGrubDistribution::from_registry(name, version),
|
||||
};
|
||||
let version_id = dist.version_id();
|
||||
|
||||
// Wait for the metadata to be available.
|
||||
self.index
|
||||
.distributions()
|
||||
.wait_blocking(&version_id)
|
||||
.ok_or(ResolveError::Unregistered)?;
|
||||
|
||||
return Ok(Dependencies::Available(Vec::default()));
|
||||
}
|
||||
|
||||
// Determine if the distribution is editable.
|
||||
if let Some(editable) = self.editables.get(name) {
|
||||
let requirements: Vec<_> = editable
|
||||
.metadata
|
||||
.requires_dist
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Requirement::from)
|
||||
.collect();
|
||||
let dependencies = PubGrubDependencies::from_requirements(
|
||||
&requirements,
|
||||
&self.constraints,
|
||||
&self.overrides,
|
||||
Some(name),
|
||||
extra.as_ref(),
|
||||
&self.urls,
|
||||
&self.locals,
|
||||
self.markers.as_ref(),
|
||||
)?;
|
||||
|
||||
for (dep_package, dep_version) in dependencies.iter() {
|
||||
debug!("Adding transitive dependency for {package}=={version}: {dep_package}{dep_version}");
|
||||
|
||||
// Update the package priorities.
|
||||
priorities.insert(dep_package, dep_version);
|
||||
|
||||
// Emit a request to fetch the metadata for this package.
|
||||
self.visit_package(dep_package, request_sink)?;
|
||||
}
|
||||
|
||||
return Ok(Dependencies::Available(dependencies.into()));
|
||||
}
|
||||
|
||||
// Determine the distribution to lookup.
|
||||
let dist = match url {
|
||||
Some(url) => PubGrubDistribution::from_url(name, url),
|
||||
|
|
@ -1232,6 +1115,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
ResolveError::FetchAndBuild(Box::new(source_dist), err)
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Some(Response::Dist { dist, metadata }))
|
||||
}
|
||||
|
||||
|
|
@ -1320,6 +1204,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
ResolveError::FetchAndBuild(Box::new(source_dist), err)
|
||||
}
|
||||
})?;
|
||||
|
||||
Response::Dist { dist, metadata }
|
||||
}
|
||||
ResolvedDist::Installed(dist) => {
|
||||
|
|
|
|||
|
|
@ -22,36 +22,6 @@ impl Urls {
|
|||
) -> Result<Self, ResolveError> {
|
||||
let mut urls: FxHashMap<PackageName, VerbatimParsedUrl> = FxHashMap::default();
|
||||
|
||||
// Add the editables themselves to the list of required URLs.
|
||||
for editable in &manifest.editables {
|
||||
let editable_url = VerbatimParsedUrl {
|
||||
parsed_url: ParsedUrl::Path(ParsedPathUrl {
|
||||
url: editable.built.url.to_url(),
|
||||
path: editable.built.path.clone(),
|
||||
editable: true,
|
||||
}),
|
||||
verbatim: editable.built.url.clone(),
|
||||
};
|
||||
if let Some(previous) =
|
||||
urls.insert(editable.metadata.name.clone(), editable_url.clone())
|
||||
{
|
||||
if !is_equal(&previous.verbatim, &editable_url.verbatim) {
|
||||
if is_same_reference(&previous.verbatim, &editable_url.verbatim) {
|
||||
debug!(
|
||||
"Allowing {} as a variant of {}",
|
||||
editable_url.verbatim, previous.verbatim
|
||||
);
|
||||
} else {
|
||||
return Err(ResolveError::ConflictingUrlsDirect(
|
||||
editable.metadata.name.clone(),
|
||||
previous.verbatim.verbatim().to_string(),
|
||||
editable_url.verbatim.verbatim().to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all direct requirements and constraints. If there are any conflicts, return an error.
|
||||
for requirement in manifest.requirements(markers, dependencies) {
|
||||
match &requirement.source {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue