Skip to content

Commit 1a10d7d

Browse files
committed
Show a dedicated error for missing subdirectories
1 parent 25045cb commit 1a10d7d

3 files changed

Lines changed: 84 additions & 6 deletions

File tree

crates/uv-distribution/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ pub enum Error {
9292
MissingEggInfo,
9393
#[error("The source distribution is missing a `requires.txt` file")]
9494
MissingRequiresTxt,
95+
#[error("The source distribution `{}` has no subdirectory `{}`", _0, _1.display())]
96+
MissingSubdirectory(Url, PathBuf),
9597
#[error("Failed to extract static metadata from `PKG-INFO`")]
9698
PkgInfo(#[source] uv_pypi_types::MetadataError),
9799
#[error("Failed to extract metadata from `requires.txt`")]

crates/uv-distribution/src/source/mod.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
393393
let cache_shard = cache_shard.shard(revision.id());
394394
let source_dist_entry = cache_shard.entry(SOURCE);
395395

396+
// Validate that the subdirectory exists.
397+
if let Some(subdirectory) = subdirectory {
398+
if !source_dist_entry.path().join(subdirectory).is_dir() {
399+
return Err(Error::MissingSubdirectory(
400+
url.clone(),
401+
subdirectory.to_path_buf(),
402+
));
403+
}
404+
}
405+
396406
// If there are build settings, we need to scope to a cache shard.
397407
let config_settings = self.build_context.config_settings();
398408
let cache_shard = if config_settings.is_empty() {
@@ -496,6 +506,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
496506
let cache_shard = cache_shard.shard(revision.id());
497507
let source_dist_entry = cache_shard.entry(SOURCE);
498508

509+
// Validate that the subdirectory exists.
510+
if let Some(subdirectory) = subdirectory {
511+
if !source_dist_entry.path().join(subdirectory).is_dir() {
512+
return Err(Error::MissingSubdirectory(
513+
url.clone(),
514+
subdirectory.to_path_buf(),
515+
));
516+
}
517+
}
518+
499519
// If the metadata is static, return it.
500520
if let Some(metadata) =
501521
Self::read_static_metadata(source, source_dist_entry.path(), subdirectory).await?
@@ -1303,6 +1323,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
13031323
)
13041324
.await?;
13051325

1326+
// Validate that the subdirectory exists.
1327+
if let Some(subdirectory) = resource.subdirectory {
1328+
if !fetch.path().join(subdirectory).is_dir() {
1329+
return Err(Error::MissingSubdirectory(
1330+
resource.url.to_url(),
1331+
subdirectory.to_path_buf(),
1332+
));
1333+
}
1334+
}
1335+
13061336
let git_sha = fetch.git().precise().expect("Exact commit after checkout");
13071337
let cache_shard = self.build_context.cache().shard(
13081338
CacheBucket::SourceDistributions,
@@ -1390,6 +1420,16 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
13901420
)
13911421
.await?;
13921422

1423+
// Validate that the subdirectory exists.
1424+
if let Some(subdirectory) = resource.subdirectory {
1425+
if !fetch.path().join(subdirectory).is_dir() {
1426+
return Err(Error::MissingSubdirectory(
1427+
resource.url.to_url(),
1428+
subdirectory.to_path_buf(),
1429+
));
1430+
}
1431+
}
1432+
13931433
let git_sha = fetch.git().precise().expect("Exact commit after checkout");
13941434
let cache_shard = self.build_context.cache().shard(
13951435
CacheBucket::SourceDistributions,
@@ -1438,12 +1478,6 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
14381478
.await?
14391479
.filter(|metadata| metadata.matches(source.name(), source.version()))
14401480
{
1441-
let path = if let Some(subdirectory) = resource.subdirectory {
1442-
Cow::Owned(fetch.path().join(subdirectory))
1443-
} else {
1444-
Cow::Borrowed(fetch.path())
1445-
};
1446-
14471481
let git_member = GitWorkspaceMember {
14481482
fetch_root: fetch.path(),
14491483
git_source: resource,

crates/uv/tests/it/pip_install.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7589,3 +7589,45 @@ fn build_tag() {
75897589
----- stderr -----
75907590
"###);
75917591
}
7592+
7593+
#[test]
7594+
fn missing_subdirectory_git() -> Result<()> {
7595+
let context = TestContext::new("3.12");
7596+
let requirements_txt = context.temp_dir.child("requirements.txt");
7597+
requirements_txt.touch()?;
7598+
7599+
uv_snapshot!(context.pip_install()
7600+
.arg("source-distribution @ git+https://github.com/astral-sh/workspace-in-root-test#subdirectory=missing"), @r###"
7601+
success: false
7602+
exit_code: 1
7603+
----- stdout -----
7604+
7605+
----- stderr -----
7606+
× Failed to download and build `source-distribution @ git+https://github.com/astral-sh/workspace-in-root-test#subdirectory=missing`
7607+
╰─▶ The source distribution `git+https://github.com/astral-sh/workspace-in-root-test#subdirectory=missing` has no subdirectory `missing`
7608+
"###
7609+
);
7610+
7611+
Ok(())
7612+
}
7613+
7614+
#[test]
7615+
fn missing_subdirectory_url() -> Result<()> {
7616+
let context = TestContext::new("3.12");
7617+
let requirements_txt = context.temp_dir.child("requirements.txt");
7618+
requirements_txt.touch()?;
7619+
7620+
uv_snapshot!(context.pip_install()
7621+
.arg("source-distribution @ https://files.pythonhosted.org/packages/1f/e5/5b016c945d745f8b108e759d428341488a6aee8f51f07c6c4e33498bb91f/source_distribution-0.0.3.tar.gz#subdirectory=missing"), @r###"
7622+
success: false
7623+
exit_code: 1
7624+
----- stdout -----
7625+
7626+
----- stderr -----
7627+
× Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/1f/e5/5b016c945d745f8b108e759d428341488a6aee8f51f07c6c4e33498bb91f/source_distribution-0.0.3.tar.gz#subdirectory=missing`
7628+
╰─▶ The source distribution `https://files.pythonhosted.org/packages/1f/e5/5b016c945d745f8b108e759d428341488a6aee8f51f07c6c4e33498bb91f/source_distribution-0.0.3.tar.gz#subdirectory=missing` has no subdirectory `missing`
7629+
"###
7630+
);
7631+
7632+
Ok(())
7633+
}

0 commit comments

Comments
 (0)