Skip to content

Commit 2250dde

Browse files
Ignore --find-links entries for pinned indexes (#12396)
## Summary In general, we merge `--find-links` entries into each index. If a package is pinned to an index, though, it seems surprising (and wrong) that we'd ever select a distribution from `--find-links`. This PR modifies the provider to ignore `--find-links` for any explicitly pinned packages.
1 parent 8694578 commit 2250dde

File tree

2 files changed

+139
-5
lines changed

2 files changed

+139
-5
lines changed

crates/uv-resolver/src/resolver/provider.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ impl<Context: BuildContext> ResolverProvider for DefaultResolverProvider<'_, Con
160160
})
161161
.await;
162162

163+
// If a package is pinned to an explicit index, ignore any `--find-links` entries.
164+
let flat_index = index.is_none().then_some(&self.flat_index);
165+
163166
match result {
164167
Ok(results) => Ok(VersionsResponse::Found(
165168
results
@@ -174,31 +177,42 @@ impl<Context: BuildContext> ResolverProvider for DefaultResolverProvider<'_, Con
174177
&self.allowed_yanks,
175178
&self.hasher,
176179
self.exclude_newer.as_ref(),
177-
self.flat_index.get(package_name).cloned(),
180+
flat_index
181+
.and_then(|flat_index| flat_index.get(package_name))
182+
.cloned(),
178183
self.build_options,
179184
)
180185
})
181186
.collect(),
182187
)),
183188
Err(err) => match err.into_kind() {
184189
uv_client::ErrorKind::PackageNotFound(_) => {
185-
if let Some(flat_index) = self.flat_index.get(package_name).cloned() {
190+
if let Some(flat_index) = flat_index
191+
.and_then(|flat_index| flat_index.get(package_name))
192+
.cloned()
193+
{
186194
Ok(VersionsResponse::Found(vec![VersionMap::from(flat_index)]))
187195
} else {
188196
Ok(VersionsResponse::NotFound)
189197
}
190198
}
191199
uv_client::ErrorKind::NoIndex(_) => {
192-
if let Some(flat_index) = self.flat_index.get(package_name).cloned() {
200+
if let Some(flat_index) = flat_index
201+
.and_then(|flat_index| flat_index.get(package_name))
202+
.cloned()
203+
{
193204
Ok(VersionsResponse::Found(vec![VersionMap::from(flat_index)]))
194-
} else if self.flat_index.offline() {
205+
} else if flat_index.is_some_and(FlatIndex::offline) {
195206
Ok(VersionsResponse::Offline)
196207
} else {
197208
Ok(VersionsResponse::NoIndex)
198209
}
199210
}
200211
uv_client::ErrorKind::Offline(_) => {
201-
if let Some(flat_index) = self.flat_index.get(package_name).cloned() {
212+
if let Some(flat_index) = flat_index
213+
.and_then(|flat_index| flat_index.get(package_name))
214+
.cloned()
215+
{
202216
Ok(VersionsResponse::Found(vec![VersionMap::from(flat_index)]))
203217
} else {
204218
Ok(VersionsResponse::Offline)

crates/uv/tests/it/lock.rs

+120
Original file line numberDiff line numberDiff line change
@@ -9549,6 +9549,126 @@ fn lock_find_links_local_wheel() -> Result<()> {
95499549
Ok(())
95509550
}
95519551

9552+
/// Prefer an explicit index over any `--find-links` entries.
9553+
#[test]
9554+
fn lock_find_links_explicit_index() -> Result<()> {
9555+
let context = TestContext::new("3.12");
9556+
9557+
// Populate the `--find-links` entries.
9558+
fs_err::create_dir_all(context.temp_dir.join("links"))?;
9559+
9560+
for entry in fs_err::read_dir(context.workspace_root.join("scripts/links"))? {
9561+
let entry = entry?;
9562+
let path = entry.path();
9563+
if path
9564+
.file_name()
9565+
.and_then(|file_name| file_name.to_str())
9566+
.is_some_and(|file_name| file_name.starts_with("tqdm-"))
9567+
{
9568+
let dest = context
9569+
.temp_dir
9570+
.join("links")
9571+
.join(path.file_name().unwrap());
9572+
fs_err::copy(&path, &dest)?;
9573+
}
9574+
}
9575+
9576+
let workspace = context.temp_dir.child("workspace");
9577+
9578+
let pyproject_toml = workspace.child("pyproject.toml");
9579+
pyproject_toml.write_str(&formatdoc! { r#"
9580+
[project]
9581+
name = "project"
9582+
version = "0.1.0"
9583+
requires-python = ">=3.12"
9584+
dependencies = ["tqdm"]
9585+
9586+
[[tool.uv.index]]
9587+
name = "pypi"
9588+
url = "https://pypi.org/simple"
9589+
9590+
[tool.uv.sources]
9591+
tqdm = {{ index = "pypi" }}
9592+
9593+
[tool.uv]
9594+
find-links = ["{}"]
9595+
"#,
9596+
context.temp_dir.join("links/").portable_display(),
9597+
})?;
9598+
9599+
uv_snapshot!(context.filters(), context.lock().current_dir(&workspace), @r"
9600+
success: true
9601+
exit_code: 0
9602+
----- stdout -----
9603+
9604+
----- stderr -----
9605+
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
9606+
Resolved 3 packages in [TIME]
9607+
");
9608+
9609+
let lock = fs_err::read_to_string(workspace.join("uv.lock")).unwrap();
9610+
9611+
insta::with_settings!({
9612+
filters => context.filters(),
9613+
}, {
9614+
assert_snapshot!(
9615+
lock, @r#"
9616+
version = 1
9617+
revision = 1
9618+
requires-python = ">=3.12"
9619+
9620+
[options]
9621+
exclude-newer = "2024-03-25T00:00:00Z"
9622+
9623+
[[package]]
9624+
name = "colorama"
9625+
version = "0.4.6"
9626+
source = { registry = "https://pypi.org/simple" }
9627+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
9628+
wheels = [
9629+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
9630+
]
9631+
9632+
[[package]]
9633+
name = "project"
9634+
version = "0.1.0"
9635+
source = { virtual = "." }
9636+
dependencies = [
9637+
{ name = "tqdm" },
9638+
]
9639+
9640+
[package.metadata]
9641+
requires-dist = [{ name = "tqdm", index = "https://pypi.org/simple" }]
9642+
9643+
[[package]]
9644+
name = "tqdm"
9645+
version = "4.66.2"
9646+
source = { registry = "https://pypi.org/simple" }
9647+
dependencies = [
9648+
{ name = "colorama", marker = "sys_platform == 'win32'" },
9649+
]
9650+
sdist = { url = "https://files.pythonhosted.org/packages/ea/85/3ce0f9f7d3f596e7ea57f4e5ce8c18cb44e4a9daa58ddb46ee0d13d6bff8/tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531", size = 169462 }
9651+
wheels = [
9652+
{ url = "https://files.pythonhosted.org/packages/2a/14/e75e52d521442e2fcc9f1df3c5e456aead034203d4797867980de558ab34/tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9", size = 78296 },
9653+
]
9654+
"#
9655+
);
9656+
});
9657+
9658+
// Re-run with `--locked`.
9659+
uv_snapshot!(context.filters(), context.lock().arg("--locked").current_dir(&workspace), @r"
9660+
success: true
9661+
exit_code: 0
9662+
----- stdout -----
9663+
9664+
----- stderr -----
9665+
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
9666+
Resolved 3 packages in [TIME]
9667+
");
9668+
9669+
Ok(())
9670+
}
9671+
95529672
/// Lock a local source distribution via `--find-links`.
95539673
#[test]
95549674
fn lock_find_links_local_sdist() -> Result<()> {

0 commit comments

Comments
 (0)