Skip to content

Commit

Permalink
Respect path dependencies within Git dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Dec 3, 2024
1 parent 4ec9ad2 commit cca841a
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
27 changes: 27 additions & 0 deletions crates/uv-distribution/src/metadata/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ impl LoweredRequirement {
}
let source = path_source(
PathBuf::from(path),
git_member,
origin,
project_dir,
workspace.install_path(),
Expand Down Expand Up @@ -465,6 +466,7 @@ impl LoweredRequirement {
}
let source = path_source(
PathBuf::from(path),
None,
RequirementOrigin::Project,
dir,
dir,
Expand Down Expand Up @@ -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}`")]
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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()));
}
Expand Down
89 changes: 89 additions & 0 deletions crates/uv/tests/it/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5200,3 +5200,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(())
}

0 comments on commit cca841a

Please sign in to comment.