Skip to content

Commit

Permalink
Support editable in pip-sync and pip-compile (#587)
Browse files Browse the repository at this point in the history
Support `-e path/do/dir` in pip-sync and and pip-compile.
  • Loading branch information
konstin authored Dec 16, 2023
1 parent f62458f commit f059c6e
Show file tree
Hide file tree
Showing 47 changed files with 1,013 additions and 247 deletions.
15 changes: 12 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
.venv

# Generated by Cargo
# will have compiled files and executables
debug/
Expand All @@ -14,5 +12,16 @@ target/
# Use e.g. `--cache-dir cache-docker` to keep a cache across container invocations
cache-*

# python tmp files
# Python tmp files
__pycache__

# Maturin builds, and other native editable builds
*.so
*.pyd
*.dll

# Profiling
flamegraph.svg
perf.data
perf.data.old
profile.json
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/distribution-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ puffin-cache = { path = "../puffin-cache" }
puffin-git = { path = "../puffin-git" }
puffin-normalize = { path = "../puffin-normalize" }
pypi-types = { path = "../pypi-types" }
requirements-txt = { path = "../requirements-txt" }

anyhow = { workspace = true }
fs-err = { workspace = true }
Expand Down
29 changes: 27 additions & 2 deletions crates/distribution-types/src/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use distribution_filename::WheelFilename;
use pep508_rs::VerbatimUrl;
use puffin_normalize::PackageName;

use crate::direct_url::DirectUrl;
use crate::direct_url::{DirectUrl, LocalFileUrl};
use crate::traits::Metadata;
use crate::{BuiltDist, Dist, SourceDist, VersionOrUrl};

Expand All @@ -30,6 +30,7 @@ pub struct CachedDirectUrlDist {
pub filename: WheelFilename,
pub url: VerbatimUrl,
pub path: PathBuf,
pub editable: bool,
}

impl Metadata for CachedRegistryDist {
Expand Down Expand Up @@ -79,11 +80,13 @@ impl CachedDist {
filename,
url: dist.url,
path,
editable: false,
}),
Dist::Built(BuiltDist::Path(dist)) => Self::Url(CachedDirectUrlDist {
filename,
url: dist.url,
path,
editable: false,
}),
Dist::Source(SourceDist::Registry(_dist)) => {
Self::Registry(CachedRegistryDist { filename, path })
Expand All @@ -92,16 +95,19 @@ impl CachedDist {
filename,
url: dist.url,
path,
editable: false,
}),
Dist::Source(SourceDist::Git(dist)) => Self::Url(CachedDirectUrlDist {
filename,
url: dist.url,
path,
editable: false,
}),
Dist::Source(SourceDist::Path(dist)) => Self::Url(CachedDirectUrlDist {
filename,
url: dist.url,
path,
editable: dist.editable,
}),
}
}
Expand All @@ -118,7 +124,24 @@ impl CachedDist {
pub fn direct_url(&self) -> Result<Option<DirectUrl>> {
match self {
CachedDist::Registry(_) => Ok(None),
CachedDist::Url(dist) => DirectUrl::try_from(dist.url.raw()).map(Some),
CachedDist::Url(dist) => {
if dist.editable {
assert_eq!(dist.url.scheme(), "file", "{}", dist.url);
Ok(Some(DirectUrl::LocalFile(LocalFileUrl {
url: dist.url.raw().clone(),
editable: dist.editable,
})))
} else {
DirectUrl::try_from(dist.url.raw()).map(Some)
}
}
}
}

pub fn editable(&self) -> bool {
match self {
CachedDist::Registry(_) => false,
CachedDist::Url(dist) => dist.editable,
}
}
}
Expand All @@ -130,6 +153,7 @@ impl CachedDirectUrlDist {
filename,
url,
path,
editable: false,
}
}
}
Expand Down Expand Up @@ -177,6 +201,7 @@ impl CachedWheel {
filename: self.filename,
url,
path: self.path,
editable: false,
}
}
}
26 changes: 15 additions & 11 deletions crates/distribution-types/src/direct_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,21 @@ pub enum DirectUrl {
Archive(DirectArchiveUrl),
}

