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 https://github.com/astral-sh/uv/issues/15362.
This commit is contained in:
Charlie Marsh 2025-09-15 09:54:38 -04:00 committed by GitHub
parent 43c187dd93
commit ef17e7d0f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 85 additions and 16 deletions

1
Cargo.lock generated
View file

@ -5624,6 +5624,7 @@ dependencies = [
"toml",
"tracing",
"url",
"uv-auth",
"uv-cache",
"uv-cache-info",
"uv-client",

View file

@ -16,6 +16,7 @@ doctest = false
workspace = true
[dependencies]
uv-auth = { workspace = true }
uv-cache = { workspace = true }
uv-cache-info = { workspace = true }
uv-client = { workspace = true }

View file

@ -1,6 +1,7 @@
use std::collections::BTreeMap;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use either::Either;
use thiserror::Error;
@ -222,20 +223,20 @@ impl LoweredRequirement {
.find(|Index { name, .. }| {
name.as_ref().is_some_and(|name| *name == index)
})
.map(
|Index {
url, format: kind, ..
}| IndexMetadata {
url: url.clone(),
format: *kind,
},
)
else {
return Err(LoweringError::MissingIndex(
requirement.name.clone(),
index,
));
};
if let Some(credentials) = index.credentials() {
let credentials = Arc::new(credentials);
uv_auth::store_credentials(index.raw_url(), credentials);
}
let index = IndexMetadata {
url: index.url.clone(),
format: index.format,
};
let conflict = project_name.and_then(|project_name| {
if let Some(extra) = extra {
Some(ConflictItem::from((project_name.clone(), extra)))
@ -456,20 +457,20 @@ impl LoweredRequirement {
.find(|Index { name, .. }| {
name.as_ref().is_some_and(|name| *name == index)
})
.map(
|Index {
url, format: kind, ..
}| IndexMetadata {
url: url.clone(),
format: *kind,
},
)
else {
return Err(LoweringError::MissingIndex(
requirement.name.clone(),
index,
));
};
if let Some(credentials) = index.credentials() {
let credentials = Arc::new(credentials);
uv_auth::store_credentials(index.raw_url(), credentials);
}
let index = IndexMetadata {
url: index.url.clone(),
format: index.format,
};
let conflict = None;
let source = registry_source(&requirement, index, conflict);
(source, marker)

View file

@ -17784,3 +17784,69 @@ fn omit_python_patch_universal() -> Result<()> {
Ok(())
}
#[test]
fn credentials_from_subdirectory() -> Result<()> {
let context = TestContext::new("3.12");
// Create a local dependency in a subdirectory.
let pyproject_toml = context.temp_dir.child("foo").child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "foo"
version = "1.0.0"
dependencies = ["iniconfig"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.uv.sources]
iniconfig = { index = "internal" }
[[tool.uv.index]]
name = "internal"
url = "https://pypi-proxy.fly.dev/basic-auth/simple/"
explicit = true
"#,
)?;
context
.temp_dir
.child("foo")
.child("src")
.child("foo")
.child("__init__.py")
.touch()?;
uv_snapshot!(context.filters(), context
.pip_compile()
.arg("foo/pyproject.toml"), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
× No solution found when resolving dependencies:
Because iniconfig was not found in the package registry and foo depends on iniconfig, we can conclude that your requirements are unsatisfiable.
");
uv_snapshot!(context.filters(), context
.pip_compile()
.arg("foo/pyproject.toml")
.env("UV_INDEX_INTERNAL_USERNAME", "public")
.env("UV_INDEX_INTERNAL_PASSWORD", "heron"), @r"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] foo/pyproject.toml
iniconfig==2.0.0
# via foo (foo/pyproject.toml)
----- stderr -----
Resolved 1 package in [TIME]
");
Ok(())
}