Skip to content

Commit

Permalink
Show a dedicated error for missing subdirectories
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Dec 10, 2024
1 parent 25045cb commit 1a10d7d
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 6 deletions.
2 changes: 2 additions & 0 deletions crates/uv-distribution/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub enum Error {
MissingEggInfo,
#[error("The source distribution is missing a `requires.txt` file")]
MissingRequiresTxt,
#[error("The source distribution `{}` has no subdirectory `{}`", _0, _1.display())]
MissingSubdirectory(Url, PathBuf),
#[error("Failed to extract static metadata from `PKG-INFO`")]
PkgInfo(#[source] uv_pypi_types::MetadataError),
#[error("Failed to extract metadata from `requires.txt`")]
Expand Down
46 changes: 40 additions & 6 deletions crates/uv-distribution/src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
let cache_shard = cache_shard.shard(revision.id());
let source_dist_entry = cache_shard.entry(SOURCE);

// Validate that the subdirectory exists.
if let Some(subdirectory) = subdirectory {
if !source_dist_entry.path().join(subdirectory).is_dir() {
return Err(Error::MissingSubdirectory(
url.clone(),
subdirectory.to_path_buf(),
));
}
}

// If there are build settings, we need to scope to a cache shard.
let config_settings = self.build_context.config_settings();
let cache_shard = if config_settings.is_empty() {
Expand Down Expand Up @@ -496,6 +506,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
let cache_shard = cache_shard.shard(revision.id());
let source_dist_entry = cache_shard.entry(SOURCE);

// Validate that the subdirectory exists.
if let Some(subdirectory) = subdirectory {
if !source_dist_entry.path().join(subdirectory).is_dir() {
return Err(Error::MissingSubdirectory(
url.clone(),
subdirectory.to_path_buf(),
));
}
}

// If the metadata is static, return it.
if let Some(metadata) =
Self::read_static_metadata(source, source_dist_entry.path(), subdirectory).await?
Expand Down Expand Up @@ -1303,6 +1323,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
)
.await?;

// Validate that the subdirectory exists.
if let Some(subdirectory) = resource.subdirectory {
if !fetch.path().join(subdirectory).is_dir() {
return Err(Error::MissingSubdirectory(
resource.url.to_url(),
subdirectory.to_path_buf(),
));
}
}

let git_sha = fetch.git().precise().expect("Exact commit after checkout");
let cache_shard = self.build_context.cache().shard(
CacheBucket::SourceDistributions,
Expand Down Expand Up @@ -1390,6 +1420,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
)
.await?;

// Validate that the subdirectory exists.
if let Some(subdirectory) = resource.subdirectory {
if !fetch.path().join(subdirectory).is_dir() {
return Err(Error::MissingSubdirectory(
resource.url.to_url(),
subdirectory.to_path_buf(),
));
}
}

let git_sha = fetch.git().precise().expect("Exact commit after checkout");
let cache_shard = self.build_context.cache().shard(
CacheBucket::SourceDistributions,
Expand Down Expand Up @@ -1438,12 +1478,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.await?
.filter(|metadata| metadata.matches(source.name(), source.version()))
{
let path = if let Some(subdirectory) = resource.subdirectory {
Cow::Owned(fetch.path().join(subdirectory))
} else {
Cow::Borrowed(fetch.path())
};

let git_member = GitWorkspaceMember {
fetch_root: fetch.path(),
git_source: resource,
Expand Down
42 changes: 42 additions & 0 deletions crates/uv/tests/it/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7589,3 +7589,45 @@ fn build_tag() {
----- stderr -----
"###);
}

#[test]
fn missing_subdirectory_git() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.touch()?;

uv_snapshot!(context.pip_install()
.arg("source-distribution @ git+https://github.com/astral-sh/workspace-in-root-test#subdirectory=missing"), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× Failed to download and build `source-distribution @ git+https://github.com/astral-sh/workspace-in-root-test#subdirectory=missing`
╰─▶ The source distribution `git+https://github.com/astral-sh/workspace-in-root-test#subdirectory=missing` has no subdirectory `missing`
"###
);

Ok(())
}

#[test]
fn missing_subdirectory_url() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.touch()?;

uv_snapshot!(context.pip_install()
.arg("source-distribution @ https://files.pythonhosted.org/packages/1f/e5/5b016c945d745f8b108e759d428341488a6aee8f51f07c6c4e33498bb91f/source_distribution-0.0.3.tar.gz#subdirectory=missing"), @r###"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/1f/e5/5b016c945d745f8b108e759d428341488a6aee8f51f07c6c4e33498bb91f/source_distribution-0.0.3.tar.gz#subdirectory=missing`
╰─▶ The source distribution `https://files.pythonhosted.org/packages/1f/e5/5b016c945d745f8b108e759d428341488a6aee8f51f07c6c4e33498bb91f/source_distribution-0.0.3.tar.gz#subdirectory=missing` has no subdirectory `missing`
"###
);

Ok(())
}

0 comments on commit 1a10d7d

Please sign in to comment.