/// A git repository url
/// A local path url
///
/// Examples:
/// * `git+https://git.example.com/MyProject.git`
/// * `git+https://git.example.com/MyProject.git@v1.0#egg=pkg&subdirectory=pkg_dir`
/// * `file:///home/ferris/my_project`
#[derive(Debug)]
pub struct LocalFileUrl {
pub url: Url,
pub editable: bool,
}

/// A git repository url
///
/// Examples:
/// * `git+https://git.example.com/MyProject.git`
/// * `git+https://git.example.com/MyProject.git@v1.0#egg=pkg&subdirectory=pkg_dir`
#[derive(Debug)]
pub struct DirectGitUrl {
pub url: GitUrl,
Expand All @@ -43,12 +48,6 @@ pub struct DirectArchiveUrl {
pub subdirectory: Option<PathBuf>,
}

impl From<&Url> for LocalFileUrl {
fn from(url: &Url) -> Self {
Self { url: url.clone() }
}
}

impl TryFrom<&Url> for DirectGitUrl {
type Error = Error;

Expand Down Expand Up @@ -106,7 +105,10 @@ impl TryFrom<&Url> for DirectUrl {
))),
}
} else if url.scheme().eq_ignore_ascii_case("file") {
Ok(Self::LocalFile(LocalFileUrl::from(url)))
Ok(Self::LocalFile(LocalFileUrl {
url: url.clone(),
editable: false,
}))
} else {
Ok(Self::Archive(DirectArchiveUrl::from(url)))
}
Expand All @@ -131,7 +133,9 @@ impl TryFrom<&LocalFileUrl> for pypi_types::DirectUrl {
fn try_from(value: &LocalFileUrl) -> Result<Self, Self::Error> {
Ok(pypi_types::DirectUrl::LocalDirectory {
url: value.url.clone(),
dir_info: pypi_types::DirInfo { editable: None },
dir_info: pypi_types::DirInfo {
editable: value.editable.then_some(true),
},
})
}
}
Expand Down
33 changes: 33 additions & 0 deletions crates/distribution-types/src/editable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use url::Url;

use pep508_rs::VerbatimUrl;
use requirements_txt::EditableRequirement;

#[derive(Debug, Clone)]
pub struct LocalEditable {
pub requirement: EditableRequirement,
/// Either the path to the editable or its checkout
pub path: PathBuf,
}

impl LocalEditable {
pub fn url(&self) -> &VerbatimUrl {
self.requirement.url()
}

pub fn raw(&self) -> &Url {
self.requirement.raw()
}

pub fn path(&self) -> &Path {
&self.path
}
}

impl Display for LocalEditable {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.requirement.fmt(f)
}
}
4 changes: 4 additions & 0 deletions crates/distribution-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ use pypi_types::{File, IndexUrl};

pub use crate::any::*;
pub use crate::cached::*;
pub use crate::editable::LocalEditable;
pub use crate::error::*;
pub use crate::id::*;
pub use crate::installed::*;
Expand All @@ -59,6 +60,7 @@ pub use crate::traits::*;
mod any;
mod cached;
pub mod direct_url;
mod editable;
mod error;
mod id;
mod installed;
Expand Down Expand Up @@ -167,6 +169,7 @@ pub struct PathSourceDist {
pub name: PackageName,
pub url: VerbatimUrl,
pub path: PathBuf,
pub editable: bool,
}

