Use portable paths for subdirectories in lock URLs (#8707)

## Summary

We're writing different URLs on Windows vs. Linux because the
subdirectory path is being serialized with a back vs. forward slash.
This commit is contained in:
Charlie Marsh 2024-10-30 15:37:40 -04:00 committed by GitHub
parent 4a5a79eed8
commit bed47d512a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 149 additions and 4 deletions

View file

@ -3152,9 +3152,15 @@ fn locked_git_url(git_dist: &GitSourceDist) -> Url {
url.set_query(None); url.set_query(None);
// Put the subdirectory in the query. // Put the subdirectory in the query.
if let Some(subdirectory) = git_dist.subdirectory.as_deref().and_then(Path::to_str) { if let Some(subdirectory) = git_dist
.subdirectory
.as_deref()
.map(PortablePath::from)
.as_ref()
.map(PortablePath::to_string)
{
url.query_pairs_mut() url.query_pairs_mut()
.append_pair("subdirectory", subdirectory); .append_pair("subdirectory", &subdirectory);
} }
// Put the requested reference in the query. // Put the requested reference in the query.

View file

@ -17389,3 +17389,144 @@ fn lock_group_empty_entry_table() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn lock_transitive_git() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "a"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["c"]
[tool.uv.sources]
c = { git = "https://github.com/astral-sh/workspace-virtual-root-test", subdirectory = "packages/c", rev = "fac39c8d4c5d0ef32744e2bb309bbe34a759fd46" }
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 6 packages in [TIME]
"###);
let lock = context.read("uv.lock");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "a"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "c" },
]
[package.metadata]
requires-dist = [{ name = "c", git = "https://github.com/astral-sh/workspace-virtual-root-test?subdirectory=packages%2Fc&rev=fac39c8d4c5d0ef32744e2bb309bbe34a759fd46" }]
[[package]]
name = "anyio"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", size = 85584 },
]
[[package]]
name = "c"
version = "1.0.0"
source = { git = "https://github.com/astral-sh/workspace-virtual-root-test?subdirectory=packages%2Fc&rev=fac39c8d4c5d0ef32744e2bb309bbe34a759fd46#fac39c8d4c5d0ef32744e2bb309bbe34a759fd46" }
dependencies = [
{ name = "d" },
]
[[package]]
name = "d"
version = "1.0.0"
source = { git = "https://github.com/astral-sh/workspace-virtual-root-test?subdirectory=packages%2Fd&rev=fac39c8d4c5d0ef32744e2bb309bbe34a759fd46#fac39c8d4c5d0ef32744e2bb309bbe34a759fd46" }
dependencies = [
{ name = "anyio" },
]
[[package]]
name = "idna"
version = "3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
"###
);
});
// Re-run with `--locked`.
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 6 packages in [TIME]
"###);
// Re-run with `--offline`. We shouldn't need a network connection to validate an
// already-correct lockfile with immutable metadata.
uv_snapshot!(context.filters(), context.lock().arg("--locked").arg("--offline").arg("--no-cache"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 6 packages in [TIME]
"###);
// Install from the lockfile.
uv_snapshot!(context.filters(), context.sync().arg("--frozen"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 5 packages in [TIME]
Installed 5 packages in [TIME]
+ anyio==4.3.0
+ c==1.0.0 (from git+https://github.com/astral-sh/workspace-virtual-root-test@fac39c8d4c5d0ef32744e2bb309bbe34a759fd46#subdirectory=packages/c)
+ d==1.0.0 (from git+https://github.com/astral-sh/workspace-virtual-root-test@fac39c8d4c5d0ef32744e2bb309bbe34a759fd46#subdirectory=packages/d)
+ idna==3.6
+ sniffio==1.3.1
"###);
Ok(())
}

View file

@ -1766,7 +1766,6 @@ fn test_path_hopping() -> Result<()> {
/// are correctly resolving `d` to a git dependency with a subdirectory and not relative to the /// are correctly resolving `d` to a git dependency with a subdirectory and not relative to the
/// checkout directory. /// checkout directory.
#[test] #[test]
#[cfg(not(windows))]
fn transitive_dep_in_git_workspace_no_root() -> Result<()> { fn transitive_dep_in_git_workspace_no_root() -> Result<()> {
let context = TestContext::new("3.12"); let context = TestContext::new("3.12");
@ -1789,7 +1788,6 @@ fn transitive_dep_in_git_workspace_no_root() -> Result<()> {
let lock1: SourceLock = let lock1: SourceLock =
toml::from_str(&fs_err::read_to_string(context.temp_dir.child("uv.lock"))?)?; toml::from_str(&fs_err::read_to_string(context.temp_dir.child("uv.lock"))?)?;
// TODO(charlie): Fails on Windows due to non-portable subdirectory path in URL.
assert_json_snapshot!(lock1.sources(), @r###" assert_json_snapshot!(lock1.sources(), @r###"
{ {
"a": { "a": {