Make Directory its own distribution kind (#3519)

## Summary

I think this is overall good change because it explicitly encodes (in
the type system) something that was previously implicit. I'm not a huge
fan of the names here, open to input.

It covers some of https://github.com/astral-sh/uv/issues/3506 but I
don't think it _closes_ it.
This commit is contained in:
Charlie Marsh 2024-05-13 10:03:14 -04:00 committed by GitHub
parent 6bbfe555be
commit 42c3bfa351
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 434 additions and 163 deletions

View file

@ -6,7 +6,7 @@ use url::Url;
use uv_normalize::PackageName;
use crate::{GitSourceDist, Name, PathSourceDist, SourceDist};
use crate::{DirectorySourceDist, GitSourceDist, Name, PathSourceDist, SourceDist};
/// A reference to a source that can be built into a built distribution.
///
@ -62,6 +62,7 @@ pub enum SourceUrl<'a> {
Direct(DirectSourceUrl<'a>),
Git(GitSourceUrl<'a>),
Path(PathSourceUrl<'a>),
Directory(DirectorySourceUrl<'a>),
}
impl<'a> SourceUrl<'a> {
@ -71,6 +72,7 @@ impl<'a> SourceUrl<'a> {
Self::Direct(dist) => dist.url,
Self::Git(dist) => dist.url,
Self::Path(dist) => dist.url,
Self::Directory(dist) => dist.url,
}
}
}
@ -81,6 +83,7 @@ impl std::fmt::Display for SourceUrl<'_> {
Self::Direct(url) => write!(f, "{url}"),
Self::Git(url) => write!(f, "{url}"),
Self::Path(url) => write!(f, "{url}"),
Self::Directory(url) => write!(f, "{url}"),
}
}
}
@ -133,3 +136,24 @@ impl<'a> From<&'a PathSourceDist> for PathSourceUrl<'a> {
}
}
}
#[derive(Debug, Clone)]
pub struct DirectorySourceUrl<'a> {
pub url: &'a Url,
pub path: Cow<'a, Path>,
}
impl std::fmt::Display for DirectorySourceUrl<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{url}", url = self.url)
}
}
impl<'a> From<&'a DirectorySourceDist> for DirectorySourceUrl<'a> {
fn from(dist: &'a DirectorySourceDist) -> Self {
Self {
url: &dist.url,
path: Cow::Borrowed(&dist.path),
}
}
}

View file