impl Dist {
Expand Down Expand Up @@ -219,6 +222,7 @@ impl Dist {
name,
url,
path,
editable: false,
})))
};
}
Expand Down
17 changes: 10 additions & 7 deletions crates/install-wheel-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub enum Error {
/// the metadata name and the dist info name are lowercase, while the wheel name is uppercase.
/// Either way, we just search the wheel for the name.
///
/// Returns the dist info dir prefix without the `.dist-info` extension.
///
/// Reference implementation: <https://github.com/pypa/packaging/blob/2f83540272e79e3fe1f5d42abae8df0c14ddf4c2/src/packaging/utils.py#L146-L172>
pub fn find_dist_info<'a, T: Copy>(
filename: &WheelFilename,
Expand All @@ -106,13 +108,13 @@ pub fn find_dist_info<'a, T: Copy>(
&& Version::from_str(version).ok()? == filename.version
&& file == "METADATA"
{
Some((payload, dist_info_dir))
Some((payload, dir_stem))
} else {
None
}
})
.collect();
let (payload, dist_info_dir) = match metadatas[..] {
let (payload, dist_info_prefix) = match metadatas[..] {
[] => {
return Err(Error::MissingDistInfo);
}
Expand All @@ -127,18 +129,19 @@ pub fn find_dist_info<'a, T: Copy>(
));
}
};
Ok((payload, dist_info_dir))
Ok((payload, dist_info_prefix))
}

/// Given an archive, read the `dist-info` metadata into a buffer.
pub fn read_dist_info(
filename: &WheelFilename,
archive: &mut ZipArchive<impl Read + Seek + Sized>,
) -> Result<Vec<u8>, Error> {
let dist_info_dir = find_dist_info(filename, archive.file_names().map(|name| (name, name)))?.1;
let dist_info_prefix =
find_dist_info(filename, archive.file_names().map(|name| (name, name)))?.1;

let mut file = archive
.by_name(&format!("{dist_info_dir}/METADATA"))
.by_name(&format!("{dist_info_prefix}.dist-info/METADATA"))
.map_err(|err| Error::Zip(filename.to_string(), err))?;

#[allow(clippy::cast_possible_truncation)]
Expand Down Expand Up @@ -170,8 +173,8 @@ mod test {
"Mastodon.py-1.5.1.dist-info/RECORD",
];
let filename = WheelFilename::from_str("Mastodon.py-1.5.1-py2.py3-none-any.whl").unwrap();
let (_, dist_info_dir) =
let (_, dist_info_prefix) =
find_dist_info(&filename, files.into_iter().map(|file| (file, file))).unwrap();
assert_eq!(dist_info_dir, "Mastodon.py-1.5.1.dist-info");
assert_eq!(dist_info_prefix, "Mastodon.py-1.5.1");
}
}
6 changes: 3 additions & 3 deletions crates/install-wheel-rs/src/wheel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ pub(crate) fn read_scripts_from_section(
/// Extras are supposed to be ignored, which happens if you pass None for extras
fn parse_scripts<R: Read + Seek>(
archive: &mut ZipArchive<R>,
dist_info_prefix: &str,
dist_info_dir: &str,
extras: Option<&[String]>,
) -> Result<(Vec<Script>, Vec<Script>), Error> {
let entry_points_path = format!("{dist_info_prefix}.dist-info/entry_points.txt");
let entry_points_path = format!("{dist_info_dir}/entry_points.txt");
let entry_points_mapping = match archive.by_name(&entry_points_path) {
Ok(mut file) => {
let mut ini_text = String::new();
Expand Down Expand Up @@ -1043,7 +1043,7 @@ fn dist_info_metadata(
archive: &mut ZipArchive<impl Read + Seek + Sized>,
) -> Result<Vec<u8>, Error> {
let mut content = Vec::new();
let dist_info_file = format!("{dist_info_prefix}.dist-info/DIST-INFO");
let dist_info_file = format!("{dist_info_prefix}.dist-info/METADATA");
archive
.by_name(&dist_info_file)
.map_err(|err| Error::Zip(dist_info_file.clone(), err))?
Expand Down
Loading

0 comments on commit f059c6e

Please sign in to comment.