Skip to content

Commit

Permalink
Respect --no-index in Finder
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Mar 27, 2024
1 parent 236a416 commit a95e602
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 11 deletions.
5 changes: 5 additions & 0 deletions crates/uv-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ impl Error {
*self.kind
}

/// Get a reference to the [`ErrorKind`] variant of this error.
pub fn kind(&self) -> &ErrorKind {
&self.kind
}

/// Create a new error from a JSON parsing error.
pub(crate) fn from_json_err(err: serde_json::Error, url: Url) -> Self {
ErrorKind::BadJson { source: err, url }.into()
Expand Down
49 changes: 44 additions & 5 deletions crates/uv-resolver/src/finder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,29 @@ impl<'a> DistFinder<'a> {
match requirement.version_or_url.as_ref() {
None | Some(VersionOrUrl::VersionSpecifier(_)) => {
// Query the index(es) (cached) to get the URLs for the available files.
let (index, raw_metadata) = self.client.simple(&requirement.name).await?;
let metadata = OwnedArchive::deserialize(&raw_metadata);
let dist = match self.client.simple(&requirement.name).await {
Ok((index, raw_metadata)) => {
let metadata = OwnedArchive::deserialize(&raw_metadata);

// Pick a version that satisfies the requirement.
let Some(dist) = self.select(requirement, metadata, &index, flat_index) else {
// Pick a version that satisfies the requirement.
self.select_from_index(requirement, metadata, &index, flat_index)
}
Err(err) => match err.kind() {
uv_client::ErrorKind::PackageNotFound(_)
| uv_client::ErrorKind::NoIndex(_)
| uv_client::ErrorKind::Offline(_) => {
if let Some(flat_index) = self.flat_index.get(&requirement.name) {
Self::select_from_flat_index(requirement, flat_index)
} else {
return Err(ResolveError::Client(err));
}
}
_ => return Err(ResolveError::Client(err)),
},
};

// Verify that a distribution was found.
let Some(dist) = dist else {
return Err(ResolveError::NotFound(requirement.clone()));
};

Expand Down Expand Up @@ -126,7 +144,7 @@ impl<'a> DistFinder<'a> {
///
/// Wheels are preferred to source distributions unless `no_binary` excludes wheels
/// for the requirement.
fn select(
fn select_from_index(
&self,
requirement: &Requirement,
metadata: SimpleMetadata,
Expand Down Expand Up @@ -248,6 +266,27 @@ impl<'a> DistFinder<'a> {

best_wheel.map_or(best_sdist, |(wheel, ..)| Some(wheel))
}

/// Select a matching version from a flat index.
fn select_from_flat_index(
requirement: &Requirement,
flat_index: &FlatDistributions,
) -> Option<Dist> {
let matching_override = match &requirement.version_or_url {
None => flat_index.iter().next(),
Some(VersionOrUrl::Url(_)) => None,
Some(VersionOrUrl::VersionSpecifier(specifiers)) => flat_index
.iter()
.find(|(version, _)| specifiers.contains(version)),
};

let (_, resolvable_dist) = matching_override?;

resolvable_dist.compatible_wheel().map_or_else(
|| resolvable_dist.compatible_source().cloned(),
|(dist, _)| Some(dist.clone()),
)
}
}

pub trait Reporter: Send + Sync {
Expand Down
30 changes: 24 additions & 6 deletions crates/uv/tests/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2969,15 +2969,33 @@ fn compile_editable_url_requirement() -> Result<()> {
success: true
exit_code: 0
----- stdout -----
Found static `pyproject.toml` for: black @ file:///Users/crmarsh/workspace/uv/scripts/packages/black_editable
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z [TEMP_DIR]/requirements.in
-e ../../scripts/packages/hatchling_editable
iniconfig @ git+https://github.com/pytest-dev/iniconfig@9cae43103df70bac6fde7b9f35ad11a9f1be0cb4
# via hatchling-editable
# uv pip compile requirements.in --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z
aiohttp==3.9.3
# via black
aiosignal==1.3.1
# via aiohttp
attrs==23.2.0
# via aiohttp
black @ ../../scripts/packages/black_editable
frozenlist==1.4.1
# via
# aiohttp
# aiosignal
idna==3.6
# via yarl
multidict==6.0.5
# via
# aiohttp
# yarl
uvloop==0.19.0
# via black
yarl==1.9.4
# via aiohttp
----- stderr -----
Built 1 editable in [TIME]
Resolved 2 packages in [TIME]
Resolved 9 packages in [TIME]
"###);

Ok(())
Expand Down
88 changes: 88 additions & 0 deletions crates/uv/tests/pip_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2480,6 +2480,94 @@ fn find_links() -> Result<()> {
Ok(())
}

/// Sync using `--find-links` with `--no-index`, which should accept the local wheel.
#[test]
fn find_links_no_index_match() -> Result<()> {
let context = TestContext::new("3.12");

let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str(indoc! {r"
tqdm==1000.0.0
"})?;

uv_snapshot!(context.filters(), command(&context)
.arg("requirements.txt")
.arg("--no-index")
.arg("--find-links")
.arg(context.workspace_root.join("scripts/wheels/")), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Downloaded 1 package in [TIME]
Installed 1 package in [TIME]
+ tqdm==1000.0.0
"###
);

Ok(())
}

/// Sync using `--find-links` with `--offline`, which should accept the local wheel.
#[test]
fn find_links_offline_match() -> Result<()> {
let context = TestContext::new("3.12");

let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str(indoc! {r"
tqdm==1000.0.0
"})?;

uv_snapshot!(context.filters(), command(&context)
.arg("requirements.txt")
.arg("--offline")
.arg("--find-links")
.arg(context.workspace_root.join("scripts/wheels/")), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Downloaded 1 package in [TIME]
Installed 1 package in [TIME]
+ tqdm==1000.0.0
"###
);

Ok(())
}

/// Sync using `--find-links` with `--offline`, which should fail to find `numpy`.
#[test]
fn find_links_offline_no_match() -> Result<()> {
let context = TestContext::new("3.12");

let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str(indoc! {r"
numpy
tqdm==1000.0.0
"})?;

uv_snapshot!(context.filters(), command(&context)
.arg("requirements.txt")
.arg("--offline")
.arg("--find-links")
.arg(context.workspace_root.join("scripts/wheels/")), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Network connectivity is disabled, but the requested data wasn't found in the cache for: `numpy`
"###
);

Ok(())
}

/// Install without network access via the `--offline` flag.
#[test]
fn offline() -> Result<()> {
Expand Down

0 comments on commit a95e602

Please sign in to comment.