From 1dc0276458795d314375d62272e7a3887c261608 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 8 Dec 2024 12:22:18 -0500 Subject: [PATCH] Avoid treating non-existent `--find-links` as relative URLs (#9720) ## Summary Closes https://github.com/astral-sh/uv/issues/9681. --- crates/uv-client/src/flat_index.rs | 6 +++- crates/uv-distribution-types/src/index_url.rs | 29 ++++++++++----- crates/uv/tests/it/pip_install.rs | 35 +++++++++++++++++++ 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/crates/uv-client/src/flat_index.rs b/crates/uv-client/src/flat_index.rs index b0d41c9304e2..994bdd6013d6 100644 --- a/crates/uv-client/src/flat_index.rs +++ b/crates/uv-client/src/flat_index.rs @@ -243,8 +243,12 @@ impl<'a> FlatIndexClient<'a> { path: &Path, flat_index: &IndexUrl, ) -> Result { + // The path context is provided by the caller. + #[allow(clippy::disallowed_methods)] + let entries = std::fs::read_dir(path)?; + let mut dists = Vec::new(); - for entry in fs_err::read_dir(path)? { + for entry in entries { let entry = entry?; let metadata = entry.metadata()?; diff --git a/crates/uv-distribution-types/src/index_url.rs b/crates/uv-distribution-types/src/index_url.rs index 7600e0d97341..3973174b7deb 100644 --- a/crates/uv-distribution-types/src/index_url.rs +++ b/crates/uv-distribution-types/src/index_url.rs @@ -1,15 +1,15 @@ -use itertools::Either; -use rustc_hash::{FxHashMap, FxHashSet}; use std::borrow::Cow; use std::fmt::{Display, Formatter}; use std::ops::Deref; -use std::path::Path; use std::str::FromStr; use std::sync::{Arc, LazyLock, RwLock}; + +use itertools::Either; +use rustc_hash::{FxHashMap, FxHashSet}; use thiserror::Error; use url::{ParseError, Url}; -use uv_pep508::{VerbatimUrl, VerbatimUrlError}; +use uv_pep508::{split_scheme, Scheme, VerbatimUrl, VerbatimUrlError}; use crate::{Index, Verbatim}; @@ -114,10 +114,23 @@ impl FromStr for IndexUrl { type Err = IndexUrlError; fn from_str(s: &str) -> Result { - let url = if Path::new(s).exists() { - VerbatimUrl::from_absolute_path(std::path::absolute(s)?)? - } else { - VerbatimUrl::parse_url(s)? + let url = match split_scheme(s) { + Some((scheme, ..)) => { + match Scheme::parse(scheme) { + Some(_) => { + // Ex) `https://pypi.org/simple` + VerbatimUrl::parse_url(s)? + } + None => { + // Ex) `C:\Users\user\index` + VerbatimUrl::from_absolute_path(std::path::absolute(s)?)? + } + } + } + None => { + // Ex) `/Users/user/index` + VerbatimUrl::from_absolute_path(std::path::absolute(s)?)? + } }; Ok(Self::from(url.with_given(s))) } diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 416b80eb57b2..2342c295eda3 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -79,6 +79,41 @@ fn missing_pyproject_toml() { ); } +#[test] +fn missing_find_links() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_txt = context.temp_dir.child("requirements.txt"); + requirements_txt.write_str("flask")?; + + let error = regex::escape("The system cannot find the path specified. (os error 3)"); + let filters = context + .filters() + .into_iter() + .chain(std::iter::once(( + error.as_str(), + "No such file or directory (os error 2)", + ))) + .collect::>(); + + uv_snapshot!(filters, context.pip_install() + .arg("-r") + .arg("requirements.txt") + .arg("--find-links") + .arg("./missing") + .arg("--strict"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to read `--find-links` directory: [TEMP_DIR]/missing + Caused by: No such file or directory (os error 2) + "### + ); + + Ok(()) +} + #[test] fn invalid_pyproject_toml_syntax() -> Result<()> { let context = TestContext::new("3.12");