mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 21:02:37 +00:00
Refactor editables for supporting them in bluejay commands (#3639)
This is split out from workspaces support, which needs editables in the bluejay commands. It consists mainly of refactorings: * Move the `editable` module one level up. * Introduce a `BuiltEditableMetadata` type for `(LocalEditable, Metadata23, Requirements)`. * Add editables to `InstalledPackagesProvider` so we can use `EmptyInstalledPackages` for them.
This commit is contained in:
parent
c32fb8647f
commit
95c9621541
20 changed files with 229 additions and 140 deletions
|
|
@ -1,36 +1,34 @@
|
|||
use std::hash::BuildHasherDefault;
|
||||
|
||||
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, (LocalEditable, Metadata23, Requirements)>);
|
||||
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<(LocalEditable, Metadata23, Requirements)>,
|
||||
) -> Self {
|
||||
let mut editables =
|
||||
FxHashMap::with_capacity_and_hasher(requirements.len(), BuildHasherDefault::default());
|
||||
for (editable_requirement, metadata, requirements) in requirements {
|
||||
editables.insert(
|
||||
metadata.name.clone(),
|
||||
(editable_requirement, metadata, requirements),
|
||||
);
|
||||
}
|
||||
Self(editables)
|
||||
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<&(LocalEditable, Metadata23, Requirements)> {
|
||||
pub(crate) fn get(&self, name: &PackageName) -> Option<&BuiltEditableMetadata> {
|
||||
self.0.get(name)
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +38,7 @@ impl Editables {
|
|||
}
|
||||
|
||||
/// Iterate over all editables.
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &(LocalEditable, Metadata23, Requirements)> {
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &BuiltEditableMetadata> {
|
||||
self.0.values()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pub use dependency_mode::DependencyMode;
|
||||
pub use editables::BuiltEditableMetadata;
|
||||
pub use error::ResolveError;
|
||||
pub use exclude_newer::ExcludeNewer;
|
||||
pub use exclusions::Exclusions;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use distribution_types::{LocalEditable, Requirement, Requirements};
|
||||
use either::Either;
|
||||
|
||||
use distribution_types::Requirement;
|
||||
use pep508_rs::MarkerEnvironment;
|
||||
use pypi_types::Metadata23;
|
||||
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.
|
||||
|
|
@ -34,7 +35,7 @@ pub struct Manifest {
|
|||
///
|
||||
/// The requirements of the editables should be included in resolution as if they were
|
||||
/// direct requirements in their own right.
|
||||
pub(crate) editables: Vec<(LocalEditable, Metadata23, Requirements)>,
|
||||
pub(crate) editables: Vec<BuiltEditableMetadata>,
|
||||
|
||||
/// The installed packages to exclude from consideration during resolution.
|
||||
///
|
||||
|
|
@ -58,7 +59,7 @@ impl Manifest {
|
|||
overrides: Overrides,
|
||||
preferences: Vec<Preference>,
|
||||
project: Option<PackageName>,
|
||||
editables: Vec<(LocalEditable, Metadata23, Requirements)>,
|
||||
editables: Vec<BuiltEditableMetadata>,
|
||||
exclusions: Exclusions,
|
||||
lookaheads: Vec<RequestedRequirements>,
|
||||
) -> Self {
|
||||
|
|
@ -112,11 +113,11 @@ impl Manifest {
|
|||
requirement.evaluate_markers(markers, lookahead.extras())
|
||||
})
|
||||
})
|
||||
.chain(self.editables.iter().flat_map(move |(editable, _metadata, requirements)| {
|
||||
.chain(self.editables.iter().flat_map(move |editable| {
|
||||
self.overrides
|
||||
.apply(&requirements.dependencies)
|
||||
.apply(&editable.requirements.dependencies)
|
||||
.filter(move |requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.extras)
|
||||
requirement.evaluate_markers(markers, &editable.built.extras)
|
||||
})
|
||||
}))
|
||||
.chain(
|
||||
|
|
@ -174,15 +175,13 @@ impl Manifest {
|
|||
requirement.evaluate_markers(markers, lookahead.extras())
|
||||
})
|
||||
})
|
||||
.chain(self.editables.iter().flat_map(
|
||||
move |(editable, _metadata, uv_requirements)| {
|
||||
self.overrides.apply(&uv_requirements.dependencies).filter(
|
||||
move |requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.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)
|
||||
|
|
|
|||
|
|
@ -137,8 +137,8 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let node = if let Some((editable, _, _)) = self.resolution.editables.get(name) {
|
||||
Node::Editable(editable)
|
||||
let node = if let Some(editable) = self.resolution.editables.get(name) {
|
||||
Node::Editable(&editable.built)
|
||||
} else {
|
||||
Node::Distribution(dist)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -93,14 +93,15 @@ impl ResolutionGraph {
|
|||
}
|
||||
}
|
||||
PubGrubPackage::Package(package_name, Some(extra), Some(url)) => {
|
||||
if let Some((editable, metadata, _)) = editables.get(package_name) {
|
||||
if metadata.provides_extras.contains(extra) {
|
||||
if let Some(editable) = editables.get(package_name) {
|
||||
if editable.metadata.provides_extras.contains(extra) {
|
||||
extras
|
||||
.entry(package_name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(extra.clone());
|
||||
} else {
|
||||
let dist = Dist::from_editable(package_name.clone(), editable.clone())?;
|
||||
let dist =
|
||||
Dist::from_editable(package_name.clone(), editable.built.clone())?;
|
||||
|
||||
diagnostics.push(Diagnostic::MissingExtra {
|
||||
dist: dist.into(),
|
||||
|
|
@ -219,15 +220,16 @@ impl ResolutionGraph {
|
|||
}
|
||||
PubGrubPackage::Package(package_name, None, Some(url)) => {
|
||||
// Create the distribution.
|
||||
if let Some((editable, metadata, _)) = editables.get(package_name) {
|
||||
let dist = Dist::from_editable(package_name.clone(), editable.clone())?;
|
||||
if let Some(editable) = editables.get(package_name) {
|
||||
let dist =
|
||||
Dist::from_editable(package_name.clone(), editable.built.clone())?;
|
||||
|
||||
// Add the distribution to the graph.
|
||||
let index = petgraph.add_node(AnnotatedDist {
|
||||
dist: dist.into(),
|
||||
extras: editable.extras.clone(),
|
||||
extras: editable.built.extras.clone(),
|
||||
hashes: vec![],
|
||||
metadata: metadata.clone(),
|
||||
metadata: editable.metadata.clone(),
|
||||
});
|
||||
inverse.insert(package_name, index);
|
||||
} else {
|
||||
|
|
@ -487,7 +489,7 @@ impl ResolutionGraph {
|
|||
manifest
|
||||
.editables
|
||||
.iter()
|
||||
.flat_map(|(_, _, uv_requirements)| &uv_requirements.dependencies),
|
||||
.flat_map(|editable| &editable.requirements.dependencies),
|
||||
);
|
||||
for direct_req in manifest.apply(direct_reqs) {
|
||||
let Some(ref marker_tree) = direct_req.marker else {
|
||||
|
|
|
|||
|
|
@ -728,8 +728,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
);
|
||||
|
||||
// If the dist is an editable, return the version from the editable metadata.
|
||||
if let Some((_local, metadata, _)) = self.editables.get(package_name) {
|
||||
let version = &metadata.version;
|
||||
if let Some(editable) = self.editables.get(package_name) {
|
||||
let version = &editable.metadata.version;
|
||||
|
||||
// The version is incompatible with the requirement.
|
||||
if !range.contains(version) {
|
||||
|
|
@ -737,7 +737,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
}
|
||||
|
||||
// The version is incompatible due to its Python requirement.
|
||||
if let Some(requires_python) = metadata.requires_python.as_ref() {
|
||||
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(
|
||||
|
|
@ -947,10 +947,13 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
}
|
||||
|
||||
// Add a dependency on each editable.
|
||||
for (editable, metadata, _) in self.editables.iter() {
|
||||
let package =
|
||||
PubGrubPackage::from_package(metadata.name.clone(), None, &self.urls);
|
||||
let version = Range::singleton(metadata.version.clone());
|
||||
for editable in self.editables.iter() {
|
||||
let package = PubGrubPackage::from_package(
|
||||
editable.metadata.name.clone(),
|
||||
None,
|
||||
&self.urls,
|
||||
);
|
||||
let version = Range::singleton(editable.metadata.version.clone());
|
||||
|
||||
// Update the package priorities.
|
||||
priorities.insert(&package, &version);
|
||||
|
|
@ -959,19 +962,24 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
dependencies.push(package, version);
|
||||
|
||||
// Add a dependency on each extra.
|
||||
for extra in &editable.extras {
|
||||
for extra in &editable.built.extras {
|
||||
dependencies.push(
|
||||
PubGrubPackage::from_package(
|
||||
metadata.name.clone(),
|
||||
editable.metadata.name.clone(),
|
||||
Some(extra.clone()),
|
||||
&self.urls,
|
||||
),
|
||||
Range::singleton(metadata.version.clone()),
|
||||
Range::singleton(editable.metadata.version.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
// Add any constraints.
|
||||
for constraint in self.constraints.get(&metadata.name).into_iter().flatten() {
|
||||
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(
|
||||
|
|
@ -1013,8 +1021,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
}
|
||||
|
||||
// Determine if the distribution is editable.
|
||||
if let Some((_local, metadata, _)) = self.editables.get(package_name) {
|
||||
let requirements: Vec<_> = metadata
|
||||
if let Some(editable) = self.editables.get(package_name) {
|
||||
let requirements: Vec<_> = editable
|
||||
.metadata
|
||||
.requires_dist
|
||||
.iter()
|
||||
.cloned()
|
||||
|
|
|
|||
|
|
@ -25,16 +25,18 @@ impl Urls {
|
|||
let mut urls: FxHashMap<PackageName, VerbatimParsedUrl> = FxHashMap::default();
|
||||
|
||||
// Add the editables themselves to the list of required URLs.
|
||||
for (editable, metadata, _) in &manifest.editables {
|
||||
for editable in &manifest.editables {
|
||||
let editable_url = VerbatimParsedUrl {
|
||||
parsed_url: ParsedUrl::Path(ParsedPathUrl {
|
||||
url: editable.url.to_url(),
|
||||
path: editable.path.clone(),
|
||||
url: editable.built.url.to_url(),
|
||||
path: editable.built.path.clone(),
|
||||
editable: true,
|
||||
}),
|
||||
verbatim: editable.url.clone(),
|
||||
verbatim: editable.built.url.clone(),
|
||||
};
|
||||
if let Some(previous) = urls.insert(metadata.name.clone(), editable_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!(
|
||||
|
|
@ -43,7 +45,7 @@ impl Urls {
|
|||
);
|
||||
} else {
|
||||
return Err(ResolveError::ConflictingUrlsDirect(
|
||||
metadata.name.clone(),
|
||||
editable.metadata.name.clone(),
|
||||
previous.verbatim.verbatim().to_string(),
|
||||
editable_url.verbatim.verbatim().to_string(),
|
||||
));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue