From fbb8bc1f6f7003b649eab677fd0deeb38452ae3b Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 14 Mar 2024 16:26:31 -0700 Subject: [PATCH] Pull in `packse` tests for post releases (#2468) ## Summary Like local versions, a few of these failures and have fixups in the generation script. --- crates/uv-resolver/src/pubgrub/specifier.rs | 1 + crates/uv/tests/pip_compile_scenarios.rs | 6 +- crates/uv/tests/pip_install_scenarios.rs | 381 +++++++++++++++++++- scripts/scenarios/generate.py | 29 +- scripts/scenarios/requirements.in | 2 +- scripts/scenarios/requirements.txt | 4 +- 6 files changed, 412 insertions(+), 11 deletions(-) diff --git a/crates/uv-resolver/src/pubgrub/specifier.rs b/crates/uv-resolver/src/pubgrub/specifier.rs index 4fd65f3bc..536e1c70a 100644 --- a/crates/uv-resolver/src/pubgrub/specifier.rs +++ b/crates/uv-resolver/src/pubgrub/specifier.rs @@ -62,6 +62,7 @@ impl TryFrom<&VersionSpecifier> for PubGrubSpecifier { Operator::GreaterThan => { // Per PEP 440: "The exclusive ordered comparison >V MUST NOT allow a post-release of // the given version unless V itself is a post release." + // TODO(charlie): This needs to exclude post and local releases. let version = specifier.version().clone(); Range::strictly_higher_than(version) } diff --git a/crates/uv/tests/pip_compile_scenarios.rs b/crates/uv/tests/pip_compile_scenarios.rs index 8d0387d6f..8b2250cb9 100644 --- a/crates/uv/tests/pip_compile_scenarios.rs +++ b/crates/uv/tests/pip_compile_scenarios.rs @@ -1,7 +1,7 @@ //! DO NOT EDIT //! //! Generated with ./scripts/scenarios/sync.sh -//! Scenarios from +//! Scenarios from //! #![cfg(all(feature = "python", feature = "pypi"))] @@ -27,9 +27,9 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { .arg("compile") .arg("requirements.in") .arg("--index-url") - .arg("https://astral-sh.github.io/packse/0.3.9/simple-html/") + .arg("https://astral-sh.github.io/packse/0.3.10/simple-html/") .arg("--find-links") - .arg("https://raw.githubusercontent.com/zanieb/packse/0.3.9/vendor/links.html") + .arg("https://raw.githubusercontent.com/zanieb/packse/0.3.10/vendor/links.html") .arg("--cache-dir") .arg(context.cache_dir.path()) .env("VIRTUAL_ENV", context.venv.as_os_str()) diff --git a/crates/uv/tests/pip_install_scenarios.rs b/crates/uv/tests/pip_install_scenarios.rs index 8eb072293..1c76e6f26 100644 --- a/crates/uv/tests/pip_install_scenarios.rs +++ b/crates/uv/tests/pip_install_scenarios.rs @@ -1,7 +1,7 @@ //! DO NOT EDIT //! //! Generated with ./scripts/scenarios/sync.sh -//! Scenarios from +//! Scenarios from //! #![cfg(all(feature = "python", feature = "pypi"))] @@ -46,9 +46,9 @@ fn command(context: &TestContext) -> Command { .arg("pip") .arg("install") .arg("--index-url") - .arg("https://astral-sh.github.io/packse/0.3.9/simple-html/") + .arg("https://astral-sh.github.io/packse/0.3.10/simple-html/") .arg("--find-links") - .arg("https://raw.githubusercontent.com/zanieb/packse/0.3.9/vendor/links.html") + .arg("https://raw.githubusercontent.com/zanieb/packse/0.3.10/vendor/links.html") .arg("--cache-dir") .arg(context.cache_dir.path()) .env("VIRTUAL_ENV", context.venv.as_os_str()) @@ -2026,6 +2026,381 @@ fn local_less_than_or_equal() { ); } +/// A simple version constraint should not match a post-release version. +/// +/// ```text +/// post-simple +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a==1.2.3 +/// │ └── unsatisfied: no matching version +/// └── a +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_simple() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-simple-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-simple-a==1.2.3") + , @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because there is no version of package-a==1.2.3 and you require package-a==1.2.3, we can conclude that the requirements are unsatisfiable. + "###); + + assert_not_installed(&context.venv, "post_simple_a", &context.temp_dir); +} + +/// A greater-than-or-equal version constraint should match a post-release version. +/// +/// ```text +/// post-greater-than-or-equal +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>=1.2.3 +/// │ └── satisfied by a-1.2.3.post1 +/// └── a +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_greater_than_or_equal() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-greater-than-or-equal-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-greater-than-or-equal-a>=1.2.3") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3.post1 + "###); + + // The version '1.2.3.post1' satisfies the constraint '>=1.2.3'. + assert_installed( + &context.venv, + "post_greater_than_or_equal_a", + "1.2.3.post1", + &context.temp_dir, + ); +} + +/// A greater-than version constraint should not match a post-release version. +/// +/// ```text +/// post-greater-than +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>1.2.3 +/// │ └── unsatisfied: no matching version +/// └── a +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_greater_than() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-greater-than-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-greater-than-a>1.2.3") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3.post1 + "###); + + assert_installed( + &context.venv, + "post_greater_than_a", + "1.2.3.post1", + &context.temp_dir, + ); +} + +/// A greater-than version constraint should match a post-release version if the +/// constraint is itself a post-release version. +/// +/// ```text +/// post-greater-than-post +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>1.2.3.post0 +/// │ └── satisfied by a-1.2.3.post1 +/// └── a +/// ├── a-1.2.3.post0 +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_greater_than_post() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-greater-than-post-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-greater-than-post-a>1.2.3.post0") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3.post1 + "###); + + // The version '1.2.3.post1' satisfies the constraint '>1.2.3.post0'. + assert_installed( + &context.venv, + "post_greater_than_post_a", + "1.2.3.post1", + &context.temp_dir, + ); +} + +/// A greater-than-or-equal version constraint should match a post-release version +/// if the constraint is itself a post-release version. +/// +/// ```text +/// post-greater-than-or-equal-post +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>=1.2.3.post0 +/// │ ├── satisfied by a-1.2.3.post0 +/// │ └── satisfied by a-1.2.3.post1 +/// └── a +/// ├── a-1.2.3.post0 +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_greater_than_or_equal_post() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-greater-than-or-equal-post-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-greater-than-or-equal-post-a>=1.2.3.post0") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3.post1 + "###); + + // The version '1.2.3.post1' satisfies the constraint '>=1.2.3.post0'. + assert_installed( + &context.venv, + "post_greater_than_or_equal_post_a", + "1.2.3.post1", + &context.temp_dir, + ); +} + +/// A less-than-or-equal version constraint should not match a post-release version. +/// +/// ```text +/// post-less-than-or-equal +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a<=1.2.3 +/// │ └── unsatisfied: no matching version +/// └── a +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_less_than_or_equal() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-less-than-or-equal-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-less-than-or-equal-a<=1.2.3") + , @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because only package-a>1.2.3 is available and you require package-a<=1.2.3, we can conclude that the requirements are unsatisfiable. + "###); + + assert_not_installed( + &context.venv, + "post_less_than_or_equal_a", + &context.temp_dir, + ); +} + +/// A less-than version constraint should not match a post-release version. +/// +/// ```text +/// post-less-than +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a<1.2.3 +/// │ └── unsatisfied: no matching version +/// └── a +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_less_than() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-less-than-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-less-than-a<1.2.3") + , @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because only package-a>=1.2.3 is available and you require package-a<1.2.3, we can conclude that the requirements are unsatisfiable. + "###); + + assert_not_installed(&context.venv, "post_less_than_a", &context.temp_dir); +} + +/// A greater-than version constraint should not match a post-release version with a +/// local version identifier. +/// +/// ```text +/// post-local-greater-than +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>1.2.3 +/// │ └── unsatisfied: no matching version +/// └── a +/// ├── a-1.2.3.post1 +/// └── a-1.2.3.post1+local +/// ``` +#[test] +fn post_local_greater_than() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-local-greater-than-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-local-greater-than-a>1.2.3") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3.post1+local + "###); + + assert_installed( + &context.venv, + "post_local_greater_than_a", + "1.2.3.post1+local", + &context.temp_dir, + ); +} + +/// A greater-than version constraint should not match a post-release version with a +/// local version identifier. +/// +/// ```text +/// post-local-greater-than-post +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>1.2.3.post0 +/// │ └── satisfied by a-1.2.3.post1 +/// └── a +/// ├── a-1.2.3.post0 +/// ├── a-1.2.3.post0+local +/// └── a-1.2.3.post1 +/// ``` +#[test] +fn post_local_greater_than_post() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for shorter messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"post-local-greater-than-post-", "package-")); + + uv_snapshot!(filters, command(&context) + .arg("post-local-greater-than-post-a>1.2.3.post0") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + package-a==1.2.3.post1 + "###); + + // The version '1.2.3.post1' satisfies the constraint '>1.2.3.post0'. + assert_installed( + &context.venv, + "post_local_greater_than_post_a", + "1.2.3.post1", + &context.temp_dir, + ); +} + /// The user requires any version of package `a` which only has prerelease versions /// available. /// diff --git a/scripts/scenarios/generate.py b/scripts/scenarios/generate.py index 636f1cbab..d6c8539ed 100755 --- a/scripts/scenarios/generate.py +++ b/scripts/scenarios/generate.py @@ -133,10 +133,11 @@ def main(scenarios: list[Path], snapshot_update: bool = True): else [] ) - # TEMPORARY - # We do not yet support local version identifiers + for scenario in data["scenarios"]: expected = scenario["expected"] + + # TODO(charlie): We do not yet support local version identifiers if scenario["name"] in ( "local-less-than-or-equal", "local-simple", @@ -180,6 +181,30 @@ def main(scenarios: list[Path], snapshot_update: bool = True): expected["explanation"] = ( "We do not have correct behavior for local version identifiers yet" ) + elif scenario["name"] == 'post-greater-than': + expected["satisfiable"] = True + expected["packages"] = [ + { + "name": "post-greater-than-a", + "version": "1.2.3.post1", + "module_name": "post_greater_than_a", + } + ] + expected["explanation"] = ( + "We do not have correct behavior for local version identifiers yet" + ) + elif scenario["name"] == 'post-local-greater-than': + expected["satisfiable"] = True + expected["packages"] = [ + { + "name": "post-local-greater-than-a", + "version": "1.2.3.post1+local", + "module_name": "post_local_greater_than_a", + } + ] + expected["explanation"] = ( + "We do not have correct behavior for local version identifiers yet" + ) # Split scenarios into `install` and `compile` cases install_scenarios = [] diff --git a/scripts/scenarios/requirements.in b/scripts/scenarios/requirements.in index fa36647ef..578705400 100644 --- a/scripts/scenarios/requirements.in +++ b/scripts/scenarios/requirements.in @@ -1,2 +1,2 @@ chevron-blue -packse>=0.3.9 +packse>=0.3.10 diff --git a/scripts/scenarios/requirements.txt b/scripts/scenarios/requirements.txt index ef7cce1bc..718578c23 100644 --- a/scripts/scenarios/requirements.txt +++ b/scripts/scenarios/requirements.txt @@ -32,7 +32,7 @@ nh3==0.2.15 # via readme-renderer packaging==24.0 # via hatchling -packse==0.3.9 +packse==0.3.10 pathspec==0.12.1 # via hatchling pkginfo==1.10.0 @@ -65,5 +65,5 @@ urllib3==2.2.1 # via # requests # twine -zipp==3.18.0 +zipp==3.18.1 # via importlib-metadata