diff --git a/crates/uv/tests/pip_compile_scenarios.rs b/crates/uv/tests/pip_compile_scenarios.rs index 2cbece6dd..b2fe1fbd5 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/update.py -//! Scenarios from +//! Generated with ./scripts/scenarios/sync.sh +//! 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://test.pypi.org/simple") + .arg("https://astral-sh.github.io/packse/0.3.7/simple-html/") .arg("--find-links") - .arg("https://raw.githubusercontent.com/zanieb/packse/4f39539c1b858e28268554604e75c69e25272e5a/vendor/links.html") + .arg("https://raw.githubusercontent.com/zanieb/packse/0.3.7/vendor/links.html") .arg("--cache-dir") .arg(context.cache_dir.path()) .env("VIRTUAL_ENV", context.venv.as_os_str()) @@ -46,14 +46,12 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { command } -/// requires-incompatible-python-version-compatible-override -/// /// The user requires a package which requires a Python version greater than the /// current version, but they use an alternative Python version for package /// resolution. /// /// ```text -/// 3f4ac9b2 +/// incompatible-python-compatible-override /// ├── environment /// │ └── python3.9 /// ├── root @@ -64,17 +62,17 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { /// └── requires python>=3.10 (incompatible with environment) /// ``` #[test] -fn requires_incompatible_python_version_compatible_override() -> Result<()> { +fn incompatible_python_compatible_override() -> Result<()> { let context = TestContext::new("3.9"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-3f4ac9b2", "albatross")); - filters.push((r"-3f4ac9b2", "")); + filters.push((r"incompatible-python-compatible-override-a", "albatross")); + filters.push((r"incompatible-python-compatible-override-", "pkg-")); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-3f4ac9b2==1.0.0")?; + requirements_in.write_str("incompatible-python-compatible-override-a==1.0.0")?; let output = uv_snapshot!(filters, command(&context, python_versions) .arg("--python-version=3.11") @@ -92,21 +90,18 @@ fn requires_incompatible_python_version_compatible_override() -> Result<()> { "### ); - output - .assert() - .success() - .stdout(predicate::str::contains("a-3f4ac9b2==1.0.0")); + output.assert().success().stdout(predicate::str::contains( + "incompatible-python-compatible-override-a==1.0.0", + )); Ok(()) } -/// requires-compatible-python-version-incompatible-override -/// /// The user requires a package which requires a compatible Python version, but they /// request an incompatible Python version for package resolution. /// /// ```text -/// fd6db412 +/// compatible-python-incompatible-override /// ├── environment /// │ └── python3.11 /// ├── root @@ -117,17 +112,17 @@ fn requires_incompatible_python_version_compatible_override() -> Result<()> { /// └── requires python>=3.10 /// ``` #[test] -fn requires_compatible_python_version_incompatible_override() -> Result<()> { +fn compatible_python_incompatible_override() -> Result<()> { let context = TestContext::new("3.11"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-fd6db412", "albatross")); - filters.push((r"-fd6db412", "")); + filters.push((r"compatible-python-incompatible-override-a", "albatross")); + filters.push((r"compatible-python-incompatible-override-", "pkg-")); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-fd6db412==1.0.0")?; + requirements_in.write_str("compatible-python-incompatible-override-a==1.0.0")?; let output = uv_snapshot!(filters, command(&context, python_versions) .arg("--python-version=3.9") @@ -149,14 +144,12 @@ fn requires_compatible_python_version_incompatible_override() -> Result<()> { Ok(()) } -/// requires-incompatible-python-version-compatible-override-no-wheels -/// /// The user requires a package which requires a incompatible Python version, but /// they request a compatible Python version for package resolution. There are only /// source distributions available for the package. /// /// ```text -/// 3521037f +/// incompatible-python-compatible-override-unavailable-no-wheels /// ├── environment /// │ └── python3.9 /// ├── root @@ -167,17 +160,24 @@ fn requires_compatible_python_version_incompatible_override() -> Result<()> { /// └── requires python>=3.10 (incompatible with environment) /// ``` #[test] -fn requires_incompatible_python_version_compatible_override_no_wheels() -> Result<()> { +fn incompatible_python_compatible_override_unavailable_no_wheels() -> Result<()> { let context = TestContext::new("3.9"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-3521037f", "albatross")); - filters.push((r"-3521037f", "")); + filters.push(( + r"incompatible-python-compatible-override-unavailable-no-wheels-a", + "albatross", + )); + filters.push(( + r"incompatible-python-compatible-override-unavailable-no-wheels-", + "pkg-", + )); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-3521037f==1.0.0")?; + requirements_in + .write_str("incompatible-python-compatible-override-unavailable-no-wheels-a==1.0.0")?; // Since there are no wheels for the package and it is not compatible with the // local installation, we cannot build the source distribution to determine its @@ -202,15 +202,13 @@ fn requires_incompatible_python_version_compatible_override_no_wheels() -> Resul Ok(()) } -/// requires-incompatible-python-version-compatible-override-no-wheels-available-system -/// /// The user requires a package which requires a incompatible Python version, but /// they request a compatible Python version for package resolution. There are only /// source distributions available for the package. The user has a compatible Python /// version installed elsewhere on their system. /// /// ```text -/// c68bcf5c +/// incompatible-python-compatible-override-available-no-wheels /// ├── environment /// │ ├── python3.11 /// │ └── python3.9 (active) @@ -222,18 +220,24 @@ fn requires_incompatible_python_version_compatible_override_no_wheels() -> Resul /// └── requires python>=3.10 (incompatible with environment) /// ``` #[test] -fn requires_incompatible_python_version_compatible_override_no_wheels_available_system( -) -> Result<()> { +fn incompatible_python_compatible_override_available_no_wheels() -> Result<()> { let context = TestContext::new("3.9"); let python_versions = &["3.11"]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-c68bcf5c", "albatross")); - filters.push((r"-c68bcf5c", "")); + filters.push(( + r"incompatible-python-compatible-override-available-no-wheels-a", + "albatross", + )); + filters.push(( + r"incompatible-python-compatible-override-available-no-wheels-", + "pkg-", + )); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-c68bcf5c==1.0.0")?; + requirements_in + .write_str("incompatible-python-compatible-override-available-no-wheels-a==1.0.0")?; // Since there is a compatible Python version available on the system, it should be // used to build the source distributions. @@ -252,22 +256,19 @@ fn requires_incompatible_python_version_compatible_override_no_wheels_available_ "### ); - output - .assert() - .success() - .stdout(predicate::str::contains("a-c68bcf5c==1.0.0")); + output.assert().success().stdout(predicate::str::contains( + "incompatible-python-compatible-override-available-no-wheels-a==1.0.0", + )); Ok(()) } -/// requires-incompatible-python-version-compatible-override-no-compatible-wheels -/// /// The user requires a package which requires a incompatible Python version, but /// they request a compatible Python version for package resolution. There is a /// wheel available for the package, but it does not have a compatible tag. /// /// ```text -/// d7b25a2d +/// incompatible-python-compatible-override-no-compatible-wheels /// ├── environment /// │ └── python3.9 /// ├── root @@ -278,17 +279,24 @@ fn requires_incompatible_python_version_compatible_override_no_wheels_available_ /// └── requires python>=3.10 (incompatible with environment) /// ``` #[test] -fn requires_incompatible_python_version_compatible_override_no_compatible_wheels() -> Result<()> { +fn incompatible_python_compatible_override_no_compatible_wheels() -> Result<()> { let context = TestContext::new("3.9"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-d7b25a2d", "albatross")); - filters.push((r"-d7b25a2d", "")); + filters.push(( + r"incompatible-python-compatible-override-no-compatible-wheels-a", + "albatross", + )); + filters.push(( + r"incompatible-python-compatible-override-no-compatible-wheels-", + "pkg-", + )); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-d7b25a2d==1.0.0")?; + requirements_in + .write_str("incompatible-python-compatible-override-no-compatible-wheels-a==1.0.0")?; // Since there are no compatible wheels for the package and it is not compatible // with the local installation, we cannot build the source distribution to @@ -313,15 +321,13 @@ fn requires_incompatible_python_version_compatible_override_no_compatible_wheels Ok(()) } -/// requires-incompatible-python-version-compatible-override-other-wheel -/// /// The user requires a package which requires a incompatible Python version, but /// they request a compatible Python version for package resolution. There are only /// source distributions available for the compatible version of the package, but /// there is an incompatible version with a wheel available. /// /// ```text -/// a9179f0c +/// incompatible-python-compatible-override-other-wheel /// ├── environment /// │ └── python3.9 /// ├── root @@ -335,17 +341,23 @@ fn requires_incompatible_python_version_compatible_override_no_compatible_wheels /// └── requires python>=3.12 (incompatible with environment) /// ``` #[test] -fn requires_incompatible_python_version_compatible_override_other_wheel() -> Result<()> { +fn incompatible_python_compatible_override_other_wheel() -> Result<()> { let context = TestContext::new("3.9"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-a9179f0c", "albatross")); - filters.push((r"-a9179f0c", "")); + filters.push(( + r"incompatible-python-compatible-override-other-wheel-a", + "albatross", + )); + filters.push(( + r"incompatible-python-compatible-override-other-wheel-", + "pkg-", + )); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-a9179f0c")?; + requirements_in.write_str("incompatible-python-compatible-override-other-wheel-a")?; // Since there are no wheels for the version of the package compatible with the // target and it is not compatible with the local installation, we cannot build the @@ -378,13 +390,11 @@ fn requires_incompatible_python_version_compatible_override_other_wheel() -> Res Ok(()) } -/// requires-python-patch-version-override-no-patch -/// /// The user requires a package which requires a Python version with a patch version /// and the user provides a target version without a patch version. /// /// ```text -/// e1884826 +/// python-patch-override-no-patch /// ├── environment /// │ └── python3.8.18 /// ├── root @@ -395,17 +405,17 @@ fn requires_incompatible_python_version_compatible_override_other_wheel() -> Res /// └── requires python>=3.8.4 /// ``` #[test] -fn requires_python_patch_version_override_no_patch() -> Result<()> { +fn python_patch_override_no_patch() -> Result<()> { let context = TestContext::new("3.8.18"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-e1884826", "albatross")); - filters.push((r"-e1884826", "")); + filters.push((r"python-patch-override-no-patch-a", "albatross")); + filters.push((r"python-patch-override-no-patch-", "pkg-")); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-e1884826==1.0.0")?; + requirements_in.write_str("python-patch-override-no-patch-a==1.0.0")?; // Since the resolver is asked to solve with 3.8, the minimum compatible Python // requirement is treated as 3.8.0. @@ -428,13 +438,11 @@ fn requires_python_patch_version_override_no_patch() -> Result<()> { Ok(()) } -/// requires-python-patch-version-override-patch-compatible -/// /// The user requires a package which requires a Python version with a patch version /// and the user provides a target version with a compatible patch version. /// /// ```text -/// 91b4bcfc +/// python-patch-override-patch-compatible /// ├── environment /// │ └── python3.8.18 /// ├── root @@ -445,17 +453,17 @@ fn requires_python_patch_version_override_no_patch() -> Result<()> { /// └── requires python>=3.8.0 /// ``` #[test] -fn requires_python_patch_version_override_patch_compatible() -> Result<()> { +fn python_patch_override_patch_compatible() -> Result<()> { let context = TestContext::new("3.8.18"); let python_versions = &[]; // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-91b4bcfc", "albatross")); - filters.push((r"-91b4bcfc", "")); + filters.push((r"python-patch-override-patch-compatible-a", "albatross")); + filters.push((r"python-patch-override-patch-compatible-", "pkg-")); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("a-91b4bcfc==1.0.0")?; + requirements_in.write_str("python-patch-override-patch-compatible-a==1.0.0")?; let output = uv_snapshot!(filters, command(&context, python_versions) .arg("--python-version=3.8.0") @@ -473,10 +481,9 @@ fn requires_python_patch_version_override_patch_compatible() -> Result<()> { "### ); - output - .assert() - .success() - .stdout(predicate::str::contains("a-91b4bcfc==1.0.0")); + output.assert().success().stdout(predicate::str::contains( + "python-patch-override-patch-compatible-a==1.0.0", + )); Ok(()) } diff --git a/crates/uv/tests/pip_install_scenarios.rs b/crates/uv/tests/pip_install_scenarios.rs index e0ad34ecb..4ff10eb07 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/update.py -//! Scenarios from +//! Generated with ./scripts/scenarios/sync.sh +//! 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://test.pypi.org/simple") + .arg("https://astral-sh.github.io/packse/0.3.7/simple-html/") .arg("--find-links") - .arg("https://raw.githubusercontent.com/zanieb/packse/4f39539c1b858e28268554604e75c69e25272e5a/vendor/links.html") + .arg("https://raw.githubusercontent.com/zanieb/packse/0.3.7/vendor/links.html") .arg("--cache-dir") .arg(context.cache_dir.path()) .env("VIRTUAL_ENV", context.venv.as_os_str()) @@ -64,12 +64,10 @@ fn command(context: &TestContext) -> Command { command } -/// requires-package-does-not-exist -/// /// The user requires any version of package `a` which does not exist. /// /// ```text -/// 5a1a4a35 +/// requires-package-does-not-exist /// ├── environment /// │ └── python3.8 /// └── root @@ -82,10 +80,10 @@ fn requires_package_does_not_exist() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"-5a1a4a35", "")); + filters.push((r"requires-package-does-not-exist-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-5a1a4a35") + .arg("requires-package-does-not-exist-a") , @r###" success: false exit_code: 1 @@ -93,18 +91,20 @@ fn requires_package_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because a was not found in the package registry and you require a, we can conclude that the requirements are unsatisfiable. + ╰─▶ Because pkg-a was not found in the package registry and you require pkg-a, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_5a1a4a35", &context.temp_dir); + assert_not_installed( + &context.venv, + "requires_package_does_not_exist_a", + &context.temp_dir, + ); } -/// requires-exact-version-does-not-exist -/// /// The user requires an exact version of package `a` but only other versions exist /// /// ```text -/// 7cff23d9 +/// requires-exact-version-does-not-exist /// ├── environment /// │ └── python3.8 /// ├── root @@ -119,11 +119,11 @@ fn requires_exact_version_does_not_exist() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-7cff23d9", "albatross")); - filters.push((r"-7cff23d9", "")); + filters.push((r"requires-exact-version-does-not-exist-a", "albatross")); + filters.push((r"requires-exact-version-does-not-exist-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-7cff23d9==2.0.0") + .arg("requires-exact-version-does-not-exist-a==2.0.0") , @r###" success: false exit_code: 1 @@ -134,16 +134,18 @@ fn requires_exact_version_does_not_exist() { ╰─▶ Because there is no version of albatross==2.0.0 and you require albatross==2.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_7cff23d9", &context.temp_dir); + assert_not_installed( + &context.venv, + "requires_exact_version_does_not_exist_a", + &context.temp_dir, + ); } -/// requires-greater-version-does-not-exist -/// /// The user requires a version of `a` greater than `1.0.0` but only smaller or /// equal versions exist /// /// ```text -/// 63569c9e +/// requires-greater-version-does-not-exist /// ├── environment /// │ └── python3.8 /// ├── root @@ -159,11 +161,11 @@ fn requires_greater_version_does_not_exist() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-63569c9e", "albatross")); - filters.push((r"-63569c9e", "")); + filters.push((r"requires-greater-version-does-not-exist-a", "albatross")); + filters.push((r"requires-greater-version-does-not-exist-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-63569c9e>1.0.0") + .arg("requires-greater-version-does-not-exist-a>1.0.0") , @r###" success: false exit_code: 1 @@ -174,16 +176,18 @@ fn requires_greater_version_does_not_exist() { ╰─▶ Because only albatross<=1.0.0 is available and you require albatross>1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_63569c9e", &context.temp_dir); + assert_not_installed( + &context.venv, + "requires_greater_version_does_not_exist_a", + &context.temp_dir, + ); } -/// requires-less-version-does-not-exist -/// /// The user requires a version of `a` less than `1.0.0` but only larger versions /// exist /// /// ```text -/// 2af6fa02 +/// requires-less-version-does-not-exist /// ├── environment /// │ └── python3.8 /// ├── root @@ -200,11 +204,11 @@ fn requires_less_version_does_not_exist() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-2af6fa02", "albatross")); - filters.push((r"-2af6fa02", "")); + filters.push((r"requires-less-version-does-not-exist-a", "albatross")); + filters.push((r"requires-less-version-does-not-exist-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-2af6fa02<2.0.0") + .arg("requires-less-version-does-not-exist-a<2.0.0") , @r###" success: false exit_code: 1 @@ -215,15 +219,17 @@ fn requires_less_version_does_not_exist() { ╰─▶ Because only albatross>=2.0.0 is available and you require albatross<2.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_2af6fa02", &context.temp_dir); + assert_not_installed( + &context.venv, + "requires_less_version_does_not_exist_a", + &context.temp_dir, + ); } -/// transitive-requires-package-does-not-exist -/// /// The user requires package `a` but `a` requires package `b` which does not exist /// /// ```text -/// 64b04b2b +/// transitive-requires-package-does-not-exist /// ├── environment /// │ └── python3.8 /// ├── root @@ -240,11 +246,11 @@ fn transitive_requires_package_does_not_exist() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-64b04b2b", "albatross")); - filters.push((r"-64b04b2b", "")); + filters.push((r"transitive-requires-package-does-not-exist-a", "albatross")); + filters.push((r"transitive-requires-package-does-not-exist-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-64b04b2b") + .arg("transitive-requires-package-does-not-exist-a") , @r###" success: false exit_code: 1 @@ -252,20 +258,22 @@ fn transitive_requires_package_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because b was not found in the package registry and albatross==1.0.0 depends on b, we can conclude that albatross==1.0.0 cannot be used. + ╰─▶ Because pkg-b was not found in the package registry and albatross==1.0.0 depends on pkg-b, we can conclude that albatross==1.0.0 cannot be used. And because only albatross==1.0.0 is available and you require albatross, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_64b04b2b", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_requires_package_does_not_exist_a", + &context.temp_dir, + ); } -/// excluded-only-version -/// /// Only one version of the requested package is available, but the user has banned /// that version. /// /// ```text -/// 72f0d052 +/// excluded-only-version /// ├── environment /// │ └── python3.8 /// ├── root @@ -280,11 +288,11 @@ fn excluded_only_version() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-72f0d052", "albatross")); - filters.push((r"-72f0d052", "")); + filters.push((r"excluded-only-version-a", "albatross")); + filters.push((r"excluded-only-version-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-72f0d052!=1.0.0") + .arg("excluded-only-version-a!=1.0.0") , @r###" success: false exit_code: 1 @@ -299,16 +307,14 @@ fn excluded_only_version() { "###); // Only `a==1.0.0` is available but the user excluded it. - assert_not_installed(&context.venv, "a_72f0d052", &context.temp_dir); + assert_not_installed(&context.venv, "excluded_only_version_a", &context.temp_dir); } -/// excluded-only-compatible-version -/// /// Only one version of the requested package `a` is compatible, but the user has /// banned that version. /// /// ```text -/// d6ce69da +/// excluded-only-compatible-version /// ├── environment /// │ └── python3.8 /// ├── root @@ -338,13 +344,13 @@ fn excluded_only_compatible_version() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-d6ce69da", "albatross")); - filters.push((r"b-d6ce69da", "bluebird")); - filters.push((r"-d6ce69da", "")); + filters.push((r"excluded-only-compatible-version-a", "albatross")); + filters.push((r"excluded-only-compatible-version-b", "bluebird")); + filters.push((r"excluded-only-compatible-version-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-d6ce69da!=2.0.0") - .arg("b-d6ce69da<3.0.0,>=2.0.0") + .arg("excluded-only-compatible-version-a!=2.0.0") + .arg("excluded-only-compatible-version-b<3.0.0,>=2.0.0") , @r###" success: false exit_code: 1 @@ -373,17 +379,23 @@ fn excluded_only_compatible_version() { // Only `a==1.2.0` is available since `a==1.0.0` and `a==3.0.0` require // incompatible versions of `b`. The user has excluded that version of `a` so // resolution fails. - assert_not_installed(&context.venv, "a_d6ce69da", &context.temp_dir); - assert_not_installed(&context.venv, "b_d6ce69da", &context.temp_dir); + assert_not_installed( + &context.venv, + "excluded_only_compatible_version_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "excluded_only_compatible_version_b", + &context.temp_dir, + ); } -/// dependency-excludes-range-of-compatible-versions -/// /// There is a range of compatible versions for the requested package `a`, but /// another dependency `c` excludes that range. /// /// ```text -/// 5824fb81 +/// dependency-excludes-range-of-compatible-versions /// ├── environment /// │ └── python3.8 /// ├── root @@ -436,15 +448,24 @@ fn dependency_excludes_range_of_compatible_versions() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-5824fb81", "albatross")); - filters.push((r"b-5824fb81", "bluebird")); - filters.push((r"c-5824fb81", "crow")); - filters.push((r"-5824fb81", "")); + filters.push(( + r"dependency-excludes-range-of-compatible-versions-a", + "albatross", + )); + filters.push(( + r"dependency-excludes-range-of-compatible-versions-b", + "bluebird", + )); + filters.push(( + r"dependency-excludes-range-of-compatible-versions-c", + "crow", + )); + filters.push((r"dependency-excludes-range-of-compatible-versions-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-5824fb81") - .arg("b-5824fb81<3.0.0,>=2.0.0") - .arg("c-5824fb81") + .arg("dependency-excludes-range-of-compatible-versions-a") + .arg("dependency-excludes-range-of-compatible-versions-b<3.0.0,>=2.0.0") + .arg("dependency-excludes-range-of-compatible-versions-c") , @r###" success: false exit_code: 1 @@ -465,7 +486,7 @@ fn dependency_excludes_range_of_compatible_versions() { albatross<2.0.0 albatross>=3.0.0 - And because we know from (1) that albatross<2.0.0 depends on bluebird==1.0.0, we can conclude that bluebird!=1.0.0, albatross!=3.0.0, all versions of crow are incompatible. + And because we know from (1) that albatross<2.0.0 depends on bluebird==1.0.0, we can conclude that albatross!=3.0.0, all versions of crow, bluebird!=1.0.0 are incompatible. And because albatross==3.0.0 depends on bluebird==3.0.0, we can conclude that all versions of crow depend on one of: bluebird<=1.0.0 bluebird>=3.0.0 @@ -476,13 +497,23 @@ fn dependency_excludes_range_of_compatible_versions() { // Only the `2.x` versions of `a` are available since `a==1.0.0` and `a==3.0.0` // require incompatible versions of `b`, but all available versions of `c` exclude // that range of `a` so resolution fails. - assert_not_installed(&context.venv, "a_5824fb81", &context.temp_dir); - assert_not_installed(&context.venv, "b_5824fb81", &context.temp_dir); - assert_not_installed(&context.venv, "c_5824fb81", &context.temp_dir); + assert_not_installed( + &context.venv, + "dependency_excludes_range_of_compatible_versions_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "dependency_excludes_range_of_compatible_versions_b", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "dependency_excludes_range_of_compatible_versions_c", + &context.temp_dir, + ); } -/// dependency-excludes-non-contiguous-range-of-compatible-versions -/// /// There is a non-contiguous range of compatible versions for the requested package /// `a`, but another dependency `c` excludes the range. This is the same as /// `dependency-excludes-range-of-compatible-versions` but some of the versions of @@ -490,7 +521,7 @@ fn dependency_excludes_range_of_compatible_versions() { /// `d`. /// /// ```text -/// 119f929b +/// dependency-excludes-non-contiguous-range-of-compatible-versions /// ├── environment /// │ └── python3.8 /// ├── root @@ -551,15 +582,27 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-119f929b", "albatross")); - filters.push((r"b-119f929b", "bluebird")); - filters.push((r"c-119f929b", "crow")); - filters.push((r"-119f929b", "")); + filters.push(( + r"dependency-excludes-non-contiguous-range-of-compatible-versions-a", + "albatross", + )); + filters.push(( + r"dependency-excludes-non-contiguous-range-of-compatible-versions-b", + "bluebird", + )); + filters.push(( + r"dependency-excludes-non-contiguous-range-of-compatible-versions-c", + "crow", + )); + filters.push(( + r"dependency-excludes-non-contiguous-range-of-compatible-versions-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-119f929b") - .arg("b-119f929b<3.0.0,>=2.0.0") - .arg("c-119f929b") + .arg("dependency-excludes-non-contiguous-range-of-compatible-versions-a") + .arg("dependency-excludes-non-contiguous-range-of-compatible-versions-b<3.0.0,>=2.0.0") + .arg("dependency-excludes-non-contiguous-range-of-compatible-versions-c") , @r###" success: false exit_code: 1 @@ -580,7 +623,7 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { albatross<2.0.0 albatross>=3.0.0 - And because we know from (1) that albatross<2.0.0 depends on bluebird==1.0.0, we can conclude that bluebird!=1.0.0, all versions of crow, albatross!=3.0.0 are incompatible. + And because we know from (1) that albatross<2.0.0 depends on bluebird==1.0.0, we can conclude that all versions of crow, albatross!=3.0.0, bluebird!=1.0.0 are incompatible. And because albatross==3.0.0 depends on bluebird==3.0.0, we can conclude that all versions of crow depend on one of: bluebird<=1.0.0 bluebird>=3.0.0 @@ -591,17 +634,27 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { // Only the `2.x` versions of `a` are available since `a==1.0.0` and `a==3.0.0` // require incompatible versions of `b`, but all available versions of `c` exclude // that range of `a` so resolution fails. - assert_not_installed(&context.venv, "a_119f929b", &context.temp_dir); - assert_not_installed(&context.venv, "b_119f929b", &context.temp_dir); - assert_not_installed(&context.venv, "c_119f929b", &context.temp_dir); + assert_not_installed( + &context.venv, + "dependency_excludes_non_contiguous_range_of_compatible_versions_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "dependency_excludes_non_contiguous_range_of_compatible_versions_b", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "dependency_excludes_non_contiguous_range_of_compatible_versions_c", + &context.temp_dir, + ); } -/// extra-required -/// /// Optional dependencies are requested for the package. /// /// ```text -/// c1e0ed38 +/// extra-required /// ├── environment /// │ └── python3.8 /// ├── root @@ -622,12 +675,12 @@ fn extra_required() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-c1e0ed38", "albatross")); - filters.push((r"b-c1e0ed38", "bluebird")); - filters.push((r"-c1e0ed38", "")); + filters.push((r"extra-required-a", "albatross")); + filters.push((r"extra-required-b", "bluebird")); + filters.push((r"extra-required-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-c1e0ed38[extra]") + .arg("extra-required-a[extra]") , @r###" success: true exit_code: 0 @@ -641,17 +694,25 @@ fn extra_required() { + bluebird==1.0.0 "###); - assert_installed(&context.venv, "a_c1e0ed38", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "b_c1e0ed38", "1.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "extra_required_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "extra_required_b", + "1.0.0", + &context.temp_dir, + ); } -/// missing-extra -/// /// Optional dependencies are requested for the package, but the extra does not /// exist. /// /// ```text -/// de25a6db +/// missing-extra /// ├── environment /// │ └── python3.8 /// ├── root @@ -666,11 +727,11 @@ fn missing_extra() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-de25a6db", "albatross")); - filters.push((r"-de25a6db", "")); + filters.push((r"missing-extra-a", "albatross")); + filters.push((r"missing-extra-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-de25a6db[extra]") + .arg("missing-extra-a[extra]") , @r###" success: true exit_code: 0 @@ -684,15 +745,13 @@ fn missing_extra() { "###); // Missing extras are ignored during resolution. - assert_installed(&context.venv, "a_de25a6db", "1.0.0", &context.temp_dir); + assert_installed(&context.venv, "missing_extra_a", "1.0.0", &context.temp_dir); } -/// multiple-extras-required -/// /// Multiple optional dependencies are requested for the package. /// /// ```text -/// 502cbb59 +/// multiple-extras-required /// ├── environment /// │ └── python3.8 /// ├── root @@ -719,13 +778,13 @@ fn multiple_extras_required() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-502cbb59", "albatross")); - filters.push((r"b-502cbb59", "bluebird")); - filters.push((r"c-502cbb59", "crow")); - filters.push((r"-502cbb59", "")); + filters.push((r"multiple-extras-required-a", "albatross")); + filters.push((r"multiple-extras-required-b", "bluebird")); + filters.push((r"multiple-extras-required-c", "crow")); + filters.push((r"multiple-extras-required-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-502cbb59[extra_b,extra_c]") + .arg("multiple-extras-required-a[extra_b,extra_c]") , @r###" success: true exit_code: 0 @@ -740,17 +799,30 @@ fn multiple_extras_required() { + crow==1.0.0 "###); - assert_installed(&context.venv, "a_502cbb59", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "b_502cbb59", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "c_502cbb59", "1.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "multiple_extras_required_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "multiple_extras_required_b", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "multiple_extras_required_c", + "1.0.0", + &context.temp_dir, + ); } -/// all-extras-required -/// /// Multiple optional dependencies are requested for the package via an 'all' extra. /// /// ```text -/// 4cf56e90 +/// all-extras-required /// ├── environment /// │ └── python3.8 /// ├── root @@ -789,13 +861,13 @@ fn all_extras_required() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-4cf56e90", "albatross")); - filters.push((r"b-4cf56e90", "bluebird")); - filters.push((r"c-4cf56e90", "crow")); - filters.push((r"-4cf56e90", "")); + filters.push((r"all-extras-required-a", "albatross")); + filters.push((r"all-extras-required-b", "bluebird")); + filters.push((r"all-extras-required-c", "crow")); + filters.push((r"all-extras-required-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-4cf56e90[all]") + .arg("all-extras-required-a[all]") , @r###" success: true exit_code: 0 @@ -810,18 +882,31 @@ fn all_extras_required() { + crow==1.0.0 "###); - assert_installed(&context.venv, "a_4cf56e90", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "b_4cf56e90", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "c_4cf56e90", "1.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "all_extras_required_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "all_extras_required_b", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "all_extras_required_c", + "1.0.0", + &context.temp_dir, + ); } -/// extra-incompatible-with-extra -/// /// Multiple optional dependencies are requested for the package, but they have /// conflicting requirements with each other. /// /// ```text -/// a5547b80 +/// extra-incompatible-with-extra /// ├── environment /// │ └── python3.8 /// ├── root @@ -847,12 +932,12 @@ fn extra_incompatible_with_extra() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-a5547b80", "albatross")); - filters.push((r"b-a5547b80", "bluebird")); - filters.push((r"-a5547b80", "")); + filters.push((r"extra-incompatible-with-extra-a", "albatross")); + filters.push((r"extra-incompatible-with-extra-b", "bluebird")); + filters.push((r"extra-incompatible-with-extra-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-a5547b80[extra_b,extra_c]") + .arg("extra-incompatible-with-extra-a[extra_b,extra_c]") , @r###" success: false exit_code: 1 @@ -867,15 +952,17 @@ fn extra_incompatible_with_extra() { // Because both `extra_b` and `extra_c` are requested and they require incompatible // versions of `b`, `a` cannot be installed. - assert_not_installed(&context.venv, "a_a5547b80", &context.temp_dir); + assert_not_installed( + &context.venv, + "extra_incompatible_with_extra_a", + &context.temp_dir, + ); } -/// extra-incompatible-with-extra-not-requested -/// /// One of two incompatible optional dependencies are requested for the package. /// /// ```text -/// 8bb31c23 +/// extra-incompatible-with-extra-not-requested /// ├── environment /// │ └── python3.8 /// ├── root @@ -901,12 +988,15 @@ fn extra_incompatible_with_extra_not_requested() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-8bb31c23", "albatross")); - filters.push((r"b-8bb31c23", "bluebird")); - filters.push((r"-8bb31c23", "")); + filters.push(( + r"extra-incompatible-with-extra-not-requested-a", + "albatross", + )); + filters.push((r"extra-incompatible-with-extra-not-requested-b", "bluebird")); + filters.push((r"extra-incompatible-with-extra-not-requested-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-8bb31c23[extra_c]") + .arg("extra-incompatible-with-extra-not-requested-a[extra_c]") , @r###" success: true exit_code: 0 @@ -922,17 +1012,25 @@ fn extra_incompatible_with_extra_not_requested() { // Because the user does not request both extras, it is okay that one is // incompatible with the other. - assert_installed(&context.venv, "a_8bb31c23", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "b_8bb31c23", "2.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "extra_incompatible_with_extra_not_requested_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "extra_incompatible_with_extra_not_requested_b", + "2.0.0", + &context.temp_dir, + ); } -/// extra-incompatible-with-root -/// /// Optional dependencies are requested for the package, but the extra is not /// compatible with other requested versions. /// /// ```text -/// aca6971b +/// extra-incompatible-with-root /// ├── environment /// │ └── python3.8 /// ├── root @@ -956,13 +1054,13 @@ fn extra_incompatible_with_root() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-aca6971b", "albatross")); - filters.push((r"b-aca6971b", "bluebird")); - filters.push((r"-aca6971b", "")); + filters.push((r"extra-incompatible-with-root-a", "albatross")); + filters.push((r"extra-incompatible-with-root-b", "bluebird")); + filters.push((r"extra-incompatible-with-root-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-aca6971b[extra]") - .arg("b-aca6971b==2.0.0") + .arg("extra-incompatible-with-root-a[extra]") + .arg("extra-incompatible-with-root-b==2.0.0") , @r###" success: false exit_code: 1 @@ -976,17 +1074,23 @@ fn extra_incompatible_with_root() { // Because the user requested `b==2.0.0` but the requested extra requires // `b==1.0.0`, the dependencies cannot be satisfied. - assert_not_installed(&context.venv, "a_aca6971b", &context.temp_dir); - assert_not_installed(&context.venv, "b_aca6971b", &context.temp_dir); + assert_not_installed( + &context.venv, + "extra_incompatible_with_root_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "extra_incompatible_with_root_b", + &context.temp_dir, + ); } -/// extra-does-not-exist-backtrack -/// /// Optional dependencies are requested for the package, the extra is only available /// on an older version. /// /// ```text -/// c4307e58 +/// extra-does-not-exist-backtrack /// ├── environment /// │ └── python3.8 /// ├── root @@ -1011,12 +1115,12 @@ fn extra_does_not_exist_backtrack() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-c4307e58", "albatross")); - filters.push((r"b-c4307e58", "bluebird")); - filters.push((r"-c4307e58", "")); + filters.push((r"extra-does-not-exist-backtrack-a", "albatross")); + filters.push((r"extra-does-not-exist-backtrack-b", "bluebird")); + filters.push((r"extra-does-not-exist-backtrack-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-c4307e58[extra]") + .arg("extra-does-not-exist-backtrack-a[extra]") , @r###" success: true exit_code: 0 @@ -1031,15 +1135,18 @@ fn extra_does_not_exist_backtrack() { // The resolver should not backtrack to `a==1.0.0` because missing extras are // allowed during resolution. `b` should not be installed. - assert_installed(&context.venv, "a_c4307e58", "3.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "extra_does_not_exist_backtrack_a", + "3.0.0", + &context.temp_dir, + ); } -/// direct-incompatible-versions -/// /// The user requires two incompatible, existing versions of package `a` /// /// ```text -/// c0e7adfa +/// direct-incompatible-versions /// ├── environment /// │ └── python3.8 /// ├── root @@ -1057,12 +1164,12 @@ fn direct_incompatible_versions() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-c0e7adfa", "albatross")); - filters.push((r"-c0e7adfa", "")); + filters.push((r"direct-incompatible-versions-a", "albatross")); + filters.push((r"direct-incompatible-versions-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-c0e7adfa==1.0.0") - .arg("a-c0e7adfa==2.0.0") + .arg("direct-incompatible-versions-a==1.0.0") + .arg("direct-incompatible-versions-a==2.0.0") , @r###" success: false exit_code: 1 @@ -1073,17 +1180,23 @@ fn direct_incompatible_versions() { ╰─▶ Because you require albatross==1.0.0 and you require albatross==2.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_c0e7adfa", &context.temp_dir); - assert_not_installed(&context.venv, "a_c0e7adfa", &context.temp_dir); + assert_not_installed( + &context.venv, + "direct_incompatible_versions_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "direct_incompatible_versions_a", + &context.temp_dir, + ); } -/// transitive-incompatible-with-root-version -/// /// The user requires packages `a` and `b` but `a` requires a different version of /// `b` /// /// ```text -/// a13da883 +/// transitive-incompatible-with-root-version /// ├── environment /// │ └── python3.8 /// ├── root @@ -1105,13 +1218,13 @@ fn transitive_incompatible_with_root_version() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-a13da883", "albatross")); - filters.push((r"b-a13da883", "bluebird")); - filters.push((r"-a13da883", "")); + filters.push((r"transitive-incompatible-with-root-version-a", "albatross")); + filters.push((r"transitive-incompatible-with-root-version-b", "bluebird")); + filters.push((r"transitive-incompatible-with-root-version-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-a13da883") - .arg("b-a13da883==1.0.0") + .arg("transitive-incompatible-with-root-version-a") + .arg("transitive-incompatible-with-root-version-b==1.0.0") , @r###" success: false exit_code: 1 @@ -1123,17 +1236,23 @@ fn transitive_incompatible_with_root_version() { And because you require albatross and you require bluebird==1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_a13da883", &context.temp_dir); - assert_not_installed(&context.venv, "b_a13da883", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_incompatible_with_root_version_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "transitive_incompatible_with_root_version_b", + &context.temp_dir, + ); } -/// transitive-incompatible-with-transitive -/// /// The user requires package `a` and `b`; `a` and `b` require different versions of /// `c` /// /// ```text -/// ec82e315 +/// transitive-incompatible-with-transitive /// ├── environment /// │ └── python3.8 /// ├── root @@ -1159,14 +1278,14 @@ fn transitive_incompatible_with_transitive() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-ec82e315", "albatross")); - filters.push((r"b-ec82e315", "bluebird")); - filters.push((r"c-ec82e315", "crow")); - filters.push((r"-ec82e315", "")); + filters.push((r"transitive-incompatible-with-transitive-a", "albatross")); + filters.push((r"transitive-incompatible-with-transitive-b", "bluebird")); + filters.push((r"transitive-incompatible-with-transitive-c", "crow")); + filters.push((r"transitive-incompatible-with-transitive-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-ec82e315") - .arg("b-ec82e315") + .arg("transitive-incompatible-with-transitive-a") + .arg("transitive-incompatible-with-transitive-b") , @r###" success: false exit_code: 1 @@ -1175,21 +1294,302 @@ fn transitive_incompatible_with_transitive() { ----- stderr ----- × No solution found when resolving dependencies: ╰─▶ Because only albatross==1.0.0 is available and albatross==1.0.0 depends on crow==1.0.0, we can conclude that all versions of albatross depend on crow==1.0.0. - And because bluebird==1.0.0 depends on crow==2.0.0 and only bluebird==1.0.0 is available, we can conclude that all versions of bluebird and all versions of albatross are incompatible. + And because bluebird==1.0.0 depends on crow==2.0.0 and only bluebird==1.0.0 is available, we can conclude that all versions of albatross and all versions of bluebird are incompatible. And because you require albatross and you require bluebird, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_ec82e315", &context.temp_dir); - assert_not_installed(&context.venv, "b_ec82e315", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_incompatible_with_transitive_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "transitive_incompatible_with_transitive_b", + &context.temp_dir, + ); } -/// package-only-prereleases +/// A simple version constraint should not exclude published versions with local +/// segments. /// +/// ```text +/// local-simple +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a==1.2.3 +/// │ └── satisfied by a-1.2.3+foo +/// └── a +/// └── a-1.2.3+foo +/// ``` +#[test] +fn local_simple() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"local-simple-a", "albatross")); + filters.push((r"local-simple-", "pkg-")); + + uv_snapshot!(filters, command(&context) + .arg("local-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 albatross==1.2.3 and you require albatross==1.2.3, we can conclude that the requirements are unsatisfiable. + "###); + + // The verison '1.2.3+foo' satisfies the constraint '==1.2.3'. + assert_not_installed(&context.venv, "local_simple_a", &context.temp_dir); +} + +/// If there is a 1.2.3 version with an sdist published and no compatible wheels, +/// then the sdist will be used. +/// +/// ```text +/// local-not-used-with-sdist +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a==1.2.3 +/// │ ├── satisfied by a-1.2.3 +/// │ └── satisfied by a-1.2.3+foo +/// └── a +/// ├── a-1.2.3 +/// └── a-1.2.3+foo +/// ``` +#[test] +fn local_not_used_with_sdist() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"local-not-used-with-sdist-a", "albatross")); + filters.push((r"local-not-used-with-sdist-", "pkg-")); + + uv_snapshot!(filters, command(&context) + .arg("local-not-used-with-sdist-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] + + albatross==1.2.3 + "###); + + // The verison '1.2.3' with an sdist satisfies the constraint '==1.2.3'. + assert_not_installed( + &context.venv, + "local_not_used_with_sdist_a", + &context.temp_dir, + ); +} + +/// Even if there is a 1.2.3 version published, if it is unavailable for some reason +/// (no sdist and no compatible wheels in this case), a 1.2.3 version with a local +/// segment should be usable instead. +/// +/// ```text +/// local-used-without-sdist +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a==1.2.3 +/// │ ├── satisfied by a-1.2.3 +/// │ └── satisfied by a-1.2.3+foo +/// └── a +/// ├── a-1.2.3 +/// └── a-1.2.3+foo +/// ``` +#[test] +fn local_used_without_sdist() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"local-used-without-sdist-a", "albatross")); + filters.push((r"local-used-without-sdist-", "pkg-")); + + uv_snapshot!(filters, command(&context) + .arg("local-used-without-sdist-a==1.2.3") + , @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because albatross==1.2.3 is unusable because no wheels are available with a matching Python ABI and you require albatross==1.2.3, we can conclude that the requirements are unsatisfiable. + "###); + + // The verison '1.2.3+foo' satisfies the constraint '==1.2.3'. + assert_not_installed( + &context.venv, + "local_used_without_sdist_a", + &context.temp_dir, + ); +} + +/// Tests that we can select an older version with a local segment when newer +/// versions are incompatible. +/// +/// ```text +/// local-not-latest +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a>=1 +/// │ ├── satisfied by a-1.2.3 +/// │ ├── satisfied by a-1.2.2+foo +/// │ └── satisfied by a-1.2.1+foo +/// └── a +/// ├── a-1.2.3 +/// ├── a-1.2.2+foo +/// └── a-1.2.1+foo +/// ``` +#[test] +fn local_not_latest() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"local-not-latest-a", "albatross")); + filters.push((r"local-not-latest-", "pkg-")); + + uv_snapshot!(filters, command(&context) + .arg("local-not-latest-a>=1") + , @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + albatross==1.2.1+foo + "###); + + assert_installed( + &context.venv, + "local_not_latest_a", + "1.2.1+foo", + &context.temp_dir, + ); +} + +/// A simple version constraint should not exclude published versions with local +/// segments. +/// +/// ```text +/// local-transitive +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ ├── requires a +/// │ │ └── satisfied by a-1.0.0 +/// │ └── requires b==2.0.0+foo +/// │ └── satisfied by b-2.0.0+foo +/// ├── a +/// │ └── a-1.0.0 +/// │ └── requires b==2.0.0 +/// │ └── satisfied by b-2.0.0+foo +/// └── b +/// └── b-2.0.0+foo +/// ``` +#[test] +fn local_transitive() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"local-transitive-a", "albatross")); + filters.push((r"local-transitive-b", "bluebird")); + filters.push((r"local-transitive-", "pkg-")); + + uv_snapshot!(filters, command(&context) + .arg("local-transitive-a") + .arg("local-transitive-b==2.0.0+foo") + , @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because only albatross==1.0.0 is available and albatross==1.0.0 depends on bluebird==2.0.0, we can conclude that all versions of albatross depend on bluebird==2.0.0. + And because you require albatross and you require bluebird==2.0.0+foo, we can conclude that the requirements are unsatisfiable. + "###); + + // The verison '2.0.0+foo' satisfies both ==2.0.0 and ==2.0.0+foo. + assert_not_installed(&context.venv, "local_transitive_a", &context.temp_dir); + assert_not_installed(&context.venv, "local_transitive_b", &context.temp_dir); +} + +/// A transitive dependency has both a non-local and local version published, but +/// the non-local version is unuable. +/// +/// ```text +/// local-transitive-confounding +/// ├── environment +/// │ └── python3.8 +/// ├── root +/// │ └── requires a +/// │ └── satisfied by a-1.0.0 +/// ├── a +/// │ └── a-1.0.0 +/// │ └── requires b==2.0.0 +/// │ ├── satisfied by b-2.0.0 +/// │ └── satisfied by b-2.0.0+foo +/// └── b +/// ├── b-2.0.0 +/// └── b-2.0.0+foo +/// ``` +#[test] +fn local_transitive_confounding() { + let context = TestContext::new("3.8"); + + // In addition to the standard filters, swap out package names for more realistic messages + let mut filters = INSTA_FILTERS.to_vec(); + filters.push((r"local-transitive-confounding-a", "albatross")); + filters.push((r"local-transitive-confounding-b", "bluebird")); + filters.push((r"local-transitive-confounding-", "pkg-")); + + uv_snapshot!(filters, command(&context) + .arg("local-transitive-confounding-a") + , @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because bluebird==2.0.0 is unusable because no wheels are available with a matching Python ABI and albatross==1.0.0 depends on bluebird==2.0.0, we can conclude that albatross==1.0.0 cannot be used. + And because only albatross==1.0.0 is available and you require albatross, we can conclude that the requirements are unsatisfiable. + "###); + + // The verison '1.2.3+foo' satisfies the constraint '==1.2.3'. + assert_not_installed( + &context.venv, + "local_transitive_confounding_a", + &context.temp_dir, + ); +} + /// The user requires any version of package `a` which only has prerelease versions /// available. /// /// ```text -/// 472fcc7e +/// package-only-prereleases /// ├── environment /// │ └── python3.8 /// ├── root @@ -1204,11 +1604,11 @@ fn package_only_prereleases() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-472fcc7e", "albatross")); - filters.push((r"-472fcc7e", "")); + filters.push((r"package-only-prereleases-a", "albatross")); + filters.push((r"package-only-prereleases-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-472fcc7e") + .arg("package-only-prereleases-a") , @r###" success: true exit_code: 0 @@ -1223,16 +1623,19 @@ fn package_only_prereleases() { // Since there are only prerelease versions of `a` available, it should be // installed even though the user did not include a prerelease specifier. - assert_installed(&context.venv, "a_472fcc7e", "1.0.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "package_only_prereleases_a", + "1.0.0a1", + &context.temp_dir, + ); } -/// package-only-prereleases-in-range -/// /// The user requires a version of package `a` which only matches prerelease /// versions but they did not include a prerelease specifier. /// /// ```text -/// 1017748b +/// package-only-prereleases-in-range /// ├── environment /// │ └── python3.8 /// ├── root @@ -1248,11 +1651,11 @@ fn package_only_prereleases_in_range() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-1017748b", "albatross")); - filters.push((r"-1017748b", "")); + filters.push((r"package-only-prereleases-in-range-a", "albatross")); + filters.push((r"package-only-prereleases-in-range-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-1017748b>0.1.0") + .arg("package-only-prereleases-in-range-a>0.1.0") , @r###" success: false exit_code: 1 @@ -1267,17 +1670,19 @@ fn package_only_prereleases_in_range() { // Since there are stable versions of `a` available, prerelease versions should not // be selected without explicit opt-in. - assert_not_installed(&context.venv, "a_1017748b", &context.temp_dir); + assert_not_installed( + &context.venv, + "package_only_prereleases_in_range_a", + &context.temp_dir, + ); } -/// requires-package-only-prereleases-in-range-global-opt-in -/// /// The user requires a version of package `a` which only matches prerelease /// versions. They did not include a prerelease specifier for the package, but they /// opted into prereleases globally. /// /// ```text -/// 95140069 +/// requires-package-only-prereleases-in-range-global-opt-in /// ├── environment /// │ └── python3.8 /// ├── root @@ -1293,12 +1698,18 @@ fn requires_package_only_prereleases_in_range_global_opt_in() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-95140069", "albatross")); - filters.push((r"-95140069", "")); + filters.push(( + r"requires-package-only-prereleases-in-range-global-opt-in-a", + "albatross", + )); + filters.push(( + r"requires-package-only-prereleases-in-range-global-opt-in-", + "pkg-", + )); uv_snapshot!(filters, command(&context) .arg("--prerelease=allow") - .arg("a-95140069>0.1.0") + .arg("requires-package-only-prereleases-in-range-global-opt-in-a>0.1.0") , @r###" success: true exit_code: 0 @@ -1311,16 +1722,19 @@ fn requires_package_only_prereleases_in_range_global_opt_in() { + albatross==1.0.0a1 "###); - assert_installed(&context.venv, "a_95140069", "1.0.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "requires_package_only_prereleases_in_range_global_opt_in_a", + "1.0.0a1", + &context.temp_dir, + ); } -/// requires-package-prerelease-and-final-any -/// /// The user requires any version of package `a` has a prerelease version available /// and an older non-prerelease version. /// /// ```text -/// 909975d8 +/// requires-package-prerelease-and-final-any /// ├── environment /// │ └── python3.8 /// ├── root @@ -1336,11 +1750,11 @@ fn requires_package_prerelease_and_final_any() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-909975d8", "albatross")); - filters.push((r"-909975d8", "")); + filters.push((r"requires-package-prerelease-and-final-any-a", "albatross")); + filters.push((r"requires-package-prerelease-and-final-any-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-909975d8") + .arg("requires-package-prerelease-and-final-any-a") , @r###" success: true exit_code: 0 @@ -1355,16 +1769,19 @@ fn requires_package_prerelease_and_final_any() { // Since the user did not provide a prerelease specifier, the older stable version // should be selected. - assert_installed(&context.venv, "a_909975d8", "0.1.0", &context.temp_dir); + assert_installed( + &context.venv, + "requires_package_prerelease_and_final_any_a", + "0.1.0", + &context.temp_dir, + ); } -/// package-prerelease-specified-only-final-available -/// /// The user requires a version of `a` with a prerelease specifier and only stable /// releases are available. /// /// ```text -/// 6f8bea9f +/// package-prerelease-specified-only-final-available /// ├── environment /// │ └── python3.8 /// ├── root @@ -1383,11 +1800,17 @@ fn package_prerelease_specified_only_final_available() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-6f8bea9f", "albatross")); - filters.push((r"-6f8bea9f", "")); + filters.push(( + r"package-prerelease-specified-only-final-available-a", + "albatross", + )); + filters.push(( + r"package-prerelease-specified-only-final-available-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-6f8bea9f>=0.1.0a1") + .arg("package-prerelease-specified-only-final-available-a>=0.1.0a1") , @r###" success: true exit_code: 0 @@ -1401,16 +1824,19 @@ fn package_prerelease_specified_only_final_available() { "###); // The latest stable version should be selected. - assert_installed(&context.venv, "a_6f8bea9f", "0.3.0", &context.temp_dir); + assert_installed( + &context.venv, + "package_prerelease_specified_only_final_available_a", + "0.3.0", + &context.temp_dir, + ); } -/// package-prerelease-specified-only-prerelease-available -/// /// The user requires a version of `a` with a prerelease specifier and only /// prerelease releases are available. /// /// ```text -/// 48d4bba0 +/// package-prerelease-specified-only-prerelease-available /// ├── environment /// │ └── python3.8 /// ├── root @@ -1429,11 +1855,17 @@ fn package_prerelease_specified_only_prerelease_available() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-48d4bba0", "albatross")); - filters.push((r"-48d4bba0", "")); + filters.push(( + r"package-prerelease-specified-only-prerelease-available-a", + "albatross", + )); + filters.push(( + r"package-prerelease-specified-only-prerelease-available-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-48d4bba0>=0.1.0a1") + .arg("package-prerelease-specified-only-prerelease-available-a>=0.1.0a1") , @r###" success: true exit_code: 0 @@ -1447,16 +1879,19 @@ fn package_prerelease_specified_only_prerelease_available() { "###); // The latest prerelease version should be selected. - assert_installed(&context.venv, "a_48d4bba0", "0.3.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "package_prerelease_specified_only_prerelease_available_a", + "0.3.0a1", + &context.temp_dir, + ); } -/// package-prerelease-specified-mixed-available -/// /// The user requires a version of `a` with a prerelease specifier and both /// prerelease and stable releases are available. /// /// ```text -/// 2b1193a7 +/// package-prerelease-specified-mixed-available /// ├── environment /// │ └── python3.8 /// ├── root @@ -1477,11 +1912,14 @@ fn package_prerelease_specified_mixed_available() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-2b1193a7", "albatross")); - filters.push((r"-2b1193a7", "")); + filters.push(( + r"package-prerelease-specified-mixed-available-a", + "albatross", + )); + filters.push((r"package-prerelease-specified-mixed-available-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-2b1193a7>=0.1.0a1") + .arg("package-prerelease-specified-mixed-available-a>=0.1.0a1") , @r###" success: true exit_code: 0 @@ -1496,16 +1934,19 @@ fn package_prerelease_specified_mixed_available() { // Since the user provided a prerelease specifier, the latest prerelease version // should be selected. - assert_installed(&context.venv, "a_2b1193a7", "1.0.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "package_prerelease_specified_mixed_available_a", + "1.0.0a1", + &context.temp_dir, + ); } -/// package-multiple-prereleases-kinds -/// /// The user requires `a` which has multiple prereleases available with different /// labels. /// /// ```text -/// 72919cf7 +/// package-multiple-prereleases-kinds /// ├── environment /// │ └── python3.8 /// ├── root @@ -1524,11 +1965,11 @@ fn package_multiple_prereleases_kinds() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-72919cf7", "albatross")); - filters.push((r"-72919cf7", "")); + filters.push((r"package-multiple-prereleases-kinds-a", "albatross")); + filters.push((r"package-multiple-prereleases-kinds-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-72919cf7>=1.0.0a1") + .arg("package-multiple-prereleases-kinds-a>=1.0.0a1") , @r###" success: true exit_code: 0 @@ -1542,15 +1983,18 @@ fn package_multiple_prereleases_kinds() { "###); // Release candidates should be the highest precedence prerelease kind. - assert_installed(&context.venv, "a_72919cf7", "1.0.0rc1", &context.temp_dir); + assert_installed( + &context.venv, + "package_multiple_prereleases_kinds_a", + "1.0.0rc1", + &context.temp_dir, + ); } -/// package-multiple-prereleases-numbers -/// /// The user requires `a` which has multiple alphas available. /// /// ```text -/// cecdb92d +/// package-multiple-prereleases-numbers /// ├── environment /// │ └── python3.8 /// ├── root @@ -1569,11 +2013,11 @@ fn package_multiple_prereleases_numbers() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-cecdb92d", "albatross")); - filters.push((r"-cecdb92d", "")); + filters.push((r"package-multiple-prereleases-numbers-a", "albatross")); + filters.push((r"package-multiple-prereleases-numbers-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-cecdb92d>=1.0.0a1") + .arg("package-multiple-prereleases-numbers-a>=1.0.0a1") , @r###" success: true exit_code: 0 @@ -1587,16 +2031,19 @@ fn package_multiple_prereleases_numbers() { "###); // The latest alpha version should be selected. - assert_installed(&context.venv, "a_cecdb92d", "1.0.0a3", &context.temp_dir); + assert_installed( + &context.venv, + "package_multiple_prereleases_numbers_a", + "1.0.0a3", + &context.temp_dir, + ); } -/// transitive-package-only-prereleases -/// /// The user requires any version of package `a` which requires `b` which only has /// prerelease versions available. /// /// ```text -/// e3c94488 +/// transitive-package-only-prereleases /// ├── environment /// │ └── python3.8 /// ├── root @@ -1615,12 +2062,12 @@ fn transitive_package_only_prereleases() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-e3c94488", "albatross")); - filters.push((r"b-e3c94488", "bluebird")); - filters.push((r"-e3c94488", "")); + filters.push((r"transitive-package-only-prereleases-a", "albatross")); + filters.push((r"transitive-package-only-prereleases-b", "bluebird")); + filters.push((r"transitive-package-only-prereleases-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-e3c94488") + .arg("transitive-package-only-prereleases-a") , @r###" success: true exit_code: 0 @@ -1636,17 +2083,25 @@ fn transitive_package_only_prereleases() { // Since there are only prerelease versions of `b` available, it should be selected // even though the user did not opt-in to prereleases. - assert_installed(&context.venv, "a_e3c94488", "0.1.0", &context.temp_dir); - assert_installed(&context.venv, "b_e3c94488", "1.0.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "transitive_package_only_prereleases_a", + "0.1.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_package_only_prereleases_b", + "1.0.0a1", + &context.temp_dir, + ); } -/// transitive-package-only-prereleases-in-range -/// /// The user requires package `a` which has a dependency on a package which only /// matches prerelease versions but they did not include a prerelease specifier. /// /// ```text -/// 20238f1b +/// transitive-package-only-prereleases-in-range /// ├── environment /// │ └── python3.8 /// ├── root @@ -1666,12 +2121,18 @@ fn transitive_package_only_prereleases_in_range() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-20238f1b", "albatross")); - filters.push((r"b-20238f1b", "bluebird")); - filters.push((r"-20238f1b", "")); + filters.push(( + r"transitive-package-only-prereleases-in-range-a", + "albatross", + )); + filters.push(( + r"transitive-package-only-prereleases-in-range-b", + "bluebird", + )); + filters.push((r"transitive-package-only-prereleases-in-range-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-20238f1b") + .arg("transitive-package-only-prereleases-in-range-a") , @r###" success: false exit_code: 1 @@ -1688,17 +2149,19 @@ fn transitive_package_only_prereleases_in_range() { // Since there are stable versions of `b` available, the prerelease version should // not be selected without explicit opt-in. The available version is excluded by // the range requested by the user. - assert_not_installed(&context.venv, "a_20238f1b", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_package_only_prereleases_in_range_a", + &context.temp_dir, + ); } -/// transitive-package-only-prereleases-in-range-opt-in -/// /// The user requires package `a` which has a dependency on a package which only /// matches prerelease versions; the user has opted into allowing prereleases in `b` /// explicitly. /// /// ```text -/// d65d5fdf +/// transitive-package-only-prereleases-in-range-opt-in /// ├── environment /// │ └── python3.8 /// ├── root @@ -1720,13 +2183,22 @@ fn transitive_package_only_prereleases_in_range_opt_in() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-d65d5fdf", "albatross")); - filters.push((r"b-d65d5fdf", "bluebird")); - filters.push((r"-d65d5fdf", "")); + filters.push(( + r"transitive-package-only-prereleases-in-range-opt-in-a", + "albatross", + )); + filters.push(( + r"transitive-package-only-prereleases-in-range-opt-in-b", + "bluebird", + )); + filters.push(( + r"transitive-package-only-prereleases-in-range-opt-in-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-d65d5fdf") - .arg("b-d65d5fdf>0.0.0a1") + .arg("transitive-package-only-prereleases-in-range-opt-in-a") + .arg("transitive-package-only-prereleases-in-range-opt-in-b>0.0.0a1") , @r###" success: true exit_code: 0 @@ -1742,17 +2214,25 @@ fn transitive_package_only_prereleases_in_range_opt_in() { // Since the user included a dependency on `b` with a prerelease specifier, a // prerelease version can be selected. - assert_installed(&context.venv, "a_d65d5fdf", "0.1.0", &context.temp_dir); - assert_installed(&context.venv, "b_d65d5fdf", "1.0.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "transitive_package_only_prereleases_in_range_opt_in_a", + "0.1.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_package_only_prereleases_in_range_opt_in_b", + "1.0.0a1", + &context.temp_dir, + ); } -/// transitive-prerelease-and-stable-dependency -/// /// A transitive dependency has both a prerelease and a stable selector, but can /// only be satisfied by a prerelease /// /// ```text -/// d62255d0 +/// transitive-prerelease-and-stable-dependency /// ├── environment /// │ └── python3.8 /// ├── root @@ -1778,14 +2258,17 @@ fn transitive_prerelease_and_stable_dependency() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-d62255d0", "albatross")); - filters.push((r"b-d62255d0", "bluebird")); - filters.push((r"c-d62255d0", "crow")); - filters.push((r"-d62255d0", "")); + filters.push(( + r"transitive-prerelease-and-stable-dependency-a", + "albatross", + )); + filters.push((r"transitive-prerelease-and-stable-dependency-b", "bluebird")); + filters.push((r"transitive-prerelease-and-stable-dependency-c", "crow")); + filters.push((r"transitive-prerelease-and-stable-dependency-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-d62255d0") - .arg("b-d62255d0") + .arg("transitive-prerelease-and-stable-dependency-a") + .arg("transitive-prerelease-and-stable-dependency-b") , @r###" success: false exit_code: 1 @@ -1800,18 +2283,24 @@ fn transitive_prerelease_and_stable_dependency() { "###); // Since the user did not explicitly opt-in to a prerelease, it cannot be selected. - assert_not_installed(&context.venv, "a_d62255d0", &context.temp_dir); - assert_not_installed(&context.venv, "b_d62255d0", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_b", + &context.temp_dir, + ); } -/// transitive-prerelease-and-stable-dependency-opt-in -/// /// A transitive dependency has both a prerelease and a stable selector, but can /// only be satisfied by a prerelease. The user includes an opt-in to prereleases of /// the transitive dependency. /// /// ```text -/// 0778b0eb +/// transitive-prerelease-and-stable-dependency-opt-in /// ├── environment /// │ └── python3.8 /// ├── root @@ -1840,15 +2329,27 @@ fn transitive_prerelease_and_stable_dependency_opt_in() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-0778b0eb", "albatross")); - filters.push((r"b-0778b0eb", "bluebird")); - filters.push((r"c-0778b0eb", "crow")); - filters.push((r"-0778b0eb", "")); + filters.push(( + r"transitive-prerelease-and-stable-dependency-opt-in-a", + "albatross", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-opt-in-b", + "bluebird", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-opt-in-c", + "crow", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-opt-in-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-0778b0eb") - .arg("b-0778b0eb") - .arg("c-0778b0eb>=0.0.0a1") + .arg("transitive-prerelease-and-stable-dependency-opt-in-a") + .arg("transitive-prerelease-and-stable-dependency-opt-in-b") + .arg("transitive-prerelease-and-stable-dependency-opt-in-c>=0.0.0a1") , @r###" success: true exit_code: 0 @@ -1864,18 +2365,31 @@ fn transitive_prerelease_and_stable_dependency_opt_in() { "###); // Since the user explicitly opted-in to a prerelease for `c`, it can be installed. - assert_installed(&context.venv, "a_0778b0eb", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "b_0778b0eb", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "c_0778b0eb", "2.0.0b1", &context.temp_dir); + assert_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_opt_in_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_opt_in_b", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_opt_in_c", + "2.0.0b1", + &context.temp_dir, + ); } -/// transitive-prerelease-and-stable-dependency-many-versions -/// /// A transitive dependency has both a prerelease and a stable selector, but can /// only be satisfied by a prerelease. There are many prerelease versions. /// /// ```text -/// cc6a6eac +/// transitive-prerelease-and-stable-dependency-many-versions /// ├── environment /// │ └── python3.8 /// ├── root @@ -1926,14 +2440,26 @@ fn transitive_prerelease_and_stable_dependency_many_versions() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-cc6a6eac", "albatross")); - filters.push((r"b-cc6a6eac", "bluebird")); - filters.push((r"c-cc6a6eac", "crow")); - filters.push((r"-cc6a6eac", "")); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-a", + "albatross", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-b", + "bluebird", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-c", + "crow", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-cc6a6eac") - .arg("b-cc6a6eac") + .arg("transitive-prerelease-and-stable-dependency-many-versions-a") + .arg("transitive-prerelease-and-stable-dependency-many-versions-b") , @r###" success: false exit_code: 1 @@ -1943,25 +2469,31 @@ fn transitive_prerelease_and_stable_dependency_many_versions() { × No solution found when resolving dependencies: ╰─▶ Because only albatross==1.0.0 is available and albatross==1.0.0 depends on crow>=2.0.0b1, we can conclude that all versions of albatross depend on crow>=2.0.0b1. And because only crow<2.0.0b1 is available, we can conclude that all versions of albatross depend on crow>3.0.0. - And because bluebird==1.0.0 depends on crow and only bluebird==1.0.0 is available, we can conclude that all versions of albatross and all versions of bluebird are incompatible. + And because bluebird==1.0.0 depends on crow and only bluebird==1.0.0 is available, we can conclude that all versions of bluebird and all versions of albatross are incompatible. And because you require albatross and you require bluebird, we can conclude that the requirements are unsatisfiable. hint: crow was requested with a pre-release marker (e.g., crow>=2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`) "###); // Since the user did not explicitly opt-in to a prerelease, it cannot be selected. - assert_not_installed(&context.venv, "a_cc6a6eac", &context.temp_dir); - assert_not_installed(&context.venv, "b_cc6a6eac", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_many_versions_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_many_versions_b", + &context.temp_dir, + ); } -/// transitive-prerelease-and-stable-dependency-many-versions-holes -/// /// A transitive dependency has both a prerelease and a stable selector, but can /// only be satisfied by a prerelease. There are many prerelease versions and some /// are excluded. /// /// ```text -/// 041e36bc +/// transitive-prerelease-and-stable-dependency-many-versions-holes /// ├── environment /// │ └── python3.8 /// ├── root @@ -2004,14 +2536,26 @@ fn transitive_prerelease_and_stable_dependency_many_versions_holes() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-041e36bc", "albatross")); - filters.push((r"b-041e36bc", "bluebird")); - filters.push((r"c-041e36bc", "crow")); - filters.push((r"-041e36bc", "")); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-holes-a", + "albatross", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-holes-b", + "bluebird", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-holes-c", + "crow", + )); + filters.push(( + r"transitive-prerelease-and-stable-dependency-many-versions-holes-", + "pkg-", + )); uv_snapshot!(filters, command(&context) - .arg("a-041e36bc") - .arg("b-041e36bc") + .arg("transitive-prerelease-and-stable-dependency-many-versions-holes-a") + .arg("transitive-prerelease-and-stable-dependency-many-versions-holes-b") , @r###" success: false exit_code: 1 @@ -2039,17 +2583,23 @@ fn transitive_prerelease_and_stable_dependency_many_versions_holes() { "###); // Since the user did not explicitly opt-in to a prerelease, it cannot be selected. - assert_not_installed(&context.venv, "a_041e36bc", &context.temp_dir); - assert_not_installed(&context.venv, "b_041e36bc", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_many_versions_holes_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "transitive_prerelease_and_stable_dependency_many_versions_holes_b", + &context.temp_dir, + ); } -/// package-only-prereleases-boundary -/// /// The user requires a non-prerelease version of `a` which only has prerelease /// versions available. There are pre-releases on the boundary of their range. /// /// ```text -/// edcef999 +/// package-only-prereleases-boundary /// ├── environment /// │ └── python3.8 /// ├── root @@ -2066,11 +2616,11 @@ fn package_only_prereleases_boundary() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-edcef999", "albatross")); - filters.push((r"-edcef999", "")); + filters.push((r"package-only-prereleases-boundary-a", "albatross")); + filters.push((r"package-only-prereleases-boundary-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-edcef999<0.2.0") + .arg("package-only-prereleases-boundary-a<0.2.0") , @r###" success: true exit_code: 0 @@ -2086,16 +2636,19 @@ fn package_only_prereleases_boundary() { // Since there are only prerelease versions of `a` available, a prerelease is // allowed. Since the user did not explictly request a pre-release, pre-releases at // the boundary should not be selected. - assert_installed(&context.venv, "a_edcef999", "0.1.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "package_only_prereleases_boundary_a", + "0.1.0a1", + &context.temp_dir, + ); } -/// package-prereleases-boundary -/// /// The user requires a non-prerelease version of `a` but has enabled pre-releases. /// There are pre-releases on the boundary of their range. /// /// ```text -/// 6d600873 +/// package-prereleases-boundary /// ├── environment /// │ └── python3.8 /// ├── root @@ -2112,12 +2665,12 @@ fn package_prereleases_boundary() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-6d600873", "albatross")); - filters.push((r"-6d600873", "")); + filters.push((r"package-prereleases-boundary-a", "albatross")); + filters.push((r"package-prereleases-boundary-", "pkg-")); uv_snapshot!(filters, command(&context) .arg("--prerelease=allow") - .arg("a-6d600873<0.2.0") + .arg("package-prereleases-boundary-a<0.2.0") , @r###" success: true exit_code: 0 @@ -2132,16 +2685,19 @@ fn package_prereleases_boundary() { // Since the user did not use a pre-release specifier, pre-releases at the boundary // should not be selected even though pre-releases are allowed. - assert_installed(&context.venv, "a_6d600873", "0.1.0", &context.temp_dir); + assert_installed( + &context.venv, + "package_prereleases_boundary_a", + "0.1.0", + &context.temp_dir, + ); } -/// package-prereleases-global-boundary -/// /// The user requires a non-prerelease version of `a` but has enabled pre-releases. /// There are pre-releases on the boundary of their range. /// /// ```text -/// cf1b8081 +/// package-prereleases-global-boundary /// ├── environment /// │ └── python3.8 /// ├── root @@ -2158,12 +2714,12 @@ fn package_prereleases_global_boundary() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-cf1b8081", "albatross")); - filters.push((r"-cf1b8081", "")); + filters.push((r"package-prereleases-global-boundary-a", "albatross")); + filters.push((r"package-prereleases-global-boundary-", "pkg-")); uv_snapshot!(filters, command(&context) .arg("--prerelease=allow") - .arg("a-cf1b8081<0.2.0") + .arg("package-prereleases-global-boundary-a<0.2.0") , @r###" success: true exit_code: 0 @@ -2178,16 +2734,19 @@ fn package_prereleases_global_boundary() { // Since the user did not use a pre-release specifier, pre-releases at the boundary // should not be selected even though pre-releases are allowed. - assert_installed(&context.venv, "a_cf1b8081", "0.1.0", &context.temp_dir); + assert_installed( + &context.venv, + "package_prereleases_global_boundary_a", + "0.1.0", + &context.temp_dir, + ); } -/// package-prereleases-specifier-boundary -/// /// The user requires a prerelease version of `a`. There are pre-releases on the /// boundary of their range. /// /// ```text -/// 357b9636 +/// package-prereleases-specifier-boundary /// ├── environment /// │ └── python3.8 /// ├── root @@ -2207,11 +2766,11 @@ fn package_prereleases_specifier_boundary() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-357b9636", "albatross")); - filters.push((r"-357b9636", "")); + filters.push((r"package-prereleases-specifier-boundary-a", "albatross")); + filters.push((r"package-prereleases-specifier-boundary-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-357b9636<0.2.0a2") + .arg("package-prereleases-specifier-boundary-a<0.2.0a2") , @r###" success: true exit_code: 0 @@ -2226,15 +2785,18 @@ fn package_prereleases_specifier_boundary() { // Since the user used a pre-release specifier, pre-releases at the boundary should // be selected. - assert_installed(&context.venv, "a_357b9636", "0.2.0a1", &context.temp_dir); + assert_installed( + &context.venv, + "package_prereleases_specifier_boundary_a", + "0.2.0a1", + &context.temp_dir, + ); } -/// requires-python-version-does-not-exist -/// /// The user requires a package which requires a Python version that does not exist /// /// ```text -/// 4486c0e5 +/// python-version-does-not-exist /// ├── environment /// │ └── python3.8 /// ├── root @@ -2242,19 +2804,19 @@ fn package_prereleases_specifier_boundary() { /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 -/// └── requires python>=4.0 (incompatible with environment) +/// └── requires python>=3.30 (incompatible with environment) /// ``` #[test] -fn requires_python_version_does_not_exist() { +fn python_version_does_not_exist() { let context = TestContext::new("3.8"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-4486c0e5", "albatross")); - filters.push((r"-4486c0e5", "")); + filters.push((r"python-version-does-not-exist-a", "albatross")); + filters.push((r"python-version-does-not-exist-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-4486c0e5==1.0.0") + .arg("python-version-does-not-exist-a==1.0.0") , @r###" success: false exit_code: 1 @@ -2262,20 +2824,22 @@ fn requires_python_version_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.8.18) does not satisfy Python>=4.0 and albatross==1.0.0 depends on Python>=4.0, we can conclude that albatross==1.0.0 cannot be used. + ╰─▶ Because the current Python version (3.8.18) does not satisfy Python>=3.30 and albatross==1.0.0 depends on Python>=3.30, we can conclude that albatross==1.0.0 cannot be used. And because you require albatross==1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_4486c0e5", &context.temp_dir); + assert_not_installed( + &context.venv, + "python_version_does_not_exist_a", + &context.temp_dir, + ); } -/// requires-python-version-less-than-current -/// /// The user requires a package which requires a Python version less than the /// current version /// /// ```text -/// d4ea58de +/// python-less-than-current /// ├── environment /// │ └── python3.9 /// ├── root @@ -2286,16 +2850,16 @@ fn requires_python_version_does_not_exist() { /// └── requires python<=3.8 (incompatible with environment) /// ``` #[test] -fn requires_python_version_less_than_current() { +fn python_less_than_current() { let context = TestContext::new("3.9"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-d4ea58de", "albatross")); - filters.push((r"-d4ea58de", "")); + filters.push((r"python-less-than-current-a", "albatross")); + filters.push((r"python-less-than-current-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-d4ea58de==1.0.0") + .arg("python-less-than-current-a==1.0.0") , @r###" success: false exit_code: 1 @@ -2307,16 +2871,18 @@ fn requires_python_version_less_than_current() { And because you require albatross==1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_d4ea58de", &context.temp_dir); + assert_not_installed( + &context.venv, + "python_less_than_current_a", + &context.temp_dir, + ); } -/// requires-python-version-greater-than-current -/// /// The user requires a package which requires a Python version greater than the /// current version /// /// ```text -/// 741c8854 +/// python-greater-than-current /// ├── environment /// │ └── python3.9 /// ├── root @@ -2327,16 +2893,16 @@ fn requires_python_version_less_than_current() { /// └── requires python>=3.10 (incompatible with environment) /// ``` #[test] -fn requires_python_version_greater_than_current() { +fn python_greater_than_current() { let context = TestContext::new("3.9"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-741c8854", "albatross")); - filters.push((r"-741c8854", "")); + filters.push((r"python-greater-than-current-a", "albatross")); + filters.push((r"python-greater-than-current-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-741c8854==1.0.0") + .arg("python-greater-than-current-a==1.0.0") , @r###" success: false exit_code: 1 @@ -2348,16 +2914,18 @@ fn requires_python_version_greater_than_current() { And because you require albatross==1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_741c8854", &context.temp_dir); + assert_not_installed( + &context.venv, + "python_greater_than_current_a", + &context.temp_dir, + ); } -/// requires-python-version-greater-than-current-patch -/// /// The user requires a package which requires a Python version with a patch version /// greater than the current patch version /// /// ```text -/// 0044ac94 +/// python-greater-than-current-patch /// ├── environment /// │ └── python3.8.12 /// ├── root @@ -2368,16 +2936,16 @@ fn requires_python_version_greater_than_current() { /// └── requires python>=3.8.14 (incompatible with environment) /// ``` #[test] -fn requires_python_version_greater_than_current_patch() { +fn python_greater_than_current_patch() { let context = TestContext::new("3.8.12"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-0044ac94", "albatross")); - filters.push((r"-0044ac94", "")); + filters.push((r"python-greater-than-current-patch-a", "albatross")); + filters.push((r"python-greater-than-current-patch-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-0044ac94==1.0.0") + .arg("python-greater-than-current-patch-a==1.0.0") , @r###" success: false exit_code: 1 @@ -2389,16 +2957,18 @@ fn requires_python_version_greater_than_current_patch() { And because you require albatross==1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_0044ac94", &context.temp_dir); + assert_not_installed( + &context.venv, + "python_greater_than_current_patch_a", + &context.temp_dir, + ); } -/// requires-python-version-greater-than-current-many -/// /// The user requires a package which has many versions which all require a Python /// version greater than the current version /// /// ```text -/// da5bd150 +/// python-greater-than-current-many /// ├── environment /// │ └── python3.9 /// ├── root @@ -2431,16 +3001,16 @@ fn requires_python_version_greater_than_current_patch() { /// └── requires python>=3.11 (incompatible with environment) /// ``` #[test] -fn requires_python_version_greater_than_current_many() { +fn python_greater_than_current_many() { let context = TestContext::new("3.9"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-da5bd150", "albatross")); - filters.push((r"-da5bd150", "")); + filters.push((r"python-greater-than-current-many-a", "albatross")); + filters.push((r"python-greater-than-current-many-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-da5bd150==1.0.0") + .arg("python-greater-than-current-many-a==1.0.0") , @r###" success: false exit_code: 1 @@ -2451,16 +3021,18 @@ fn requires_python_version_greater_than_current_many() { ╰─▶ Because there is no version of albatross==1.0.0 and you require albatross==1.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_da5bd150", &context.temp_dir); + assert_not_installed( + &context.venv, + "python_greater_than_current_many_a", + &context.temp_dir, + ); } -/// requires-python-version-greater-than-current-backtrack -/// /// The user requires a package where recent versions require a Python version /// greater than the current version, but an older version is compatible. /// /// ```text -/// 3204bc0a +/// python-greater-than-current-backtrack /// ├── environment /// │ └── python3.9 /// ├── root @@ -2479,16 +3051,16 @@ fn requires_python_version_greater_than_current_many() { /// └── requires python>=3.12 (incompatible with environment) /// ``` #[test] -fn requires_python_version_greater_than_current_backtrack() { +fn python_greater_than_current_backtrack() { let context = TestContext::new("3.9"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-3204bc0a", "albatross")); - filters.push((r"-3204bc0a", "")); + filters.push((r"python-greater-than-current-backtrack-a", "albatross")); + filters.push((r"python-greater-than-current-backtrack-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-3204bc0a") + .arg("python-greater-than-current-backtrack-a") , @r###" success: true exit_code: 0 @@ -2501,16 +3073,19 @@ fn requires_python_version_greater_than_current_backtrack() { + albatross==1.0.0 "###); - assert_installed(&context.venv, "a_3204bc0a", "1.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "python_greater_than_current_backtrack_a", + "1.0.0", + &context.temp_dir, + ); } -/// requires-python-version-greater-than-current-excluded -/// /// The user requires a package where recent versions require a Python version /// greater than the current version, but an excluded older version is compatible. /// /// ```text -/// 874cae6d +/// python-greater-than-current-excluded /// ├── environment /// │ └── python3.9 /// ├── root @@ -2528,16 +3103,16 @@ fn requires_python_version_greater_than_current_backtrack() { /// └── requires python>=3.12 (incompatible with environment) /// ``` #[test] -fn requires_python_version_greater_than_current_excluded() { +fn python_greater_than_current_excluded() { let context = TestContext::new("3.9"); // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-874cae6d", "albatross")); - filters.push((r"-874cae6d", "")); + filters.push((r"python-greater-than-current-excluded-a", "albatross")); + filters.push((r"python-greater-than-current-excluded-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-874cae6d>=2.0.0") + .arg("python-greater-than-current-excluded-a>=2.0.0") , @r###" success: false exit_code: 1 @@ -2565,15 +3140,17 @@ fn requires_python_version_greater_than_current_excluded() { And because you require albatross>=2.0.0, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_874cae6d", &context.temp_dir); + assert_not_installed( + &context.venv, + "python_greater_than_current_excluded_a", + &context.temp_dir, + ); } -/// specific-tag-and-default -/// /// A wheel for a specific platform is available alongside the default. /// /// ```text -/// 8f7a81f1 +/// specific-tag-and-default /// ├── environment /// │ └── python3.8 /// ├── root @@ -2588,11 +3165,11 @@ fn specific_tag_and_default() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-8f7a81f1", "albatross")); - filters.push((r"-8f7a81f1", "")); + filters.push((r"specific-tag-and-default-a", "albatross")); + filters.push((r"specific-tag-and-default-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-8f7a81f1") + .arg("specific-tag-and-default-a") , @r###" success: true exit_code: 0 @@ -2606,12 +3183,10 @@ fn specific_tag_and_default() { "###); } -/// only-wheels -/// /// No source distributions are available, only wheels. /// /// ```text -/// a874f41e +/// only-wheels /// ├── environment /// │ └── python3.8 /// ├── root @@ -2626,11 +3201,11 @@ fn only_wheels() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-a874f41e", "albatross")); - filters.push((r"-a874f41e", "")); + filters.push((r"only-wheels-a", "albatross")); + filters.push((r"only-wheels-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-a874f41e") + .arg("only-wheels-a") , @r###" success: true exit_code: 0 @@ -2644,12 +3219,10 @@ fn only_wheels() { "###); } -/// no-wheels -/// /// No wheels are available, only source distributions. /// /// ```text -/// 0278f343 +/// no-wheels /// ├── environment /// │ └── python3.8 /// ├── root @@ -2664,11 +3237,11 @@ fn no_wheels() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-0278f343", "albatross")); - filters.push((r"-0278f343", "")); + filters.push((r"no-wheels-a", "albatross")); + filters.push((r"no-wheels-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-0278f343") + .arg("no-wheels-a") , @r###" success: true exit_code: 0 @@ -2682,12 +3255,10 @@ fn no_wheels() { "###); } -/// no-wheels-with-matching-platform -/// /// No wheels with matching platform tags are available, just source distributions. /// /// ```text -/// f1a1f15c +/// no-wheels-with-matching-platform /// ├── environment /// │ └── python3.8 /// ├── root @@ -2702,11 +3273,11 @@ fn no_wheels_with_matching_platform() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-f1a1f15c", "albatross")); - filters.push((r"-f1a1f15c", "")); + filters.push((r"no-wheels-with-matching-platform-a", "albatross")); + filters.push((r"no-wheels-with-matching-platform-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-f1a1f15c") + .arg("no-wheels-with-matching-platform-a") , @r###" success: true exit_code: 0 @@ -2720,13 +3291,11 @@ fn no_wheels_with_matching_platform() { "###); } -/// no-sdist-no-wheels-with-matching-platform -/// /// No wheels with matching platform tags are available, nor are any source /// distributions available /// /// ```text -/// 94e293e5 +/// no-sdist-no-wheels-with-matching-platform /// ├── environment /// │ └── python3.8 /// ├── root @@ -2741,11 +3310,11 @@ fn no_sdist_no_wheels_with_matching_platform() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-94e293e5", "albatross")); - filters.push((r"-94e293e5", "")); + filters.push((r"no-sdist-no-wheels-with-matching-platform-a", "albatross")); + filters.push((r"no-sdist-no-wheels-with-matching-platform-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-94e293e5") + .arg("no-sdist-no-wheels-with-matching-platform-a") , @r###" success: false exit_code: 1 @@ -2757,16 +3326,18 @@ fn no_sdist_no_wheels_with_matching_platform() { And because you require albatross, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_94e293e5", &context.temp_dir); + assert_not_installed( + &context.venv, + "no_sdist_no_wheels_with_matching_platform_a", + &context.temp_dir, + ); } -/// no-sdist-no-wheels-with-matching-python -/// /// No wheels with matching Python tags are available, nor are any source /// distributions available /// /// ```text -/// 40fe677d +/// no-sdist-no-wheels-with-matching-python /// ├── environment /// │ └── python3.8 /// ├── root @@ -2781,11 +3352,11 @@ fn no_sdist_no_wheels_with_matching_python() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-40fe677d", "albatross")); - filters.push((r"-40fe677d", "")); + filters.push((r"no-sdist-no-wheels-with-matching-python-a", "albatross")); + filters.push((r"no-sdist-no-wheels-with-matching-python-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-40fe677d") + .arg("no-sdist-no-wheels-with-matching-python-a") , @r###" success: false exit_code: 1 @@ -2797,16 +3368,18 @@ fn no_sdist_no_wheels_with_matching_python() { And because you require albatross, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_40fe677d", &context.temp_dir); + assert_not_installed( + &context.venv, + "no_sdist_no_wheels_with_matching_python_a", + &context.temp_dir, + ); } -/// no-sdist-no-wheels-with-matching-abi -/// /// No wheels with matching ABI tags are available, nor are any source distributions /// available /// /// ```text -/// 8727a9b9 +/// no-sdist-no-wheels-with-matching-abi /// ├── environment /// │ └── python3.8 /// ├── root @@ -2821,11 +3394,11 @@ fn no_sdist_no_wheels_with_matching_abi() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-8727a9b9", "albatross")); - filters.push((r"-8727a9b9", "")); + filters.push((r"no-sdist-no-wheels-with-matching-abi-a", "albatross")); + filters.push((r"no-sdist-no-wheels-with-matching-abi-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-8727a9b9") + .arg("no-sdist-no-wheels-with-matching-abi-a") , @r###" success: false exit_code: 1 @@ -2837,16 +3410,18 @@ fn no_sdist_no_wheels_with_matching_abi() { And because you require albatross, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_8727a9b9", &context.temp_dir); + assert_not_installed( + &context.venv, + "no_sdist_no_wheels_with_matching_abi_a", + &context.temp_dir, + ); } -/// no-wheels-no-build -/// /// No wheels are available, only source distributions but the user has disabled /// builds. /// /// ```text -/// 662cbd94 +/// no-wheels-no-build /// ├── environment /// │ └── python3.8 /// ├── root @@ -2861,13 +3436,13 @@ fn no_wheels_no_build() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-662cbd94", "albatross")); - filters.push((r"-662cbd94", "")); + filters.push((r"no-wheels-no-build-a", "albatross")); + filters.push((r"no-wheels-no-build-", "pkg-")); uv_snapshot!(filters, command(&context) .arg("--only-binary") - .arg("a-662cbd94") - .arg("a-662cbd94") + .arg("no-wheels-no-build-a") + .arg("no-wheels-no-build-a") , @r###" success: false exit_code: 2 @@ -2878,16 +3453,14 @@ fn no_wheels_no_build() { Caused by: Building source distributions is disabled "###); - assert_not_installed(&context.venv, "a_662cbd94", &context.temp_dir); + assert_not_installed(&context.venv, "no_wheels_no_build_a", &context.temp_dir); } -/// only-wheels-no-binary -/// /// No source distributions are available, only wheels but the user has disabled /// using pre-built binaries. /// /// ```text -/// dd137625 +/// only-wheels-no-binary /// ├── environment /// │ └── python3.8 /// ├── root @@ -2902,13 +3475,13 @@ fn only_wheels_no_binary() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-dd137625", "albatross")); - filters.push((r"-dd137625", "")); + filters.push((r"only-wheels-no-binary-a", "albatross")); + filters.push((r"only-wheels-no-binary-", "pkg-")); uv_snapshot!(filters, command(&context) .arg("--no-binary") - .arg("a-dd137625") - .arg("a-dd137625") + .arg("only-wheels-no-binary-a") + .arg("only-wheels-no-binary-a") , @r###" success: false exit_code: 1 @@ -2920,16 +3493,14 @@ fn only_wheels_no_binary() { And because you require albatross, we can conclude that the requirements are unsatisfiable. "###); - assert_not_installed(&context.venv, "a_dd137625", &context.temp_dir); + assert_not_installed(&context.venv, "only_wheels_no_binary_a", &context.temp_dir); } -/// no-build -/// /// Both wheels and source distributions are available, and the user has disabled /// builds. /// /// ```text -/// 9ff1e173 +/// no-build /// ├── environment /// │ └── python3.8 /// ├── root @@ -2944,13 +3515,13 @@ fn no_build() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-9ff1e173", "albatross")); - filters.push((r"-9ff1e173", "")); + filters.push((r"no-build-a", "albatross")); + filters.push((r"no-build-", "pkg-")); uv_snapshot!(filters, command(&context) .arg("--only-binary") - .arg("a-9ff1e173") - .arg("a-9ff1e173") + .arg("no-build-a") + .arg("no-build-a") , @r###" success: true exit_code: 0 @@ -2966,13 +3537,11 @@ fn no_build() { // The wheel should be used for install } -/// no-binary -/// /// Both wheels and source distributions are available, and the user has disabled /// binaries. /// /// ```text -/// 10e961b8 +/// no-binary /// ├── environment /// │ └── python3.8 /// ├── root @@ -2987,13 +3556,13 @@ fn no_binary() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-10e961b8", "albatross")); - filters.push((r"-10e961b8", "")); + filters.push((r"no-binary-a", "albatross")); + filters.push((r"no-binary-", "pkg-")); uv_snapshot!(filters, command(&context) .arg("--no-binary") - .arg("a-10e961b8") - .arg("a-10e961b8") + .arg("no-binary-a") + .arg("no-binary-a") , @r###" success: true exit_code: 0 @@ -3009,13 +3578,11 @@ fn no_binary() { // The source distribution should be used for install } -/// package-only-yanked -/// /// The user requires any version of package `a` which only has yanked versions /// available. /// /// ```text -/// e3de7eb4 +/// package-only-yanked /// ├── environment /// │ └── python3.8 /// ├── root @@ -3030,11 +3597,11 @@ fn package_only_yanked() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-e3de7eb4", "albatross")); - filters.push((r"-e3de7eb4", "")); + filters.push((r"package-only-yanked-a", "albatross")); + filters.push((r"package-only-yanked-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-e3de7eb4") + .arg("package-only-yanked-a") , @r###" success: false exit_code: 1 @@ -3042,21 +3609,19 @@ fn package_only_yanked() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only albatross==1.0.0 is available and albatross==1.0.0 is unusable because it was yanked, we can conclude that all versions of albatross cannot be used. + ╰─▶ Because only albatross==1.0.0 is available and albatross==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that all versions of albatross cannot be used. And because you require albatross, we can conclude that the requirements are unsatisfiable. "###); // Yanked versions should not be installed, even if they are the only one // available. - assert_not_installed(&context.venv, "a_e3de7eb4", &context.temp_dir); + assert_not_installed(&context.venv, "package_only_yanked_a", &context.temp_dir); } -/// package-only-yanked-in-range -/// /// The user requires a version of package `a` which only matches yanked versions. /// /// ```text -/// 84b3720e +/// package-only-yanked-in-range /// ├── environment /// │ └── python3.8 /// ├── root @@ -3072,11 +3637,11 @@ fn package_only_yanked_in_range() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-84b3720e", "albatross")); - filters.push((r"-84b3720e", "")); + filters.push((r"package-only-yanked-in-range-a", "albatross")); + filters.push((r"package-only-yanked-in-range-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-84b3720e>0.1.0") + .arg("package-only-yanked-in-range-a>0.1.0") , @r###" success: false exit_code: 1 @@ -3087,22 +3652,24 @@ fn package_only_yanked_in_range() { ╰─▶ Because only the following versions of albatross are available: albatross<=0.1.0 albatross==1.0.0 - and albatross==1.0.0 is unusable because it was yanked, we can conclude that albatross>0.1.0 cannot be used. + and albatross==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that albatross>0.1.0 cannot be used. And because you require albatross>0.1.0, we can conclude that the requirements are unsatisfiable. "###); // Since there are other versions of `a` available, yanked versions should not be // selected without explicit opt-in. - assert_not_installed(&context.venv, "a_84b3720e", &context.temp_dir); + assert_not_installed( + &context.venv, + "package_only_yanked_in_range_a", + &context.temp_dir, + ); } -/// requires-package-yanked-and-unyanked-any -/// /// The user requires any version of package `a` has a yanked version available and /// an older unyanked version. /// /// ```text -/// 93eac6d7 +/// requires-package-yanked-and-unyanked-any /// ├── environment /// │ └── python3.8 /// ├── root @@ -3118,11 +3685,11 @@ fn requires_package_yanked_and_unyanked_any() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-93eac6d7", "albatross")); - filters.push((r"-93eac6d7", "")); + filters.push((r"requires-package-yanked-and-unyanked-any-a", "albatross")); + filters.push((r"requires-package-yanked-and-unyanked-any-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-93eac6d7") + .arg("requires-package-yanked-and-unyanked-any-a") , @r###" success: true exit_code: 0 @@ -3136,16 +3703,19 @@ fn requires_package_yanked_and_unyanked_any() { "###); // The unyanked version should be selected. - assert_installed(&context.venv, "a_93eac6d7", "0.1.0", &context.temp_dir); + assert_installed( + &context.venv, + "requires_package_yanked_and_unyanked_any_a", + "0.1.0", + &context.temp_dir, + ); } -/// package-yanked-specified-mixed-available -/// /// The user requires any version of `a` and both yanked and unyanked releases are /// available. /// /// ```text -/// 3325916e +/// package-yanked-specified-mixed-available /// ├── environment /// │ └── python3.8 /// ├── root @@ -3164,11 +3734,11 @@ fn package_yanked_specified_mixed_available() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-3325916e", "albatross")); - filters.push((r"-3325916e", "")); + filters.push((r"package-yanked-specified-mixed-available-a", "albatross")); + filters.push((r"package-yanked-specified-mixed-available-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-3325916e>=0.1.0") + .arg("package-yanked-specified-mixed-available-a>=0.1.0") , @r###" success: true exit_code: 0 @@ -3182,16 +3752,19 @@ fn package_yanked_specified_mixed_available() { "###); // The latest unyanked version should be selected. - assert_installed(&context.venv, "a_3325916e", "0.3.0", &context.temp_dir); + assert_installed( + &context.venv, + "package_yanked_specified_mixed_available_a", + "0.3.0", + &context.temp_dir, + ); } -/// transitive-package-only-yanked -/// /// The user requires any version of package `a` which requires `b` which only has /// yanked versions available. /// /// ```text -/// 9ec30fe2 +/// transitive-package-only-yanked /// ├── environment /// │ └── python3.8 /// ├── root @@ -3210,12 +3783,12 @@ fn transitive_package_only_yanked() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-9ec30fe2", "albatross")); - filters.push((r"b-9ec30fe2", "bluebird")); - filters.push((r"-9ec30fe2", "")); + filters.push((r"transitive-package-only-yanked-a", "albatross")); + filters.push((r"transitive-package-only-yanked-b", "bluebird")); + filters.push((r"transitive-package-only-yanked-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-9ec30fe2") + .arg("transitive-package-only-yanked-a") , @r###" success: false exit_code: 1 @@ -3223,23 +3796,25 @@ fn transitive_package_only_yanked() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only bluebird==1.0.0 is available and bluebird==1.0.0 is unusable because it was yanked, we can conclude that all versions of bluebird cannot be used. + ╰─▶ Because only bluebird==1.0.0 is available and bluebird==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that all versions of bluebird cannot be used. And because albatross==0.1.0 depends on bluebird, we can conclude that albatross==0.1.0 cannot be used. And because only albatross==0.1.0 is available and you require albatross, we can conclude that the requirements are unsatisfiable. "###); // Yanked versions should not be installed, even if they are the only one // available. - assert_not_installed(&context.venv, "a_9ec30fe2", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_package_only_yanked_a", + &context.temp_dir, + ); } -/// transitive-package-only-yanked-in-range -/// /// The user requires package `a` which has a dependency on a package which only /// matches yanked versions. /// /// ```text -/// 872d714e +/// transitive-package-only-yanked-in-range /// ├── environment /// │ └── python3.8 /// ├── root @@ -3259,12 +3834,12 @@ fn transitive_package_only_yanked_in_range() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-872d714e", "albatross")); - filters.push((r"b-872d714e", "bluebird")); - filters.push((r"-872d714e", "")); + filters.push((r"transitive-package-only-yanked-in-range-a", "albatross")); + filters.push((r"transitive-package-only-yanked-in-range-b", "bluebird")); + filters.push((r"transitive-package-only-yanked-in-range-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-872d714e") + .arg("transitive-package-only-yanked-in-range-a") , @r###" success: false exit_code: 1 @@ -3275,24 +3850,26 @@ fn transitive_package_only_yanked_in_range() { ╰─▶ Because only the following versions of bluebird are available: bluebird<=0.1 bluebird==1.0.0 - and bluebird==1.0.0 is unusable because it was yanked, we can conclude that bluebird>0.1 cannot be used. + and bluebird==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that bluebird>0.1 cannot be used. And because albatross==0.1.0 depends on bluebird>0.1, we can conclude that albatross==0.1.0 cannot be used. And because only albatross==0.1.0 is available and you require albatross, we can conclude that the requirements are unsatisfiable. "###); // Yanked versions should not be installed, even if they are the only valid version // in a range. - assert_not_installed(&context.venv, "a_872d714e", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_package_only_yanked_in_range_a", + &context.temp_dir, + ); } -/// transitive-package-only-yanked-in-range-opt-in -/// /// The user requires package `a` which has a dependency on a package which only /// matches yanked versions; the user has opted into allowing the yanked version of /// `b` explicitly. /// /// ```text -/// 1bbd5d1b +/// transitive-package-only-yanked-in-range-opt-in /// ├── environment /// │ └── python3.8 /// ├── root @@ -3314,13 +3891,19 @@ fn transitive_package_only_yanked_in_range_opt_in() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-1bbd5d1b", "albatross")); - filters.push((r"b-1bbd5d1b", "bluebird")); - filters.push((r"-1bbd5d1b", "")); + filters.push(( + r"transitive-package-only-yanked-in-range-opt-in-a", + "albatross", + )); + filters.push(( + r"transitive-package-only-yanked-in-range-opt-in-b", + "bluebird", + )); + filters.push((r"transitive-package-only-yanked-in-range-opt-in-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-1bbd5d1b") - .arg("b-1bbd5d1b==1.0.0") + .arg("transitive-package-only-yanked-in-range-opt-in-a") + .arg("transitive-package-only-yanked-in-range-opt-in-b==1.0.0") , @r###" success: true exit_code: 0 @@ -3332,22 +3915,30 @@ fn transitive_package_only_yanked_in_range_opt_in() { Installed 2 packages in [TIME] + albatross==0.1.0 + bluebird==1.0.0 - warning: bluebird==1.0.0 is yanked. + warning: bluebird==1.0.0 is yanked (reason: "Yanked for testing"). "###); // Since the user included a dependency on `b` with an exact specifier, the yanked // version can be selected. - assert_installed(&context.venv, "a_1bbd5d1b", "0.1.0", &context.temp_dir); - assert_installed(&context.venv, "b_1bbd5d1b", "1.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "transitive_package_only_yanked_in_range_opt_in_a", + "0.1.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_package_only_yanked_in_range_opt_in_b", + "1.0.0", + &context.temp_dir, + ); } -/// transitive-yanked-and-unyanked-dependency -/// /// A transitive dependency has both a yanked and an unyanked version, but can only /// be satisfied by a yanked version /// /// ```text -/// eb1ba5f5 +/// transitive-yanked-and-unyanked-dependency /// ├── environment /// │ └── python3.8 /// ├── root @@ -3373,14 +3964,14 @@ fn transitive_yanked_and_unyanked_dependency() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-eb1ba5f5", "albatross")); - filters.push((r"b-eb1ba5f5", "bluebird")); - filters.push((r"c-eb1ba5f5", "crow")); - filters.push((r"-eb1ba5f5", "")); + filters.push((r"transitive-yanked-and-unyanked-dependency-a", "albatross")); + filters.push((r"transitive-yanked-and-unyanked-dependency-b", "bluebird")); + filters.push((r"transitive-yanked-and-unyanked-dependency-c", "crow")); + filters.push((r"transitive-yanked-and-unyanked-dependency-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-eb1ba5f5") - .arg("b-eb1ba5f5") + .arg("transitive-yanked-and-unyanked-dependency-a") + .arg("transitive-yanked-and-unyanked-dependency-b") , @r###" success: false exit_code: 1 @@ -3388,23 +3979,29 @@ fn transitive_yanked_and_unyanked_dependency() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because crow==2.0.0 is unusable because it was yanked and albatross==1.0.0 depends on crow==2.0.0, we can conclude that albatross==1.0.0 cannot be used. + ╰─▶ Because crow==2.0.0 is unusable because it was yanked (reason: Yanked for testing) and albatross==1.0.0 depends on crow==2.0.0, we can conclude that albatross==1.0.0 cannot be used. And because only albatross==1.0.0 is available and you require albatross, we can conclude that the requirements are unsatisfiable. "###); // Since the user did not explicitly select the yanked version, it cannot be used. - assert_not_installed(&context.venv, "a_eb1ba5f5", &context.temp_dir); - assert_not_installed(&context.venv, "b_eb1ba5f5", &context.temp_dir); + assert_not_installed( + &context.venv, + "transitive_yanked_and_unyanked_dependency_a", + &context.temp_dir, + ); + assert_not_installed( + &context.venv, + "transitive_yanked_and_unyanked_dependency_b", + &context.temp_dir, + ); } -/// transitive-yanked-and-unyanked-dependency-opt-in -/// /// A transitive dependency has both a yanked and an unyanked version, but can only /// be satisfied by a yanked. The user includes an opt-in to the yanked version of /// the transitive dependency. /// /// ```text -/// f0760ee9 +/// transitive-yanked-and-unyanked-dependency-opt-in /// ├── environment /// │ └── python3.8 /// ├── root @@ -3432,15 +4029,24 @@ fn transitive_yanked_and_unyanked_dependency_opt_in() { // In addition to the standard filters, swap out package names for more realistic messages let mut filters = INSTA_FILTERS.to_vec(); - filters.push((r"a-f0760ee9", "albatross")); - filters.push((r"b-f0760ee9", "bluebird")); - filters.push((r"c-f0760ee9", "crow")); - filters.push((r"-f0760ee9", "")); + filters.push(( + r"transitive-yanked-and-unyanked-dependency-opt-in-a", + "albatross", + )); + filters.push(( + r"transitive-yanked-and-unyanked-dependency-opt-in-b", + "bluebird", + )); + filters.push(( + r"transitive-yanked-and-unyanked-dependency-opt-in-c", + "crow", + )); + filters.push((r"transitive-yanked-and-unyanked-dependency-opt-in-", "pkg-")); uv_snapshot!(filters, command(&context) - .arg("a-f0760ee9") - .arg("b-f0760ee9") - .arg("c-f0760ee9==2.0.0") + .arg("transitive-yanked-and-unyanked-dependency-opt-in-a") + .arg("transitive-yanked-and-unyanked-dependency-opt-in-b") + .arg("transitive-yanked-and-unyanked-dependency-opt-in-c==2.0.0") , @r###" success: true exit_code: 0 @@ -3453,12 +4059,27 @@ fn transitive_yanked_and_unyanked_dependency_opt_in() { + albatross==1.0.0 + bluebird==1.0.0 + crow==2.0.0 - warning: crow==2.0.0 is yanked. + warning: crow==2.0.0 is yanked (reason: "Yanked for testing"). "###); // Since the user explicitly selected the yanked version of `c`, it can be // installed. - assert_installed(&context.venv, "a_f0760ee9", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "b_f0760ee9", "1.0.0", &context.temp_dir); - assert_installed(&context.venv, "c_f0760ee9", "2.0.0", &context.temp_dir); + assert_installed( + &context.venv, + "transitive_yanked_and_unyanked_dependency_opt_in_a", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_yanked_and_unyanked_dependency_opt_in_b", + "1.0.0", + &context.temp_dir, + ); + assert_installed( + &context.venv, + "transitive_yanked_and_unyanked_dependency_opt_in_c", + "2.0.0", + &context.temp_dir, + ); } diff --git a/scripts/scenarios/.gitignore b/scripts/scenarios/.gitignore deleted file mode 100644 index 701bfbe4a..000000000 --- a/scripts/scenarios/.gitignore +++ /dev/null @@ -1 +0,0 @@ -packse-scenarios diff --git a/scripts/scenarios/generate.py b/scripts/scenarios/generate.py new file mode 100755 index 000000000..42cd1ff87 --- /dev/null +++ b/scripts/scenarios/generate.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 +""" +Generates and updates snapshot test cases from packse scenarios. + +Important: + + This script is the backend called by `./scripts/scenarios/sync.sh`, consider using that + if not developing scenarios. + +Requirements: + + $ uv pip install -r scripts/scenarios/requirements.txt + + Uses `git`, `rustfmt`, and `cargo insta test` requirements from the project. + +Usage: + + Regenerate the scenario test files using the given scenarios: + + $ ./scripts/scenarios/generate.py + + Scenarios can be developed locally with the following workflow: + + Serve scenarios on a local index using packse + + $ packse serve + + Override the uv package index and update the tests + + $ UV_INDEX_URL="http://localhost:3141" ./scripts/scenarios/generate.py + + If an editable version of packse is installed, this script will use its bundled scenarios by default. + + Use + +""" + +import argparse +import importlib.metadata +import logging +import os +import subprocess +import sys +import textwrap +from pathlib import Path + +TOOL_ROOT = Path(__file__).parent +TEMPLATES = TOOL_ROOT / "templates" +INSTALL_TEMPLATE = TEMPLATES / "install.mustache" +COMPILE_TEMPLATE = TEMPLATES / "compile.mustache" +PACKSE = TOOL_ROOT / "packse-scenarios" +REQUIREMENTS = TOOL_ROOT / "requirements.txt" +PROJECT_ROOT = TOOL_ROOT.parent.parent +TESTS = PROJECT_ROOT / "crates" / "uv" / "tests" +INSTALL_TESTS = TESTS / "pip_install_scenarios.rs" +COMPILE_TESTS = TESTS / "pip_compile_scenarios.rs" + +CUTE_NAMES = { + "a": "albatross", + "b": "bluebird", + "c": "crow", + "d": "duck", + "e": "eagle", + "f": "flamingo", + "g": "goose", + "h": "heron", +} + +try: + import packse + import packse.inspect +except ImportError: + print( + f"missing requirement `packse`: install the requirements at {REQUIREMENTS.relative_to(PROJECT_ROOT)}", + file=sys.stderr, + ) + exit(1) + + +try: + import chevron_blue +except ImportError: + print( + f"missing requirement `chevron-blue`: install the requirements at {REQUIREMENTS.relative_to(PROJECT_ROOT)}", + file=sys.stderr, + ) + exit(1) + + +def main(scenarios: list[Path], snapshot_update: bool = True): + # Fetch packse version + packse_version = importlib.metadata.version("packse") + + debug = logging.getLogger().getEffectiveLevel() <= logging.DEBUG + + if not scenarios: + if packse_version == "0.0.0": + path = packse.__development_base_path__ / "scenarios" + if path.exists(): + logging.info( + "Detected development version of packse, using scenarios from %s", + path, + ) + scenarios = path.glob("*.json") + else: + logging.error( + "No scenarios provided. Found development version of packse but is missing scenarios. Is it installed as an editable?" + ) + sys.exit(1) + else: + logging.error("No scenarios provided, nothing to do.") + return + + targets = [] + for target in scenarios: + if target.is_dir(): + targets.extend(target.glob("*.json")) + else: + targets.append(target) + + logging.info("Loading scenario metadata...") + data = packse.inspect.inspect( + targets=targets, + no_hash=True, + ) + + data["scenarios"] = [ + scenario + for scenario in data["scenarios"] + # Drop the example scenario + if scenario["name"] != "example" + ] + + # Wrap the description onto multiple lines + for scenario in data["scenarios"]: + scenario["description_lines"] = textwrap.wrap(scenario["description"], width=80) + + # Wrap the expected explanation onto multiple lines + for scenario in data["scenarios"]: + expected = scenario["expected"] + expected["explanation_lines"] = ( + textwrap.wrap(expected["explanation"], width=80) + if expected["explanation"] + else [] + ) + + # TEMPORARY + # We do not yet support local version identifiers + for scenario in data["scenarios"]: + expected = scenario["expected"] + if ( + scenario["name"].startswith("local-") + and scenario["name"] != "local-not-latest" + ): + expected["satisfiable"] = False + expected[ + "explanation" + ] = "We do not have correct behavior for local version identifiers yet" + + # Generate cute names for each scenario + for scenario in data["scenarios"]: + for package in scenario["packages"]: + package["cute_name"] = CUTE_NAMES[package["name"].rsplit("-")[-1]] + + # Split scenarios into `install` and `compile` cases + install_scenarios = [] + compile_scenarios = [] + + for scenario in data["scenarios"]: + if (scenario["resolver_options"] or {}).get("python") is not None: + compile_scenarios.append(scenario) + else: + install_scenarios.append(scenario) + + for template, tests, scenarios in [ + (INSTALL_TEMPLATE, INSTALL_TESTS, install_scenarios), + (COMPILE_TEMPLATE, COMPILE_TESTS, compile_scenarios), + ]: + data = {"scenarios": scenarios} + + ref = "HEAD" if packse_version == "0.0.0" else packse_version + + # Add generated metadata + data[ + "generated_from" + ] = f"https://github.com/zanieb/packse/tree/{ref}/scenarios" + data["generated_with"] = "./scripts/scenarios/sync.sh" + data[ + "vendor_links" + ] = f"https://raw.githubusercontent.com/zanieb/packse/{ref}/vendor/links.html" + + data["index_url"] = f"https://astral-sh.github.io/packse/{ref}/simple-html/" + + # Render the template + logging.info(f"Rendering template {template.name}") + output = chevron_blue.render( + template=template.read_text(), data=data, no_escape=True, warn=True + ) + + # Update the test files + logging.info( + f"Updating test file at `{tests.relative_to(PROJECT_ROOT)}`...", + ) + with open(tests, "wt") as test_file: + test_file.write(output) + + # Format + logging.info( + "Formatting test file...", + ) + subprocess.check_call( + ["rustfmt", str(tests)], + stderr=subprocess.STDOUT, + stdout=sys.stderr if debug else subprocess.DEVNULL, + ) + + # Update snapshots + if snapshot_update: + logging.info("Updating snapshots...") + env = os.environ.copy() + env["UV_TEST_PYTHON_PATH"] = str(PROJECT_ROOT / "bin") + subprocess.call( + [ + "cargo", + "insta", + "test", + "--features", + "pypi,python", + "--accept", + "--test-runner", + "nextest", + "--test", + tests.with_suffix("").name, + ], + cwd=PROJECT_ROOT, + stderr=subprocess.STDOUT, + stdout=sys.stderr if debug else subprocess.DEVNULL, + env=env, + ) + else: + logging.info("Skipping snapshot update") + + logging.info("Done!") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Generates and updates snapshot test cases from packse scenarios.", + ) + parser.add_argument( + "scenarios", + type=Path, + nargs="*", + help="The scenario files to use", + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="Enable debug logging", + ) + parser.add_argument( + "-q", + "--quiet", + action="store_true", + help="Disable logging", + ) + + parser.add_argument( + "--no-snapshot-update", + action="store_true", + help="Disable automatic snapshot updates", + ) + + args = parser.parse_args() + if args.quiet: + log_level = logging.CRITICAL + elif args.verbose: + log_level = logging.DEBUG + else: + log_level = logging.INFO + + logging.basicConfig(level=log_level, format="%(message)s") + + main(args.scenarios, snapshot_update=not args.no_snapshot_update) diff --git a/scripts/scenarios/requirements.in b/scripts/scenarios/requirements.in new file mode 100644 index 000000000..7727b24e0 --- /dev/null +++ b/scripts/scenarios/requirements.in @@ -0,0 +1,2 @@ +chevron-blue +packse>=0.3.6 diff --git a/scripts/scenarios/requirements.txt b/scripts/scenarios/requirements.txt index 5e8b1763a..ed02038c3 100644 --- a/scripts/scenarios/requirements.txt +++ b/scripts/scenarios/requirements.txt @@ -1,4 +1,69 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile scripts/scenarios/requirements.in -o scripts/scenarios/requirements.txt --refresh-package packse --upgrade +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests chevron-blue==0.2.1 -packse @ git+https://github.com/zanieb/packse -waitress @ git+https://github.com/zanieb/waitress@d6d764bcc970e1e50486153588eda8a92cf5b5e4 -devpi-server @ git+https://github.com/zanieb/devpi@22f71acb8f08a59a098e7ad434cf388a1193fc24#subdirectory=server + # via packse +docutils==0.20.1 + # via readme-renderer +editables==0.5 + # via hatchling +hatchling==1.21.1 + # via packse +idna==3.6 + # via requests +importlib-metadata==7.0.1 + # via twine +jaraco-classes==3.3.1 + # via keyring +keyring==24.3.1 + # via twine +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +more-itertools==10.2.0 + # via jaraco-classes +msgspec==0.18.6 + # via packse +nh3==0.2.15 + # via readme-renderer +packaging==23.2 + # via hatchling +packse==0.3.7 +pathspec==0.12.1 + # via hatchling +pkginfo==1.10.0 + # via twine +pluggy==1.4.0 + # via hatchling +pygments==2.17.2 + # via + # readme-renderer + # rich +readme-renderer==43.0 + # via twine +requests==2.31.0 + # via + # requests-toolbelt + # twine +requests-toolbelt==1.0.0 + # via twine +rfc3986==2.0.0 + # via twine +rich==13.7.1 + # via twine +setuptools==69.1.1 + # via packse +trove-classifiers==2024.3.3 + # via hatchling +twine==4.0.2 + # via packse +urllib3==2.2.1 + # via + # requests + # twine +zipp==3.17.0 + # via importlib-metadata diff --git a/scripts/scenarios/sync.sh b/scripts/scenarios/sync.sh new file mode 100755 index 000000000..bf6e4772b --- /dev/null +++ b/scripts/scenarios/sync.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# +# Sync test scenarios with the pinned version of packse. +# +# Usage: +# +# Install the pinned packse version in a temporary virtual environment, fetch scenarios, and regenerate test cases and snapshots: +# +# $ ./scripts/scenarios/sync.sh +# +# Additional arguments are passed to `./scripts/scenarios/generate.py`, for example: +# +# $ ./scripts/scenarios/sync.sh --verbose --no-snapshot-update +# +# For development purposes, the `./scripts/scenarios/generate.py` script can be used directly to generate +# test cases from a local set of scenarios. + +set -eu + +script_root="$(realpath "$(dirname "$0")")" + +cd "$script_root" +echo "Setting up a temporary environment..." +uv venv + +source ".venv/bin/activate" +uv pip install -r requirements.txt --refresh-package packse + +echo "Fetching packse scenarios..." +packse fetch --dest "$script_root/scenarios" --force + +python "$script_root/generate.py" "$script_root/scenarios" "$@" + +# Cleanup +rm -r "$script_root/scenarios" diff --git a/scripts/scenarios/templates/compile.mustache b/scripts/scenarios/templates/compile.mustache index 98d820e97..bae6e7090 100644 --- a/scripts/scenarios/templates/compile.mustache +++ b/scripts/scenarios/templates/compile.mustache @@ -27,7 +27,7 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { .arg("compile") .arg("requirements.in") .arg("--index-url") - .arg("https://test.pypi.org/simple") + .arg("{{index_url}}") .arg("--find-links") .arg("{{vendor_links}}") .arg("--cache-dir") @@ -48,14 +48,12 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { {{#scenarios}} -/// {{name}} -/// {{#description_lines}} /// {{.}} {{/description_lines}} /// /// ```text -/// {{version}} +/// {{name}} {{#tree}} /// {{.}} {{/tree}} @@ -70,7 +68,7 @@ fn {{module_name}}() -> Result<()> { {{#packages}} filters.push((r"{{name}}", "{{cute_name}}")); {{/packages}} - filters.push((r"-{{version}}", "")); + filters.push((r"{{name}}-", "pkg-")); let requirements_in = context.temp_dir.child("requirements.in"); {{#root.requires}} @@ -86,11 +84,11 @@ fn {{module_name}}() -> Result<()> { {{/resolver_options.prereleases}} {{#resolver_options.no_build}} .arg("--only-binary") - .arg("{{.}}-{{version}}") + .arg("{{.}}") {{/resolver_options.no_build}} {{#resolver_options.no_binary}} .arg("--no-binary") - .arg("{{.}}-{{version}}") + .arg("{{.}}") {{/resolver_options.no_binary}} {{#resolver_options.python}} .arg("--python-version={{.}}") diff --git a/scripts/scenarios/templates/install.mustache b/scripts/scenarios/templates/install.mustache index b3a3811f1..80567ff8d 100644 --- a/scripts/scenarios/templates/install.mustache +++ b/scripts/scenarios/templates/install.mustache @@ -47,7 +47,7 @@ fn command(context: &TestContext) -> Command { .arg("pip") .arg("install") .arg("--index-url") - .arg("https://test.pypi.org/simple") + .arg("{{index_url}}") .arg("--find-links") .arg("{{vendor_links}}") .arg("--cache-dir") @@ -67,14 +67,12 @@ fn command(context: &TestContext) -> Command { {{#scenarios}} -/// {{name}} -/// {{#description_lines}} /// {{.}} {{/description_lines}} /// /// ```text -/// {{version}} +/// {{name}} {{#tree}} /// {{.}} {{/tree}} @@ -88,7 +86,7 @@ fn {{module_name}}() { {{#packages}} filters.push((r"{{name}}", "{{cute_name}}")); {{/packages}} - filters.push((r"-{{version}}", "")); + filters.push((r"{{name}}-", "pkg-")); uv_snapshot!(filters, command(&context) {{#resolver_options.prereleases}} @@ -96,11 +94,11 @@ fn {{module_name}}() { {{/resolver_options.prereleases}} {{#resolver_options.no_build}} .arg("--only-binary") - .arg("{{.}}-{{version}}") + .arg("{{.}}") {{/resolver_options.no_build}} {{#resolver_options.no_binary}} .arg("--no-binary") - .arg("{{.}}-{{version}}") + .arg("{{.}}") {{/resolver_options.no_binary}} {{#root.requires}} .arg("{{requirement}}") diff --git a/scripts/scenarios/update.py b/scripts/scenarios/update.py deleted file mode 100755 index e97fcccca..000000000 --- a/scripts/scenarios/update.py +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env python3 -""" -Generates and updates snapshot test cases from packse scenarios. - -Usage: - - Regenerate the scenario test file: - - $ ./scripts/scenarios/update.py - - Scenarios are pinned to a specific commit. Change the `PACKSE_COMMIT` constant to update them. - - Scenarios can be developed locally with the following workflow: - - Install the local version of packse - - $ pip install -e - - From the packse repository, build and publish the scenarios to a local index - - $ packse index up --bg - $ packse build scenarios/* - $ packse publish dist/* --index-url http://localhost:3141/packages/local --anonymous - - Override the default PyPI index for uv and update the scenarios - - $ UV_INDEX_URL="http://localhost:3141/packages/all/+simple" ./scripts/scenarios/update.py - -Requirements: - - Requires `packse` and `chevron-blue`. - - $ pip install -r scripts/scenarios/requirements.txt - - Also supports a local, editable requirement on `packse`. - - Uses `git`, `rustfmt`, and `cargo insta test` requirements from the project. -""" - -import json -import shutil -import subprocess -import sys -import textwrap -from pathlib import Path - - -PACKSE_COMMIT = "4f39539c1b858e28268554604e75c69e25272e5a" -TOOL_ROOT = Path(__file__).parent -TEMPLATES = TOOL_ROOT / "templates" -INSTALL_TEMPLATE = TEMPLATES / "install.mustache" -COMPILE_TEMPLATE = TEMPLATES / "compile.mustache" -PACKSE = TOOL_ROOT / "packse-scenarios" -REQUIREMENTS = TOOL_ROOT / "requirements.txt" -PROJECT_ROOT = TOOL_ROOT.parent.parent -TESTS = PROJECT_ROOT / "crates" / "uv" / "tests" -INSTALL_TESTS = TESTS / "pip_install_scenarios.rs" -COMPILE_TESTS = TESTS / "pip_compile_scenarios.rs" - -CUTE_NAMES = { - "a": "albatross", - "b": "bluebird", - "c": "crow", - "d": "duck", - "e": "eagle", - "f": "flamingo", - "g": "goose", - "h": "heron", -} - -try: - import packse -except ImportError: - print( - f"missing requirement `packse`: install the requirements at {REQUIREMENTS.relative_to(PROJECT_ROOT)}", - file=sys.stderr, - ) - exit(1) - - -try: - import chevron_blue -except ImportError: - print( - f"missing requirement `chevron-blue`: install the requirements at {REQUIREMENTS.relative_to(PROJECT_ROOT)}", - file=sys.stderr, - ) - exit(1) - - -if packse.__development_base_path__.name != "packse": - # Not a local editable installation, download latest scenarios - if PACKSE.exists(): - shutil.rmtree(PACKSE) - - print("Downloading scenarios from packse repository...", file=sys.stderr) - # Perform a sparse checkout where we only grab the `scenarios` folder - subprocess.check_call( - [ - "git", - "clone", - "-n", - "--depth=1", - "--filter=tree:0", - "https://github.com/zanieb/packse", - str(PACKSE), - ], - cwd=TOOL_ROOT, - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, - ) - subprocess.check_call( - ["git", "sparse-checkout", "set", "--no-cone", "scenarios"], - cwd=PACKSE, - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, - ) - subprocess.check_call( - ["git", "checkout", PACKSE_COMMIT], - cwd=PACKSE, - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, - ) - scenarios_path = str(PACKSE / "scenarios") - commit = PACKSE_COMMIT - -else: - print( - f"Using scenarios in packse repository at {packse.__development_base_path__}", - file=sys.stderr, - ) - scenarios_path = str(packse.__development_base_path__ / "scenarios") - - # Get the commit from the repository - commit = ( - subprocess.check_output( - ["git", "show", "-s", "--format=%H", "HEAD"], - cwd=packse.__development_base_path__, - ) - .decode() - .strip() - ) - - if commit != PACKSE_COMMIT: - print(f"WARNING: Expected commit {PACKSE_COMMIT!r} but found {commit!r}.") - -print("Loading scenario metadata...", file=sys.stderr) -data = json.loads( - subprocess.check_output( - [ - sys.executable, - "-m", - "packse", - "inspect", - "--short-names", - scenarios_path, - ], - ) -) - - -data["scenarios"] = [ - scenario - for scenario in data["scenarios"] - # Drop the example scenario - if scenario["name"] != "example" -] - -# Wrap the description onto multiple lines -for scenario in data["scenarios"]: - scenario["description_lines"] = textwrap.wrap(scenario["description"], width=80) - - -# Wrap the expected explanation onto multiple lines -for scenario in data["scenarios"]: - expected = scenario["expected"] - expected["explanation_lines"] = ( - textwrap.wrap(expected["explanation"], width=80) - if expected["explanation"] - else [] - ) - -# Generate cute names for each scenario -for scenario in data["scenarios"]: - for package in scenario["packages"]: - package["cute_name"] = CUTE_NAMES[package["name"][0]] - - -# Split scenarios into `install` and `compile` cases -install_scenarios = [] -compile_scenarios = [] - -for scenario in data["scenarios"]: - if (scenario["resolver_options"] or {}).get("python") is not None: - compile_scenarios.append(scenario) - else: - install_scenarios.append(scenario) - -for template, tests, scenarios in [ - (INSTALL_TEMPLATE, INSTALL_TESTS, install_scenarios), - (COMPILE_TEMPLATE, COMPILE_TESTS, compile_scenarios), -]: - data = {"scenarios": scenarios} - - # Add generated metadata - data["generated_from"] = f"https://github.com/zanieb/packse/tree/{commit}/scenarios" - data["generated_with"] = " ".join(sys.argv) - data[ - "vendor_links" - ] = f"https://raw.githubusercontent.com/zanieb/packse/{commit}/vendor/links.html" - - # Render the template - print(f"Rendering template {template.name}", file=sys.stderr) - output = chevron_blue.render( - template=template.read_text(), data=data, no_escape=True, warn=True - ) - - # Update the test files - print( - f"Updating test file at `{tests.relative_to(PROJECT_ROOT)}`...", - file=sys.stderr, - ) - with open(tests, "wt") as test_file: - test_file.write(output) - - # Format - print( - "Formatting test file...", - file=sys.stderr, - ) - subprocess.check_call(["rustfmt", str(tests)]) - - # Update snapshots - print("Updating snapshots...\n", file=sys.stderr) - subprocess.call( - [ - "cargo", - "insta", - "test", - "--features", - "pypi,python", - "--accept", - "--test-runner", - "nextest", - "--test", - tests.with_suffix("").name, - ], - cwd=PROJECT_ROOT, - ) - -print("\nDone!", file=sys.stderr)