Track editable requirements in lockfile (#3725)

## Summary

This PR adds editables using a new source type (`editable+...`), and
then extracts the editables from the lockfile in `uv sync`.

Closes https://github.com/astral-sh/uv/issues/3695.
This commit is contained in:
Charlie Marsh 2024-05-22 09:06:18 -04:00 committed by GitHub
parent 472ab144d5
commit e398444f2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 7 deletions

View File

@ -1,8 +1,12 @@
use rustc_hash::FxHashMap;
use pep508_rs::VerbatimUrl;
use uv_normalize::PackageName;
use crate::{BuiltDist, Dist, Name, Requirement, RequirementSource, ResolvedDist, SourceDist};
use crate::{
BuiltDist, DirectorySourceDist, Dist, InstalledDirectUrlDist, InstalledDist, LocalEditable,
Name, Requirement, RequirementSource, ResolvedDist, SourceDist,
};
/// A set of packages pinned at specific versions.
#[derive(Debug, Default, Clone)]
@ -68,6 +72,35 @@ impl Resolution {
requirements.sort_unstable_by(|a, b| a.name.cmp(&b.name));
requirements
}
/// Return an iterator over the [`LocalEditable`] entities in this resolution.
pub fn editables(&self) -> impl Iterator<Item = LocalEditable> + '_ {
self.0.values().filter_map(|dist| match dist {
ResolvedDist::Installable(Dist::Source(SourceDist::Directory(
DirectorySourceDist {
path,
url,
editable: true,
..
},
))) => Some(LocalEditable {
url: url.clone(),
path: path.clone(),
extras: vec![],
}),
ResolvedDist::Installed(InstalledDist::Url(InstalledDirectUrlDist {
path,
url,
editable: true,
..
})) => Some(LocalEditable {
url: VerbatimUrl::from_url(url.clone()),
path: path.clone(),
extras: vec![],
}),
_ => None,
})
}
}
impl From<&ResolvedDist> for Requirement {

View File

@ -3,9 +3,10 @@ use pep508_rs::PackageName;
use rustc_hash::FxHashSet;
/// Whether to reinstall packages.
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub enum Reinstall {
/// Don't reinstall any packages; respect the existing installation.
#[default]
None,
/// Reinstall all packages in the plan.

View File

@ -277,6 +277,9 @@ impl Distribution {
SourceKind::Directory => {
unreachable!("Wheels cannot come from directory sources")
}
SourceKind::Editable => {
unreachable!("Wheels cannot come from editable sources")
}
};
}
@ -301,6 +304,16 @@ impl Distribution {
let source_dist = distribution_types::SourceDist::Directory(dir_dist);
Dist::Source(source_dist)
}
SourceKind::Editable => {
let dir_dist = DirectorySourceDist {
name: self.id.name.clone(),
url: VerbatimUrl::from_url(self.id.source.url.clone()),
path: self.id.source.url.to_file_path().unwrap(),
editable: true,
};
let source_dist = distribution_types::SourceDist::Directory(dir_dist);
Dist::Source(source_dist)
}
SourceKind::Git(git) => {
// Reconstruct the `GitUrl` from the `GitSource`.
let git_url = uv_git::GitUrl::new(
@ -513,7 +526,11 @@ impl Source {
fn from_directory_source_dist(directory_dist: &DirectorySourceDist) -> Source {
Source {
kind: SourceKind::Directory,
kind: if directory_dist.editable {
SourceKind::Editable
} else {
SourceKind::Directory
},
url: directory_dist.url.to_url(),
}
}
@ -583,6 +600,10 @@ impl std::str::FromStr for Source {
kind: SourceKind::Directory,
url,
}),
"editable" => Ok(Source {
kind: SourceKind::Editable,
url,
}),
name => Err(SourceParseError::unrecognized_source_name(s, name)),
}
}
@ -624,6 +645,7 @@ pub(crate) enum SourceKind {
Direct(DirectSource),
Path,
Directory,
Editable,
}
impl SourceKind {
@ -634,6 +656,7 @@ impl SourceKind {
SourceKind::Direct(_) => "direct",
SourceKind::Path => "path",
SourceKind::Directory => "directory",
SourceKind::Editable => "editable",
}
}
@ -644,7 +667,7 @@ impl SourceKind {
fn requires_hash(&self) -> bool {
match *self {
SourceKind::Registry | SourceKind::Direct(_) | SourceKind::Path => true,
SourceKind::Git(_) | SourceKind::Directory => false,
SourceKind::Git(_) | SourceKind::Directory | SourceKind::Editable => false,
}
}
}

View File

@ -5,7 +5,7 @@ use install_wheel_rs::linker::LinkMode;
use uv_cache::Cache;
use uv_client::RegistryClientBuilder;
use uv_configuration::{
Concurrency, ConfigSettings, NoBinary, NoBuild, PreviewMode, SetupPyStrategy,
Concurrency, ConfigSettings, NoBinary, NoBuild, PreviewMode, Reinstall, SetupPyStrategy,
};
use uv_dispatch::BuildDispatch;
use uv_installer::SitePackages;
@ -65,6 +65,7 @@ pub(crate) async fn sync(
let no_build = NoBuild::default();
let setup_py = SetupPyStrategy::default();
let concurrency = Concurrency::default();
let reinstall = Reinstall::default();
// Create a build dispatch.
let build_dispatch = BuildDispatch::new(
@ -84,8 +85,23 @@ pub(crate) async fn sync(
concurrency,
);
// TODO(konsti): Read editables from lockfile.
let editables = ResolvedEditables::default();
let site_packages = SitePackages::from_executable(&venv)?;
// Build any editables.
let editables = ResolvedEditables::resolve(
resolution.editables(),
&site_packages,
&reinstall,
&hasher,
venv.interpreter(),
tags,
cache,
&client,
&build_dispatch,
concurrency,
printer,
)
.await?;
let site_packages = SitePackages::from_executable(&venv)?;