Avoid rejecting already-installed URL distributions with `--no-sources` (#16094)

## Summary

This PR removes a guard that was accidentally included in
https://github.com/astral-sh/uv/pull/15234/files#diff-6be6d80fe4821c47b70a372260f55e73b8da8182b8dcad7525d5cd3eb584532b.
I meant to remove that logic before merging.

Closes https://github.com/astral-sh/uv/issues/16068.
This commit is contained in:
Charlie Marsh 2025-10-02 09:32:14 -04:00 committed by GitHub
parent 0bd3dfd5ea
commit 8da9df3654
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 86 additions and 29 deletions

View File

@ -6,10 +6,8 @@ use pubgrub::Range;
use smallvec::SmallVec;
use tracing::{debug, trace};
use uv_configuration::{IndexStrategy, SourceStrategy};
use uv_distribution_types::{
CompatibleDist, IncompatibleDist, IncompatibleSource, IndexUrl, InstalledDistKind,
};
use uv_configuration::IndexStrategy;
use uv_distribution_types::{CompatibleDist, IncompatibleDist, IncompatibleSource, IndexUrl};
use uv_distribution_types::{DistributionMetadata, IncompatibleWheel, Name, PrioritizedDist};
use uv_normalize::PackageName;
use uv_pep440::Version;
@ -28,7 +26,6 @@ pub(crate) struct CandidateSelector {
resolution_strategy: ResolutionStrategy,
prerelease_strategy: PrereleaseStrategy,
index_strategy: IndexStrategy,
source_strategy: SourceStrategy,
}
impl CandidateSelector {
@ -37,7 +34,6 @@ impl CandidateSelector {
options: &Options,
manifest: &Manifest,
env: &ResolverEnvironment,
source_strategy: SourceStrategy,
) -> Self {
Self {
resolution_strategy: ResolutionStrategy::from_mode(
@ -53,7 +49,6 @@ impl CandidateSelector {
options.dependency_mode,
),
index_strategy: options.index_strategy,
source_strategy,
}
}
@ -124,13 +119,7 @@ impl CandidateSelector {
let installed = if reinstall {
None
} else {
Self::get_installed(
package_name,
range,
installed_packages,
tags,
self.source_strategy,
)
Self::get_installed(package_name, range, installed_packages, tags)
};
// If we're not upgrading, we should prefer the already-installed distribution.
@ -380,7 +369,6 @@ impl CandidateSelector {
range: &Range<Version>,
installed_packages: &'a InstalledPackages,
tags: Option<&'a Tags>,
source_strategy: SourceStrategy,
) -> Option<Candidate<'a>> {
let installed_dists = installed_packages.get_packages(package_name);
match installed_dists.as_slice() {
@ -393,16 +381,6 @@ impl CandidateSelector {
return None;
}
// When sources are disabled, only allow registry installations to be reused
if matches!(source_strategy, SourceStrategy::Disabled) {
if !matches!(dist.kind, InstalledDistKind::Registry(_)) {
debug!(
"Source strategy is disabled, rejecting non-registry installed distribution: {dist}"
);
return None;
}
}
// Verify that the installed distribution is compatible with the environment.
if tags.is_some_and(|tags| {
let Ok(Some(wheel_tags)) = dist.read_tags() else {

View File

@ -20,7 +20,7 @@ use tokio::sync::oneshot;
use tokio_stream::wrappers::ReceiverStream;
use tracing::{Level, debug, info, instrument, trace, warn};
use uv_configuration::{Constraints, Overrides, SourceStrategy};
use uv_configuration::{Constraints, Overrides};
use uv_distribution::{ArchiveMetadata, DistributionDatabase};
use uv_distribution_types::{
BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, DistributionMetadata,
@ -201,7 +201,6 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
build_context.git(),
build_context.capabilities(),
build_context.locations(),
build_context.sources(),
provider,
installed_packages,
)
@ -225,7 +224,6 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
git: &GitResolver,
capabilities: &IndexCapabilities,
locations: &IndexLocations,
source_strategy: SourceStrategy,
provider: Provider,
installed_packages: InstalledPackages,
) -> Result<Self, ResolveError> {
@ -233,7 +231,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
index: index.clone(),
git: git.clone(),
capabilities: capabilities.clone(),
selector: CandidateSelector::for_resolution(&options, &manifest, &env, source_strategy),
selector: CandidateSelector::for_resolution(&options, &manifest, &env),
dependency_mode: options.dependency_mode,
urls: Urls::from_manifest(&manifest, &env, git, options.dependency_mode),
indexes: Indexes::from_manifest(&manifest, &env, options.dependency_mode),

View File

@ -12358,6 +12358,87 @@ fn reject_invalid_short_usize_zip64() {
);
}
/// Regression test for: <https://github.com/astral-sh/uv/issues/16068>
#[test]
fn already_installed_url_dependency_no_sources() -> Result<()> {
let context = TestContext::new("3.12").with_filtered_counts();
context
.temp_dir
.child("foo")
.child("pyproject.toml")
.write_str(indoc! {r#"
[project]
name = "foo"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
"#})?;
context
.temp_dir
.child("foo")
.child("src")
.child("foo")
.child("__init__.py")
.touch()?;
context
.temp_dir
.child("bar")
.child("pyproject.toml")
.write_str(indoc! {r#"
[project]
name = "bar"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["foo"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
"#})?;
context
.temp_dir
.child("bar")
.child("src")
.child("bar")
.child("__init__.py")
.touch()?;
// Install `foo`.
uv_snapshot!(context.filters(), context.pip_install().arg("./foo"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved [N] packages in [TIME]
Prepared [N] packages in [TIME]
Installed [N] packages in [TIME]
+ foo==0.1.0 (from file://[TEMP_DIR]/foo)
+ iniconfig==2.0.0
");
// Install `bar` with `--no-sources`.
uv_snapshot!(context.filters(), context.pip_install().arg("./bar").arg("--no-sources"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved [N] packages in [TIME]
Prepared [N] packages in [TIME]
Installed [N] packages in [TIME]
+ bar==0.1.0 (from file://[TEMP_DIR]/bar)
");
Ok(())
}
/// Test that build dependencies respect locked versions from the resolution.
#[test]
fn pip_install_build_dependencies_respect_locked_versions() -> Result<()> {