mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 02:48:17 +00:00
Filter out incompatible dists (#398)
Filter out source dists and wheels whose `requires-python` from the simple api is incompatible with the current python version. This change showed an important problem: When we use a fake python version for resolving, building source distributions breaks down because we can only build with versions we actually have. This change became surprisingly big. The tests now require python 3.7 to be installed, but changing that would mean an even bigger change. Fixes #388
This commit is contained in:
parent
81c9cd0d4a
commit
76a41066ac
27 changed files with 365 additions and 187 deletions
|
@ -21,6 +21,7 @@ puffin-cache = { path = "../puffin-cache" }
|
|||
puffin-client = { path = "../puffin-client" }
|
||||
puffin-distribution = { path = "../puffin-distribution" }
|
||||
puffin-git = { path = "../puffin-git" }
|
||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||
puffin-normalize = { path = "../puffin-normalize" }
|
||||
puffin-traits = { path = "../puffin-traits" }
|
||||
pypi-types = { path = "../pypi-types" }
|
||||
|
|
|
@ -14,6 +14,7 @@ use pep508_rs::{Requirement, VersionOrUrl};
|
|||
use platform_tags::Tags;
|
||||
use puffin_client::RegistryClient;
|
||||
use puffin_distribution::Dist;
|
||||
use puffin_interpreter::InterpreterInfo;
|
||||
use puffin_normalize::PackageName;
|
||||
use pypi_types::{File, SimpleJson};
|
||||
|
||||
|
@ -24,15 +25,21 @@ pub struct DistFinder<'a> {
|
|||
tags: &'a Tags,
|
||||
client: &'a RegistryClient,
|
||||
reporter: Option<Box<dyn Reporter>>,
|
||||
interpreter_info: &'a InterpreterInfo,
|
||||
}
|
||||
|
||||
impl<'a> DistFinder<'a> {
|
||||
/// Initialize a new distribution finder.
|
||||
pub fn new(tags: &'a Tags, client: &'a RegistryClient) -> Self {
|
||||
pub fn new(
|
||||
tags: &'a Tags,
|
||||
client: &'a RegistryClient,
|
||||
interpreter_info: &'a InterpreterInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
tags,
|
||||
client,
|
||||
reporter: None,
|
||||
interpreter_info,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +136,20 @@ impl<'a> DistFinder<'a> {
|
|||
fn select(&self, requirement: &Requirement, files: Vec<File>) -> Option<Dist> {
|
||||
let mut fallback = None;
|
||||
for file in files.into_iter().rev() {
|
||||
// Only add dists compatible with the python version.
|
||||
// This is relevant for source dists which give no other indication of their
|
||||
// compatibility and wheels which may be tagged `py3-none-any` but
|
||||
// have `requires-python: ">=3.9"`
|
||||
// TODO(konstin): https://github.com/astral-sh/puffin/issues/406
|
||||
if !file
|
||||
.requires_python
|
||||
.as_ref()
|
||||
.map_or(true, |requires_python| {
|
||||
requires_python.contains(self.interpreter_info.version())
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Ok(wheel) = WheelFilename::from_str(file.filename.as_str()) {
|
||||
if !wheel.is_compatible(self.tags) {
|
||||
continue;
|
||||
|
|
|
@ -536,6 +536,21 @@ impl<'a, Context: BuildContext + Sync> Resolver<'a, Context> {
|
|||
// distributions.
|
||||
let mut version_map: VersionMap = BTreeMap::new();
|
||||
for file in metadata.files {
|
||||
// Only add dists compatible with the python version.
|
||||
// This is relevant for source dists which give no other indication of their
|
||||
// compatibility and wheels which may be tagged `py3-none-any` but
|
||||
// have `requires-python: ">=3.9"`
|
||||
// TODO(konstin): https://github.com/astral-sh/puffin/issues/406
|
||||
if !file
|
||||
.requires_python
|
||||
.as_ref()
|
||||
.map_or(true, |requires_python| {
|
||||
requires_python
|
||||
.contains(self.build_context.interpreter_info().version())
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Ok(filename) = WheelFilename::from_str(file.filename.as_str()) {
|
||||
if filename.is_compatible(self.tags) {
|
||||
let version = PubGrubVersion::from(filename.version.clone());
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! `PyPI` directly.
|
||||
|
||||
use std::future::Future;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -17,10 +17,12 @@ use platform_host::{Arch, Os, Platform};
|
|||
use platform_tags::Tags;
|
||||
use puffin_client::RegistryClientBuilder;
|
||||
use puffin_interpreter::{InterpreterInfo, Virtualenv};
|
||||
use puffin_resolver::{Manifest, PreReleaseMode, ResolutionMode, Resolver};
|
||||
use puffin_resolver::{Graph, Manifest, PreReleaseMode, ResolutionMode, Resolver};
|
||||
use puffin_traits::BuildContext;
|
||||
|
||||
struct DummyContext;
|
||||
struct DummyContext {
|
||||
interpreter_info: InterpreterInfo,
|
||||
}
|
||||
|
||||
impl BuildContext for DummyContext {
|
||||
fn cache(&self) -> &Path {
|
||||
|
@ -28,7 +30,7 @@ impl BuildContext for DummyContext {
|
|||
}
|
||||
|
||||
fn interpreter_info(&self) -> &InterpreterInfo {
|
||||
panic!("The test should not need to build source distributions")
|
||||
&self.interpreter_info
|
||||
}
|
||||
|
||||
fn base_python(&self) -> &Path {
|
||||
|
@ -61,13 +63,29 @@ impl BuildContext for DummyContext {
|
|||
}
|
||||
}
|
||||
|
||||
async fn resolve(
|
||||
manifest: Manifest,
|
||||
markers: &'static MarkerEnvironment,
|
||||
tags: &Tags,
|
||||
) -> Result<Graph> {
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
let build_context = DummyContext {
|
||||
interpreter_info: InterpreterInfo::artificial(
|
||||
Platform::current()?,
|
||||
markers.clone(),
|
||||
PathBuf::from("/dev/null"),
|
||||
PathBuf::from("/dev/null"),
|
||||
),
|
||||
};
|
||||
let resolver = Resolver::new(manifest, markers, tags, &client, &build_context);
|
||||
Ok(resolver.resolve().await?)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn black() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![],
|
||||
|
@ -77,8 +95,7 @@ async fn black() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -89,9 +106,6 @@ async fn black() -> Result<()> {
|
|||
async fn black_colorama() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black[colorama]<=23.9.1").unwrap()],
|
||||
vec![],
|
||||
|
@ -101,8 +115,7 @@ async fn black_colorama() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -113,9 +126,6 @@ async fn black_colorama() -> Result<()> {
|
|||
async fn black_python_310() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![],
|
||||
|
@ -125,8 +135,7 @@ async fn black_python_310() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_310, &TAGS_310, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_310, &TAGS_310).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -139,9 +148,6 @@ async fn black_python_310() -> Result<()> {
|
|||
async fn black_mypy_extensions() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![Requirement::from_str("mypy-extensions<0.4.4").unwrap()],
|
||||
|
@ -151,8 +157,7 @@ async fn black_mypy_extensions() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -165,9 +170,6 @@ async fn black_mypy_extensions() -> Result<()> {
|
|||
async fn black_mypy_extensions_extra() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![Requirement::from_str("mypy-extensions[extra]<0.4.4").unwrap()],
|
||||
|
@ -177,8 +179,7 @@ async fn black_mypy_extensions_extra() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -191,9 +192,6 @@ async fn black_mypy_extensions_extra() -> Result<()> {
|
|||
async fn black_flake8() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![Requirement::from_str("flake8<1").unwrap()],
|
||||
|
@ -203,8 +201,7 @@ async fn black_flake8() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -215,9 +212,6 @@ async fn black_flake8() -> Result<()> {
|
|||
async fn black_lowest() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black>21").unwrap()],
|
||||
vec![],
|
||||
|
@ -227,8 +221,7 @@ async fn black_lowest() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -239,9 +232,6 @@ async fn black_lowest() -> Result<()> {
|
|||
async fn black_lowest_direct() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black>21").unwrap()],
|
||||
vec![],
|
||||
|
@ -251,8 +241,7 @@ async fn black_lowest_direct() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -263,9 +252,6 @@ async fn black_lowest_direct() -> Result<()> {
|
|||
async fn black_respect_preference() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![],
|
||||
|
@ -275,8 +261,7 @@ async fn black_respect_preference() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -287,9 +272,6 @@ async fn black_respect_preference() -> Result<()> {
|
|||
async fn black_ignore_preference() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=23.9.1").unwrap()],
|
||||
vec![],
|
||||
|
@ -299,8 +281,7 @@ async fn black_ignore_preference() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -311,9 +292,6 @@ async fn black_ignore_preference() -> Result<()> {
|
|||
async fn black_disallow_prerelease() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=20.0").unwrap()],
|
||||
vec![],
|
||||
|
@ -323,8 +301,9 @@ async fn black_disallow_prerelease() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let err = resolver.resolve().await.unwrap_err();
|
||||
let err = resolve(manifest, &MARKERS_311, &TAGS_311)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
insta::assert_display_snapshot!(err);
|
||||
|
||||
|
@ -335,9 +314,6 @@ async fn black_disallow_prerelease() -> Result<()> {
|
|||
async fn black_allow_prerelease_if_necessary() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("black<=20.0").unwrap()],
|
||||
vec![],
|
||||
|
@ -347,10 +323,11 @@ async fn black_allow_prerelease_if_necessary() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await.unwrap_err();
|
||||
let err = resolve(manifest, &MARKERS_311, &TAGS_311)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
insta::assert_display_snapshot!(err);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -359,9 +336,6 @@ async fn black_allow_prerelease_if_necessary() -> Result<()> {
|
|||
async fn pylint_disallow_prerelease() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("pylint==2.3.0").unwrap()],
|
||||
vec![],
|
||||
|
@ -371,8 +345,7 @@ async fn pylint_disallow_prerelease() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -383,9 +356,6 @@ async fn pylint_disallow_prerelease() -> Result<()> {
|
|||
async fn pylint_allow_prerelease() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![Requirement::from_str("pylint==2.3.0").unwrap()],
|
||||
vec![],
|
||||
|
@ -395,8 +365,7 @@ async fn pylint_allow_prerelease() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -407,9 +376,6 @@ async fn pylint_allow_prerelease() -> Result<()> {
|
|||
async fn pylint_allow_explicit_prerelease_without_marker() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![
|
||||
Requirement::from_str("pylint==2.3.0").unwrap(),
|
||||
|
@ -422,8 +388,7 @@ async fn pylint_allow_explicit_prerelease_without_marker() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
@ -434,9 +399,6 @@ async fn pylint_allow_explicit_prerelease_without_marker() -> Result<()> {
|
|||
async fn pylint_allow_explicit_prerelease_with_marker() -> Result<()> {
|
||||
colored::control::set_override(false);
|
||||
|
||||
let tempdir = tempdir()?;
|
||||
let client = RegistryClientBuilder::new(tempdir.path()).build();
|
||||
|
||||
let manifest = Manifest::new(
|
||||
vec![
|
||||
Requirement::from_str("pylint==2.3.0").unwrap(),
|
||||
|
@ -449,8 +411,7 @@ async fn pylint_allow_explicit_prerelease_with_marker() -> Result<()> {
|
|||
None,
|
||||
);
|
||||
|
||||
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
|
||||
let resolution = resolver.resolve().await?;
|
||||
let resolution = resolve(manifest, &MARKERS_311, &TAGS_311).await?;
|
||||
|
||||
insta::assert_display_snapshot!(resolution);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue