Skip to content

Commit ef17e7d

Browse files
Load credentials for explicit members when lowering (#15844)
## Summary If the target for `uv pip compile` is a `pyproject.toml` in a subdirectory, we won't have loaded the credentials when we go to lower (since it won't be loaded as part of "configuration discovery"). We now add those indexes just-in-time. Closes #15362.
1 parent 43c187d commit ef17e7d

File tree

4 files changed

+85
-16
lines changed

4 files changed

+85
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/uv-distribution/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ doctest = false
1616
workspace = true
1717

1818
[dependencies]
19+
uv-auth = { workspace = true }
1920
uv-cache = { workspace = true }
2021
uv-cache-info = { workspace = true }
2122
uv-client = { workspace = true }

crates/uv-distribution/src/metadata/lowering.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::BTreeMap;
22
use std::io;
33
use std::path::{Path, PathBuf};
4+
use std::sync::Arc;
45

56
use either::Either;
67
use thiserror::Error;
@@ -222,20 +223,20 @@ impl LoweredRequirement {
222223
.find(|Index { name, .. }| {
223224
name.as_ref().is_some_and(|name| *name == index)
224225
})
225-
.map(
226-
|Index {
227-
url, format: kind, ..
228-
}| IndexMetadata {
229-
url: url.clone(),
230-
format: *kind,
231-
},
232-
)
233226
else {
234227
return Err(LoweringError::MissingIndex(
235228
requirement.name.clone(),
236229
index,
237230
));
238231
};
232+
if let Some(credentials) = index.credentials() {
233+
let credentials = Arc::new(credentials);
234+
uv_auth::store_credentials(index.raw_url(), credentials);
235+
}
236+
let index = IndexMetadata {
237+
url: index.url.clone(),
238+
format: index.format,
239+
};
239240
let conflict = project_name.and_then(|project_name| {
240241
if let Some(extra) = extra {
241242
Some(ConflictItem::from((project_name.clone(), extra)))
@@ -456,20 +457,20 @@ impl LoweredRequirement {
456457
.find(|Index { name, .. }| {
457458
name.as_ref().is_some_and(|name| *name == index)
458459
})
459-
.map(
460-
|Index {
461-
url, format: kind, ..
462-
}| IndexMetadata {
463-
url: url.clone(),
464-
format: *kind,
465-
},
466-
)
467460
else {
468461
return Err(LoweringError::MissingIndex(
469462
requirement.name.clone(),
470463
index,
471464
));
472465
};
466+
if let Some(credentials) = index.credentials() {
467+
let credentials = Arc::new(credentials);
468+
uv_auth::store_credentials(index.raw_url(), credentials);
469+
}
470+
let index = IndexMetadata {
471+
url: index.url.clone(),
472+
format: index.format,
473+
};
473474
let conflict = None;
474475
let source = registry_source(&requirement, index, conflict);
475476
(source, marker)

crates/uv/tests/it/pip_compile.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17784,3 +17784,69 @@ fn omit_python_patch_universal() -> Result<()> {
1778417784

1778517785
Ok(())
1778617786
}
17787+
17788+
#[test]
17789+
fn credentials_from_subdirectory() -> Result<()> {
17790+
let context = TestContext::new("3.12");
17791+
17792+
// Create a local dependency in a subdirectory.
17793+
let pyproject_toml = context.temp_dir.child("foo").child("pyproject.toml");
17794+
pyproject_toml.write_str(
17795+
r#"
17796+
[project]
17797+
name = "foo"
17798+
version = "1.0.0"
17799+
dependencies = ["iniconfig"]
17800+
17801+
[build-system]
17802+
requires = ["hatchling"]
17803+
build-backend = "hatchling.build"
17804+
17805+
[tool.uv.sources]
17806+
iniconfig = { index = "internal" }
17807+
17808+
[[tool.uv.index]]
17809+
name = "internal"
17810+
url = "https://pypi-proxy.fly.dev/basic-auth/simple/"
17811+
explicit = true
17812+
"#,
17813+
)?;
17814+
context
17815+
.temp_dir
17816+
.child("foo")
17817+
.child("src")
17818+
.child("foo")
17819+
.child("__init__.py")
17820+
.touch()?;
17821+
17822+
uv_snapshot!(context.filters(), context
17823+
.pip_compile()
17824+
.arg("foo/pyproject.toml"), @r"
17825+
success: false
17826+
exit_code: 1
17827+
----- stdout -----
17828+
17829+
----- stderr -----
17830+
× No solution found when resolving dependencies:
17831+
╰─▶ Because iniconfig was not found in the package registry and foo depends on iniconfig, we can conclude that your requirements are unsatisfiable.
17832+
");
17833+
17834+
uv_snapshot!(context.filters(), context
17835+
.pip_compile()
17836+
.arg("foo/pyproject.toml")
17837+
.env("UV_INDEX_INTERNAL_USERNAME", "public")
17838+
.env("UV_INDEX_INTERNAL_PASSWORD", "heron"), @r"
17839+
success: true
17840+
exit_code: 0
17841+
----- stdout -----
17842+
# This file was autogenerated by uv via the following command:
17843+
# uv pip compile --cache-dir [CACHE_DIR] foo/pyproject.toml
17844+
iniconfig==2.0.0
17845+
# via foo (foo/pyproject.toml)
17846+
17847+
----- stderr -----
17848+
Resolved 1 package in [TIME]
17849+
");
17850+
17851+
Ok(())
17852+
}

0 commit comments

Comments
 (0)