@ -85,6 +85,13 @@ impl CachedDist {
editable: false,
}),
Dist::Source(SourceDist::Path(dist)) => Self::Url(CachedDirectUrlDist {
filename,
url: dist.url,
hashes,
path,
editable: false,
}),
Dist::Source(SourceDist::Directory(dist)) => Self::Url(CachedDirectUrlDist {
filename,
url: dist.url,
hashes,

View file

@ -152,6 +152,7 @@ pub enum SourceDist {
DirectUrl(DirectUrlSourceDist),
Git(GitSourceDist),
Path(PathSourceDist),
Directory(DirectorySourceDist),
}
/// A built distribution (wheel) that exists in a registry, like `PyPI`.
@ -203,12 +204,20 @@ pub struct GitSourceDist {
pub url: VerbatimUrl,
}
/// A source distribution that exists in a local directory.
/// A source distribution that exists in a local archive (e.g., a `.tar.gz` file).
#[derive(Debug, Clone)]
pub struct PathSourceDist {
pub name: PackageName,
pub url: VerbatimUrl,
pub path: PathBuf,
}
/// A source distribution that exists in a local directory.
#[derive(Debug, Clone)]
pub struct DirectorySourceDist {
pub name: PackageName,
pub url: VerbatimUrl,
pub path: PathBuf,
pub editable: bool,
}
@ -281,7 +290,15 @@ impl Dist {
Err(err) => return Err(err.into()),
};
if path
// Determine whether the path represents an archive or a directory.
if path.is_dir() {
Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
name,
url,
path,
editable,
})))
} else if path
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("whl"))
{
@ -305,11 +322,14 @@ impl Dist {
path,
})))
} else {
if editable {
return Err(Error::EditableFile(url));
}
Ok(Self::Source(SourceDist::Path(PathSourceDist {
name,
url,
path,
editable,
})))
}
}
@ -382,7 +402,7 @@ impl Dist {
/// Create a [`Dist`] for a local editable distribution.
pub fn from_editable(name: PackageName, editable: LocalEditable) -> Result<Self, Error> {
let LocalEditable { url, path, .. } = editable;
Ok(Self::Source(SourceDist::Path(PathSourceDist {
Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
name,
url,
path,
@ -454,7 +474,7 @@ impl SourceDist {
pub fn index(&self) -> Option<&IndexUrl> {
match self {
Self::Registry(registry) => Some(&registry.index),
Self::DirectUrl(_) | Self::Git(_) | Self::Path(_) => None,
Self::DirectUrl(_) | Self::Git(_) | Self::Path(_) | Self::Directory(_) => None,
}
}
@ -462,14 +482,14 @@ impl SourceDist {
pub fn file(&self) -> Option<&File> {
match self {
Self::Registry(registry) => Some(&registry.file),
Self::DirectUrl(_) | Self::Git(_) | Self::Path(_) => None,
Self::DirectUrl(_) | Self::Git(_) | Self::Path(_) | Self::Directory(_) => None,
}
}
pub fn version(&self) -> Option<&Version> {
match self {
Self::Registry(source_dist) => Some(&source_dist.filename.version),
Self::DirectUrl(_) | Self::Git(_) | Self::Path(_) => None,
Self::DirectUrl(_) | Self::Git(_) | Self::Path(_) | Self::Directory(_) => None,
}
}
@ -487,7 +507,7 @@ impl SourceDist {
/// Return true if the distribution is editable.
pub fn is_editable(&self) -> bool {
match self {
Self::Path(PathSourceDist { editable, .. }) => *editable,
Self::Directory(DirectorySourceDist { editable, .. }) => *editable,
_ => false,
}
}
@ -496,6 +516,7 @@ impl SourceDist {
pub fn as_path(&self) -> Option<&Path> {
match self {
Self::Path(dist) => Some(&dist.path),
Self::Directory(dist) => Some(&dist.path),
_ => None,
}
}
@ -543,6 +564,12 @@ impl Name for PathSourceDist {
}
}
impl Name for DirectorySourceDist {
fn name(&self) -> &PackageName {
&self.name
}
}
impl Name for SourceDist {
fn name(&self) -> &PackageName {
match self {
@ -550,6 +577,7 @@ impl Name for SourceDist {
Self::DirectUrl(dist) => dist.name(),
Self::Git(dist) => dist.name(),
Self::Path(dist) => dist.name(),
Self::Directory(dist) => dist.name(),
}
}
}
@ -615,6 +643,12 @@ impl DistributionMetadata for PathSourceDist {
}
}
impl DistributionMetadata for DirectorySourceDist {
fn version_or_url(&self) -> VersionOrUrlRef {
VersionOrUrlRef::Url(&self.url)
}
}
impl DistributionMetadata for SourceDist {
fn version_or_url(&self) -> VersionOrUrlRef {
match self {
@ -622,6 +656,7 @@ impl DistributionMetadata for SourceDist {
Self::DirectUrl(dist) => dist.version_or_url(),
Self::Git(dist) => dist.version_or_url(),
Self::Path(dist) => dist.version_or_url(),
Self::Directory(dist) => dist.version_or_url(),
}
}
}
@ -760,6 +795,16 @@ impl RemoteSource for PathSourceDist {
}
}
impl RemoteSource for DirectorySourceDist {
fn filename(&self) -> Result<Cow<'_, str>, Error> {
self.url.filename()
}
fn size(&self) -> Option<u64> {
self.url.size()
}
}
impl RemoteSource for SourceDist {
fn filename(&self) -> Result<Cow<'_, str>, Error> {
match self {
@ -767,6 +812,7 @@ impl RemoteSource for SourceDist {
Self::DirectUrl(dist) => dist.filename(),
Self::Git(dist) => dist.filename(),
Self::Path(dist) => dist.filename(),
Self::Directory(dist) => dist.filename(),
}
}
@ -776,6 +822,7 @@ impl RemoteSource for SourceDist {
Self::DirectUrl(dist) => dist.size(),
Self::Git(dist) => dist.size(),
Self::Path(dist) => dist.size(),
Self::Directory(dist) => dist.size(),
}
}
}
@ -934,6 +981,16 @@ impl Identifier for PathSourceDist {
}
}
impl Identifier for DirectorySourceDist {
fn distribution_id(&self) -> DistributionId {
self.url.distribution_id()
}
fn resource_id(&self) -> ResourceId {
self.url.resource_id()
}
}
impl Identifier for GitSourceDist {
fn distribution_id(&self) -> DistributionId {
self.url.distribution_id()
@ -951,6 +1008,7 @@ impl Identifier for SourceDist {
Self::DirectUrl(dist) => dist.distribution_id(),
Self::Git(dist) => dist.distribution_id(),
Self::Path(dist) => dist.distribution_id(),
Self::Directory(dist) => dist.distribution_id(),
}
}
@ -960,6 +1018,7 @@ impl Identifier for SourceDist {
Self::DirectUrl(dist) => dist.resource_id(),
Self::Git(dist) => dist.resource_id(),
Self::Path(dist) => dist.resource_id(),
Self::Directory(dist) => dist.resource_id(),
}
}
}
@ -1038,12 +1097,23 @@ impl Identifier for PathSourceUrl<'_> {
}
}
impl Identifier for DirectorySourceUrl<'_> {
fn distribution_id(&self) -> DistributionId {
self.url.distribution_id()
}
fn resource_id(&self) -> ResourceId {
self.url.resource_id()
}
}
impl Identifier for SourceUrl<'_> {
fn distribution_id(&self) -> DistributionId {
match self {
Self::Direct(url) => url.distribution_id(),
Self::Git(url) => url.distribution_id(),
Self::Path(url) => url.distribution_id(),
Self::Directory(url) => url.distribution_id(),
}
}
@ -1052,6 +1122,7 @@ impl Identifier for SourceUrl<'_> {
Self::Direct(url) => url.resource_id(),
Self::Git(url) => url.resource_id(),
Self::Path(url) => url.resource_id(),
Self::Directory(url) => url.resource_id(),
}
}
}

View file

@ -126,6 +126,11 @@ impl From<&ResolvedDist> for Requirement {
url: sdist.url.clone(),
editable: None,
},
Dist::Source(SourceDist::Directory(sdist)) => RequirementSource::Path {
path: sdist.path.clone(),
url: sdist.url.clone(),
editable: None,
},
},
ResolvedDist::Installed(dist) => RequirementSource::Registry {
specifier: pep440_rs::VersionSpecifiers::from(