Respect path dependencies within Git dependencies (#9594)

## Summary

If a Git repository uses a `path` dependency (rather than a
`workspace`), we need to expand the path to make it relative to the Git
root.

Closes https://github.com/astral-sh/uv/issues/9516.
This commit is contained in:
Charlie Marsh 2024-12-03 00:13:30 -05:00 committed by GitHub
parent 16ca0c34a1
commit 90d8105117
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 116 additions and 0 deletions

View File

@ -220,6 +220,7 @@ impl LoweredRequirement {
}
let source = path_source(
PathBuf::from(path),
git_member,
origin,
project_dir,
workspace.install_path(),
@ -465,6 +466,7 @@ impl LoweredRequirement {
}
let source = path_source(
PathBuf::from(path),
None,
RequirementOrigin::Project,
dir,
dir,
@ -553,6 +555,8 @@ pub enum LoweringError {
WorkspaceFalse,
#[error("Editable must refer to a local directory, not a file: `{0}`")]
EditableFile(String),
#[error("Git repository references local file source, but only directories are supported as transitive Git dependencies: `{0}`")]
GitFile(String),
#[error(transparent)]
ParsedUrl(#[from] ParsedUrlError),
#[error("Path must be UTF-8: `{0}`")]
@ -678,6 +682,7 @@ fn registry_source(
/// Convert a path string to a file or directory source.
fn path_source(
path: impl AsRef<Path>,
git_member: Option<&GitWorkspaceMember>,
origin: RequirementOrigin,
project_dir: &Path,
workspace_root: &Path,
@ -702,6 +707,24 @@ fn path_source(
install_path.extension().is_none()
};
if is_dir {
if let Some(git_member) = git_member {
let subdirectory = uv_fs::normalize_path(
&uv_fs::relative_to(install_path, git_member.fetch_root)
.expect("Workspace member must be relative"),
);
return Ok(RequirementSource::Git {
repository: git_member.git_source.git.repository().clone(),
reference: git_member.git_source.git.reference().clone(),
precise: git_member.git_source.git.precise(),
subdirectory: if subdirectory == PathBuf::new() {
None
} else {
Some(subdirectory)
},
url,
});
}
if editable {
Ok(RequirementSource::Directory {
install_path,
@ -731,6 +754,10 @@ fn path_source(
})
}
} else {
// TODO(charlie): If a Git repo contains a source that points to a file, what should we do?
if git_member.is_some() {
return Err(LoweringError::GitFile(url.to_string()));
}
if editable {
return Err(LoweringError::EditableFile(url.to_string()));
}

View File

@ -5409,3 +5409,92 @@ fn mismatched_name_cached_wheel() -> Result<()> {
Ok(())
}
/// Sync a Git repository that depends on a package within the same repository via a `path` source.
///
/// See: <https://github.com/astral-sh/uv/issues/9516>
#[test]
fn sync_git_path_dependency() -> Result<()> {
let context = TestContext::new("3.13");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "foo"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = ["package2"]
[tool.uv.sources]
package2 = { git = "https://git@github.com/astral-sh/uv-path-dependency-test.git", subdirectory = "package2" }
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 3 packages in [TIME]
"###);
let lock = context.read("uv.lock");
insta::with_settings!(
{
filters => context.filters(),
},
{
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.13"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "foo"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "package2" },
]
[package.metadata]
requires-dist = [{ name = "package2", git = "https://github.com/astral-sh/uv-path-dependency-test.git?subdirectory=package2" }]
[[package]]
name = "package1"
version = "0.1.0"
source = { git = "https://github.com/astral-sh/uv-path-dependency-test.git?subdirectory=package1#28781b32cf1f260cdb2c8040628079eb265202bd" }
[[package]]
name = "package2"
version = "0.1.0"
source = { git = "https://github.com/astral-sh/uv-path-dependency-test.git?subdirectory=package2#28781b32cf1f260cdb2c8040628079eb265202bd" }
dependencies = [
{ name = "package1" },
]
"###
);
}
);
uv_snapshot!(context.filters(), context.sync(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 3 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ package1==0.1.0 (from git+https://github.com/astral-sh/uv-path-dependency-test.git@28781b32cf1f260cdb2c8040628079eb265202bd#subdirectory=package1)
+ package2==0.1.0 (from git+https://github.com/astral-sh/uv-path-dependency-test.git@28781b32cf1f260cdb2c8040628079eb265202bd#subdirectory=package2)
"###);
Ok(())
}