diff --git a/.github/workflows/check-generated-files.yml b/.github/workflows/check-generated-files.yml index cb23413af3..37c59814cb 100644 --- a/.github/workflows/check-generated-files.yml +++ b/.github/workflows/check-generated-files.yml @@ -33,6 +33,10 @@ jobs: run: cargo dev generate-all --mode dry-run - name: "Check sysconfig mappings" run: cargo dev generate-sysconfig-metadata --mode check + - name: "Install Rustfmt" + run: rustup component add rustfmt + - name: "Check Packse scenario tests" + run: cargo dev generate-scenario-tests --mode check - name: "Check JSON schema" if: ${{ inputs.schema-changed == 'true' }} run: cargo dev generate-json-schema --mode check diff --git a/Cargo.lock b/Cargo.lock index 2de67e7c40..bd22412f43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1485,7 +1485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2354,7 +2354,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2415,7 +2415,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4148,7 +4148,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4205,7 +4205,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4910,7 +4910,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6262,7 +6262,9 @@ dependencies = [ "uv-git", "uv-installer", "uv-macros", + "uv-normalize", "uv-options-metadata", + "uv-pep440", "uv-pep508", "uv-performance-memory-allocator", "uv-preview", @@ -6270,6 +6272,7 @@ dependencies = [ "uv-python", "uv-settings", "uv-static", + "uv-test", "uv-workspace", "walkdir", ] @@ -7243,7 +7246,10 @@ dependencies = [ "anyhow", "assert_cmd", "assert_fs", + "astral-tokio-tar", + "astral_async_zip", "base64", + "flate2", "fs-err", "futures", "ignore", @@ -7253,17 +7259,27 @@ dependencies = [ "predicates", "regex", "reqwest", + "serde", + "serde_json", + "sha2", "similar 3.1.0", "tempfile", "tokio", + "tokio-util", + "toml", "uv-cache", "uv-client", "uv-configuration", + "uv-distribution-filename", "uv-fs", + "uv-normalize", + "uv-pep440", + "uv-pep508", "uv-preview", "uv-python", "uv-static", "uv-version", + "wiremock", ] [[package]] @@ -7709,7 +7725,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/crates/uv-dev/Cargo.toml b/crates/uv-dev/Cargo.toml index 3f04db3487..0b282a160f 100644 --- a/crates/uv-dev/Cargo.toml +++ b/crates/uv-dev/Cargo.toml @@ -18,7 +18,7 @@ workspace = true uv-cache = { workspace = true, features = ["clap"] } uv-cli = { workspace = true } uv-client = { workspace = true } -uv-configuration = { workspace = true } +uv-configuration = { workspace = true, features = ["clap"] } uv-distribution-filename = { workspace = true } uv-distribution-types = { workspace = true } uv-errors = { workspace = true } @@ -26,13 +26,16 @@ uv-extract = { workspace = true } uv-git = { workspace = true } uv-installer = { workspace = true } uv-macros = { workspace = true } +uv-normalize = { workspace = true } uv-options-metadata = { workspace = true } +uv-pep440 = { workspace = true } uv-pep508 = { workspace = true } uv-pypi-types = { workspace = true } uv-preview = { workspace = true } uv-python = { workspace = true } uv-settings = { workspace = true, features = ["schemars"] } uv-static = { workspace = true } +uv-test = { workspace = true } uv-workspace = { workspace = true, features = ["schemars"] } # Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace diff --git a/crates/uv-dev/src/generate_scenarios.rs b/crates/uv-dev/src/generate_scenarios.rs new file mode 100644 index 0000000000..f6cd149238 --- /dev/null +++ b/crates/uv-dev/src/generate_scenarios.rs @@ -0,0 +1,1020 @@ +//! Generate the Packse scenario integration tests. + +use std::fmt::Write as _; +use std::path::Path; +use std::process::{Command, Stdio}; + +use anstream::println; +use anyhow::{Context, Result, bail}; +use clap::ValueEnum; +use itertools::Itertools; +use pretty_assertions::StrComparison; +use walkdir::WalkDir; + +use uv_normalize::PackageName; +use uv_pep440::Version; +use uv_pep508::{Requirement, VersionOrUrl}; +use uv_test::packse::scenario::{Package, PackageMetadata, Scenario}; + +use crate::ROOT_DIR; +use crate::generate_all::Mode; + +const GENERATED_FROM: &str = "test/scenarios"; +const GENERATED_WITH: &str = "cargo dev generate-scenario-tests"; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[arg(long, default_value_t, value_enum)] + mode: Mode, + + /// Regenerate only the selected scenario test files. + #[arg(long, value_enum)] + templates: Vec, + + /// Skip `cargo insta test --accept` after refreshing generated Rust files. + #[arg(long)] + no_snapshot_update: bool, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)] +enum TemplateKind { + Install, + Compile, + Lock, +} + +impl TemplateKind { + const ALL: [Self; 3] = [Self::Install, Self::Compile, Self::Lock]; + + fn test_file(self) -> &'static str { + match self { + Self::Install => "crates/uv/tests/it/pip_install_scenarios.rs", + Self::Compile => "crates/uv/tests/it/pip_compile_scenarios.rs", + Self::Lock => "crates/uv/tests/it/lock_scenarios.rs", + } + } + + fn test_name(self) -> &'static str { + match self { + Self::Install => "pip_install_scenarios", + Self::Compile => "pip_compile_scenarios", + Self::Lock => "lock_scenarios", + } + } +} + +struct ScenarioCase { + scenario: Scenario, + path: String, +} + +pub(crate) fn main(args: &Args) -> Result<()> { + let scenarios = load_scenarios()?; + println!("Loaded {} scenarios from {GENERATED_FROM}", scenarios.len()); + + let selected_templates = if args.templates.is_empty() { + TemplateKind::ALL.to_vec() + } else { + args.templates.clone() + }; + + for template in selected_templates { + let cases = scenarios_for_template(template, &scenarios); + let output = format_rust_source(&render(template, &cases)?)?; + let path = Path::new(ROOT_DIR).join(template.test_file()); + + match args.mode { + Mode::Check => check_generated_file(&path, &output)?, + Mode::DryRun => println!("{output}"), + Mode::Write => { + println!("Updating: {}", template.test_file()); + fs_err::write(&path, output.as_bytes()) + .with_context(|| format!("failed to write {}", path.display()))?; + + if args.no_snapshot_update { + println!("Skipping snapshots for {}", template.test_name()); + } else { + update_snapshots(template)?; + } + } + } + } + + Ok(()) +} + +fn load_scenarios() -> Result> { + let scenarios_dir = Path::new(ROOT_DIR).join(GENERATED_FROM); + load_scenarios_from(&scenarios_dir) +} + +fn load_scenarios_from(scenarios_dir: &Path) -> Result> { + let mut entries = Vec::new(); + for entry in WalkDir::new(scenarios_dir) { + let entry = entry.with_context(|| { + format!( + "failed to read scenarios from directory `{}`", + scenarios_dir.display() + ) + })?; + if entry.file_type().is_file() + && entry + .path() + .extension() + .is_some_and(|extension| extension == "toml") + { + entries.push(entry.into_path()); + } + } + entries.sort(); + + entries + .into_iter() + .map(|path| { + let scenario = Scenario::from_path(&path)?; + let relative = path.strip_prefix(scenarios_dir).with_context(|| { + format!("scenario path was outside {}", scenarios_dir.display()) + })?; + Ok(ScenarioCase { + scenario, + path: path_to_forward_slashes(relative), + }) + }) + .collect() +} + +fn path_to_forward_slashes(path: &Path) -> String { + path.components() + .map(|component| component.as_os_str().to_string_lossy()) + .join("/") +} + +fn scenarios_for_template( + template: TemplateKind, + scenarios: &[ScenarioCase], +) -> Vec<&ScenarioCase> { + scenarios + .iter() + .filter(|case| match template { + TemplateKind::Install => { + !case.scenario.resolver_options.universal + && case.scenario.resolver_options.python.is_none() + } + TemplateKind::Compile => { + !case.scenario.resolver_options.universal + && case.scenario.resolver_options.python.is_some() + } + TemplateKind::Lock => case.scenario.resolver_options.universal, + }) + .collect() +} + +fn format_rust_file(path: &Path) -> Result<()> { + let status = Command::new("rustfmt") + .arg(path) + .stdout(Stdio::null()) + .status() + .context("failed to run rustfmt")?; + if !status.success() { + bail!("rustfmt failed for {}", path.display()); + } + Ok(()) +} + +fn format_rust_source(output: &str) -> Result { + let temporary_directory = tempfile::Builder::new() + .prefix(".generate-scenario-tests-") + .tempdir_in(ROOT_DIR) + .context("failed to create temporary directory for rustfmt")?; + let path = temporary_directory.path().join("scenarios.rs"); + fs_err::write(&path, output.as_bytes()) + .with_context(|| format!("failed to write {}", path.display()))?; + format_rust_file(&path)?; + fs_err::read_to_string(&path).with_context(|| format!("failed to read {}", path.display())) +} + +fn check_generated_file(path: &Path, output: &str) -> Result<()> { + let filename = path + .strip_prefix(ROOT_DIR) + .unwrap_or(path) + .to_string_lossy(); + let output = normalize_inline_snapshots(output)?; + match fs_err::read_to_string(path) { + Ok(current) => { + let current = normalize_inline_snapshots(¤t)?; + if current == output { + println!("Up-to-date: {filename}"); + Ok(()) + } else { + let comparison = StrComparison::new(¤t, &output); + bail!("{filename} changed, please run `{GENERATED_WITH}`:\n{comparison}"); + } + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + bail!("{filename} not found, please run `{GENERATED_WITH}`"); + } + Err(error) => { + bail!("{filename} changed, please run `{GENERATED_WITH}`:\n{error}"); + } + } +} + +// Normalize only inline snapshots emitted by this generator; scenario prose is arbitrary text. +const INLINE_SNAPSHOT_MARKERS: [&str; 3] = [ + "\n , @", + "\n uv_snapshot!(filters, cmd, @", + "\n lock, @", +]; + +fn normalize_inline_snapshots(source: &str) -> Result { + let mut output = String::with_capacity(source.len()); + let mut remaining = source; + + while let Some((start, marker)) = INLINE_SNAPSHOT_MARKERS + .iter() + .filter_map(|marker| remaining.find(marker).map(|start| (start, *marker))) + .min_by_key(|(start, _)| *start) + { + let snapshot_start = start + marker.len() - 1; + output.push_str(&remaining[..snapshot_start]); + let snapshot = &remaining[snapshot_start..]; + let Some((opening_length, closing_delimiter)) = inline_snapshot_delimiters(snapshot) else { + bail!("invalid inline snapshot in generated scenario file"); + }; + let contents = &snapshot[opening_length..]; + let Some(end) = contents.find(&closing_delimiter) else { + bail!("unterminated inline snapshot in generated scenario file"); + }; + output.push_str("@\"\""); + remaining = &contents[end + closing_delimiter.len()..]; + } + + output.push_str(remaining); + Ok(output) +} + +fn inline_snapshot_delimiters(snapshot: &str) -> Option<(usize, String)> { + if snapshot.starts_with("@\"") { + return Some((2, "\"".to_string())); + } + let raw = snapshot.strip_prefix("@r")?; + let hashes = raw.bytes().take_while(|byte| *byte == b'#').count(); + if raw.as_bytes().get(hashes) != Some(&b'"') { + return None; + } + Some((hashes + 3, format!("\"{}", "#".repeat(hashes)))) +} + +fn update_snapshots(template: TemplateKind) -> Result<()> { + println!("Updating snapshots for {}", template.test_name()); + let status = Command::new("cargo") + .args([ + "insta", + "test", + "--features", + "test-python,test-pypi,test-python-patch", + "--accept", + "--test-runner", + "nextest", + "--test", + "it", + "--", + template.test_name(), + ]) + .current_dir(ROOT_DIR) + .status() + .context("failed to run cargo insta")?; + if !status.success() { + bail!("snapshot update failed for {}", template.test_name()); + } + Ok(()) +} + +fn render(template: TemplateKind, cases: &[&ScenarioCase]) -> Result { + let mut output = String::new(); + match template { + TemplateKind::Install => render_install(&mut output, cases)?, + TemplateKind::Compile => render_compile(&mut output, cases)?, + TemplateKind::Lock => render_lock(&mut output, cases)?, + } + Ok(output) +} + +fn render_header(output: &mut String) { + output.push_str("//! DO NOT EDIT\n"); + output.push_str("//!\n"); + writeln!(output, "//! Generated with `{GENERATED_WITH}`").unwrap(); + writeln!(output, "//! Scenarios from <{GENERATED_FROM}>").unwrap(); + output.push_str("//!\n"); +} + +fn render_install(output: &mut String, cases: &[&ScenarioCase]) -> Result<()> { + render_header(output); + output.push_str("#![cfg(all(feature = \"test-python\", feature = \"test-pypi\", unix))]\n\n"); + output.push_str("use std::process::Command;\n\n"); + output.push_str("use uv_static::EnvVars;\n"); + output.push_str("use uv_test::packse::PackseServer;\n"); + output.push_str("use uv_test::{TestContext, uv_snapshot};\n\n"); + output + .push_str("/// Create a `pip install` command with options shared across all scenarios.\n"); + output.push_str("fn command(context: &TestContext, server: &PackseServer) -> Command {\n"); + output.push_str(" let mut command = context.pip_install();\n"); + output.push_str(" command.arg(\"--index-url\").arg(server.index_url());\n"); + output.push_str(" command.env_remove(EnvVars::UV_EXCLUDE_NEWER);\n"); + output.push_str(" command\n"); + output.push_str("}\n\n"); + + for case in cases { + render_install_case(output, case)?; + } + Ok(()) +} + +fn render_install_case(output: &mut String, case: &ScenarioCase) -> Result<()> { + render_case_docs(output, &case.scenario)?; + if case.scenario.name.contains("patch") { + output.push_str("#[cfg(feature = \"test-python-patch\")]\n"); + } + output.push_str("#[test]\n"); + writeln!(output, "fn {}() {{", module_name(&case.scenario.name)).unwrap(); + writeln!( + output, + " let context = uv_test::test_context!(\"{}\");", + case.scenario.environment.python + ) + .unwrap(); + writeln!( + output, + " let server = PackseServer::new(\"{}\");", + case.path + ) + .unwrap(); + output.push('\n'); + output.push_str(" uv_snapshot!(context.filters(), command(&context, &server)\n"); + render_resolver_args(output, &case.scenario, ScenarioCommand::Install)?; + output.push_str(" , @r#\"\n"); + output.push_str(" \"#);\n\n"); + render_expected_explanation(output, &case.scenario, " // "); + render_install_assertions(output, &case.scenario); + output.push_str("}\n\n"); + Ok(()) +} + +fn render_compile(output: &mut String, cases: &[&ScenarioCase]) -> Result<()> { + render_header(output); + output.push_str("#![cfg(all(feature = \"test-python\", feature = \"test-pypi\", unix))]\n\n"); + output.push_str("use std::process::Command;\n\n"); + output.push_str("use anyhow::Result;\n"); + output.push_str("use assert_cmd::assert::OutputAssertExt;\n"); + output.push_str("use assert_fs::fixture::{FileWriteStr, PathChild};\n"); + output.push_str("use predicates::prelude::predicate;\n\n"); + output.push_str("use uv_static::EnvVars;\n"); + output.push_str("use uv_test::packse::PackseServer;\n"); + output.push_str( + "use uv_test::{TestContext, get_bin, python_path_with_versions, uv_snapshot};\n\n", + ); + output.push_str("/// Provision python binaries and return a `pip compile` command with options shared across all scenarios.\n"); + output.push_str("fn command(context: &TestContext, python_versions: &[&str], server: &PackseServer) -> Command {\n"); + output.push_str( + " let python_path = python_path_with_versions(&context.temp_dir, python_versions)\n", + ); + output.push_str(" .expect(\"Failed to create Python test path\");\n"); + output.push_str(" let mut command = Command::new(get_bin!());\n"); + output.push_str(" command\n"); + output.push_str(" .arg(\"pip\")\n"); + output.push_str(" .arg(\"compile\")\n"); + output.push_str(" .arg(\"requirements.in\")\n"); + output.push_str(" .arg(\"--index-url\")\n"); + output.push_str(" .arg(server.index_url());\n"); + output.push_str(" context.add_shared_options(&mut command, true);\n"); + output.push_str(" command.env_remove(EnvVars::UV_EXCLUDE_NEWER);\n"); + output.push_str(" command.env(EnvVars::UV_PYTHON_SEARCH_PATH, python_path);\n\n"); + output.push_str(" command\n"); + output.push_str("}\n\n"); + + for case in cases { + render_compile_case(output, case)?; + } + Ok(()) +} + +fn render_compile_case(output: &mut String, case: &ScenarioCase) -> Result<()> { + render_case_docs(output, &case.scenario)?; + if case.scenario.name.contains("patch") { + output.push_str("#[cfg(feature = \"test-python-patch\")]\n"); + } + output.push_str("#[test]\n"); + writeln!( + output, + "fn {}() -> Result<()> {{", + module_name(&case.scenario.name) + ) + .unwrap(); + writeln!( + output, + " let context = uv_test::test_context!(\"{}\");", + case.scenario.environment.python + ) + .unwrap(); + write!(output, " let python_versions = &[").unwrap(); + for version in &case.scenario.environment.additional_python { + write!(output, "\"{version}\", ").unwrap(); + } + output.push_str("];\n"); + writeln!( + output, + " let server = PackseServer::new(\"{}\");", + case.path + ) + .unwrap(); + output.push('\n'); + output.push_str(" let requirements_in = context.temp_dir.child(\"requirements.in\");\n"); + let requirements = compile_requirements(&case.scenario.root.requires); + writeln!(output, " requirements_in.write_str({requirements:?})?;").unwrap(); + output.push('\n'); + render_expected_explanation(output, &case.scenario, " // "); + output.push_str( + " let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server)\n", + ); + render_resolver_args(output, &case.scenario, ScenarioCommand::Compile)?; + output.push_str(" , @r###\"\n"); + output.push_str(" \"###\n"); + output.push_str(" );\n\n"); + output.push_str(" output\n"); + output.push_str(" .assert()\n"); + if case.scenario.expected.satisfiable { + output.push_str(" .success()\n"); + for (name, version) in &case.scenario.expected.packages { + writeln!( + output, + " .stdout(predicate::str::contains(\"{name}=={version}\"))" + ) + .unwrap(); + } + } else { + output.push_str(" .failure()\n"); + } + output.push_str(" ;\n\n"); + output.push_str(" Ok(())\n"); + output.push_str("}\n\n"); + Ok(()) +} + +fn render_lock(output: &mut String, cases: &[&ScenarioCase]) -> Result<()> { + render_header(output); + output.push_str("#![cfg(all(feature = \"test-python\", feature = \"test-pypi\"))]\n"); + output.push_str("#![expect(clippy::needless_raw_string_hashes)]\n"); + output.push_str("#![expect(clippy::doc_markdown)]\n"); + output.push('\n'); + output.push_str("use anyhow::Result;\n"); + output.push_str("use assert_cmd::assert::OutputAssertExt;\n"); + output.push_str("use assert_fs::prelude::*;\n"); + output.push_str("use insta::assert_snapshot;\n\n"); + output.push_str("use uv_static::EnvVars;\n"); + output.push_str("use uv_test::packse::PackseServer;\n"); + output.push_str("use uv_test::uv_snapshot;\n\n"); + + for case in cases { + render_lock_case(output, case)?; + } + Ok(()) +} + +fn render_lock_case(output: &mut String, case: &ScenarioCase) -> Result<()> { + render_case_docs(output, &case.scenario)?; + output.push_str("#[test]\n"); + writeln!( + output, + "fn {}() -> Result<()> {{", + module_name(&case.scenario.name) + ) + .unwrap(); + writeln!( + output, + " let context = uv_test::test_context!(\"{}\");", + case.scenario.environment.python + ) + .unwrap(); + writeln!( + output, + " let server = PackseServer::new(\"{}\");", + case.path + ) + .unwrap(); + output.push('\n'); + output.push_str(" let pyproject_toml = context.temp_dir.child(\"pyproject.toml\");\n"); + output.push_str(" pyproject_toml.write_str(\n"); + output.push_str(" r###\"\n"); + output.push_str(" [project]\n"); + output.push_str(" name = \"project\"\n"); + output.push_str(" version = \"0.1.0\"\n"); + output.push_str(" dependencies = [\n"); + for requirement in &case.scenario.root.requires { + writeln!(output, " '''{requirement}''',").unwrap(); + } + output.push_str(" ]\n"); + if let Some(requires_python) = &case.scenario.root.requires_python { + writeln!(output, " requires-python = \"{requires_python}\"").unwrap(); + } + if !case + .scenario + .resolver_options + .required_environments + .is_empty() + { + output.push_str(" [tool.uv]\n"); + output.push_str(" required-environments = [\n"); + for environment in &case.scenario.resolver_options.required_environments { + let environment = environment + .contents() + .context("required environment markers should not be empty")?; + writeln!(output, " '''{environment}''',").unwrap(); + } + output.push_str(" ]\n"); + } + output.push_str(" \"###\n"); + output.push_str(" )?;\n\n"); + output.push_str(" let filters = context.filters();\n\n"); + output.push_str(" let mut cmd = context.lock();\n"); + output.push_str(" cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER);\n"); + output.push_str(" cmd.arg(\"--index-url\").arg(server.index_url());\n"); + render_expected_explanation(output, &case.scenario, " // "); + output.push_str(" uv_snapshot!(filters, cmd, @r###\"\n"); + output.push_str(" \"###\n"); + output.push_str(" );\n\n"); + if case.scenario.expected.satisfiable { + output.push_str(" let lock = context.read(\"uv.lock\");\n"); + output.push_str(" insta::with_settings!({\n"); + output.push_str(" filters => filters,\n"); + output.push_str(" }, {\n"); + output.push_str(" assert_snapshot!(\n"); + output.push_str(" lock, @r###\"\n"); + output.push_str(" \"###\n"); + output.push_str(" );\n"); + output.push_str(" });\n\n"); + output.push_str(" // Assert the idempotence of `uv lock` when resolving from the lockfile (`--locked`).\n"); + output.push_str(" context\n"); + output.push_str(" .lock()\n"); + output.push_str(" .arg(\"--locked\")\n"); + output.push_str(" .env_remove(EnvVars::UV_EXCLUDE_NEWER)\n"); + output.push_str(" .arg(\"--index-url\")\n"); + output.push_str(" .arg(server.index_url())\n"); + output.push_str(" .assert()\n"); + output.push_str(" .success();\n"); + } + output.push('\n'); + output.push_str(" Ok(())\n"); + output.push_str("}\n\n"); + Ok(()) +} + +#[derive(Copy, Clone)] +enum ScenarioCommand { + Install, + Compile, +} + +fn render_resolver_args( + output: &mut String, + scenario: &Scenario, + command: ScenarioCommand, +) -> Result<()> { + if scenario.resolver_options.prereleases { + output.push_str(" .arg(\"--prerelease=allow\")\n"); + } + for package in &scenario.resolver_options.no_build { + writeln!(output, " .arg(\"--only-binary\")").unwrap(); + writeln!(output, " .arg(\"{package}\")").unwrap(); + } + for package in &scenario.resolver_options.no_binary { + writeln!(output, " .arg(\"--no-binary\")").unwrap(); + writeln!(output, " .arg(\"{package}\")").unwrap(); + } + if let Some(platform) = &scenario.resolver_options.python_platform { + let platform = platform + .to_possible_value() + .context("target triple should have a clap representation")?; + writeln!( + output, + " .arg(\"--python-platform={}\")", + platform.get_name() + ) + .unwrap(); + } + if matches!(command, ScenarioCommand::Compile) + && let Some(python) = &scenario.resolver_options.python + { + writeln!(output, " .arg(\"--python-version={python}\")").unwrap(); + } + if matches!(command, ScenarioCommand::Install) { + for requirement in &scenario.root.requires { + writeln!(output, " .arg(\"{requirement}\")").unwrap(); + } + } + Ok(()) +} + +fn render_install_assertions(output: &mut String, scenario: &Scenario) { + if scenario.expected.satisfiable { + for (name, version) in &scenario.expected.packages { + writeln!( + output, + " context.assert_installed(\"{}\", \"{version}\");", + module_name(name.as_ref()) + ) + .unwrap(); + } + } else { + for requirement in &scenario.root.requires { + writeln!( + output, + " context.assert_not_installed(\"{}\");", + module_name(requirement.name.as_ref()) + ) + .unwrap(); + } + } +} + +fn render_expected_explanation(output: &mut String, scenario: &Scenario, prefix: &str) { + if let Some(explanation) = &scenario.expected.explanation { + for line in explanation.lines() { + writeln!(output, "{prefix}{line}").unwrap(); + } + } +} + +fn compile_requirements(requirements: &[Requirement]) -> String { + requirements.iter().map(ToString::to_string).join("\n") +} + +fn render_case_docs(output: &mut String, scenario: &Scenario) -> Result<()> { + output.push_str("/// "); + if let Some(description) = &scenario.description { + output.push_str(&description.replace('\n', "\n/// ")); + } else { + output.push_str(&scenario.name); + } + output.push_str("\n///\n"); + output.push_str("/// ```text\n"); + writeln!(output, "/// {}", scenario.name).unwrap(); + for line in pretty_tree(scenario)? { + writeln!(output, "/// {line}").unwrap(); + } + output.push_str("/// ```\n"); + Ok(()) +} + +fn pretty_tree(scenario: &Scenario) -> Result> { + const SPACE: &str = " "; + const BRANCH: &str = "│ "; + const TEE: &str = "├── "; + const LAST: &str = "└── "; + + let mut lines = Vec::new(); + lines.push(format!("{TEE}environment")); + if scenario.environment.additional_python.is_empty() { + lines.push(format!( + "{BRANCH}{LAST}python{}", + scenario.environment.python + )); + } else { + let mut versions = scenario.environment.additional_python.clone(); + versions.push(scenario.environment.python.clone()); + versions.sort_by_key(ToString::to_string); + for (index, version) in versions.iter().enumerate() { + let pointer = if index + 1 == versions.len() { + LAST + } else { + TEE + }; + let active = if version == &scenario.environment.python { + " (active)" + } else { + "" + }; + lines.push(format!("{BRANCH}{pointer}python{version}{active}")); + } + } + + let root_pointer = if scenario.packages.is_empty() { + LAST + } else { + TEE + }; + lines.push(format!("{root_pointer}root")); + let root_prefix = if root_pointer == TEE { BRANCH } else { SPACE }; + render_requirements(&mut lines, scenario, &scenario.root.requires, root_prefix)?; + + let package_names = scenario.packages.keys().cloned().collect::>(); + for (index, package_name) in package_names.iter().enumerate() { + let pointer = if index + 1 == package_names.len() { + LAST + } else { + TEE + }; + lines.push(format!("{pointer}{package_name}")); + let prefix = if pointer == TEE { BRANCH } else { SPACE }; + render_versions(&mut lines, scenario, package_name, prefix, None)?; + } + + Ok(lines) +} + +fn render_requirements( + lines: &mut Vec, + scenario: &Scenario, + requirements: &[Requirement], + prefix: &str, +) -> Result<()> { + const SPACE: &str = " "; + const BRANCH: &str = "│ "; + const TEE: &str = "├── "; + const LAST: &str = "└── "; + + let mut filtered = requirements + .iter() + .filter(|requirement| !omit_python_requirement(scenario, requirement)) + .collect::>(); + filtered.sort_by(|left, right| left.name.cmp(&right.name)); + + for (index, requirement) in filtered.iter().enumerate() { + let pointer = if index + 1 == filtered.len() { + LAST + } else { + TEE + }; + if requirement.name.as_ref() == "python" { + let suffix = if requirement_specifiers(requirement).is_some_and(|specifiers| { + !specifiers.contains(&scenario.environment.python.python_version()) + }) { + " (incompatible with environment)" + } else { + "" + }; + lines.push(format!("{prefix}{pointer}requires {requirement}{suffix}")); + continue; + } + + lines.push(format!("{prefix}{pointer}requires {requirement}")); + if scenario.packages.contains_key(&requirement.name) { + let next_prefix = format!("{prefix}{}", if pointer == TEE { BRANCH } else { SPACE }); + render_versions( + lines, + scenario, + &requirement.name, + &next_prefix, + Some(requirement), + )?; + } else { + lines.push(format!( + "{prefix}{SPACE}{LAST}unsatisfied: no versions for package" + )); + } + } + + Ok(()) +} + +fn omit_python_requirement(scenario: &Scenario, requirement: &Requirement) -> bool { + if requirement.name.as_ref() != "python" { + return false; + } + let Some(specifiers) = requirement_specifiers(requirement) else { + return false; + }; + let specifiers = specifiers.iter().collect::>(); + specifiers.len() == 1 + && specifiers[0].version() == &scenario.environment.python.python_version() + && requirement_specifiers(requirement).is_some_and(|specifiers| { + specifiers.contains(&scenario.environment.python.python_version()) + }) +} + +fn render_versions( + lines: &mut Vec, + scenario: &Scenario, + package_name: &PackageName, + prefix: &str, + for_requirement: Option<&Requirement>, +) -> Result<()> { + const SPACE: &str = " "; + const BRANCH: &str = "│ "; + const TEE: &str = "├── "; + const LAST: &str = "└── "; + + let package = scenario + .packages + .get(package_name) + .with_context(|| format!("missing package metadata for {package_name}"))?; + + let versions = matching_versions(package, for_requirement); + if for_requirement.is_some() && versions.is_empty() { + lines.push(format!("{prefix}{LAST}unsatisfied: no matching version")); + return Ok(()); + } + + let rows = version_rows(package_name, &versions); + for (index, row) in rows.iter().enumerate() { + let pointer = if index + 1 == rows.len() { LAST } else { TEE }; + let satisfied = if for_requirement.is_some() { + "satisfied by " + } else { + "" + }; + let yanked = if row.metadata.yanked { " (yanked)" } else { "" }; + lines.push(format!( + "{prefix}{pointer}{satisfied}{package_name}-{}{yanked}", + row.label + )); + if for_requirement.is_none() { + let next_prefix = format!("{prefix}{}", if pointer == TEE { BRANCH } else { SPACE }); + render_requirements(lines, scenario, &row.requirements, &next_prefix)?; + } + } + + Ok(()) +} + +fn matching_versions<'a>( + package: &'a Package, + requirement: Option<&Requirement>, +) -> Vec<(&'a Version, &'a PackageMetadata)> { + package + .versions + .iter() + .filter(|(version, metadata)| { + if metadata.yanked { + return requirement.is_none(); + } + requirement + .and_then(requirement_specifiers) + .is_none_or(|specifiers| specifiers.contains(version)) + }) + .collect() +} + +struct VersionRow<'a> { + label: String, + requirements: Vec, + metadata: &'a PackageMetadata, +} + +fn version_rows<'a>( + _package_name: &PackageName, + versions: &[(&Version, &'a PackageMetadata)], +) -> Vec> { + let mut rows = Vec::new(); + for (version, metadata) in versions { + let mut requirements = metadata.requires.clone(); + if let Some(requires_python) = &metadata.requires_python { + let requirement = format!("python{requires_python}") + .parse() + .expect("generated Python requirement should parse"); + requirements.push(requirement); + } + rows.push(VersionRow { + label: version.to_string(), + requirements, + metadata, + }); + for (extra, requirements) in &metadata.extras { + rows.push(VersionRow { + label: format!("{version}[{extra}]"), + requirements: requirements.clone(), + metadata, + }); + } + } + rows +} + +fn module_name(name: &str) -> String { + name.replace('-', "_") +} + +fn requirement_specifiers(requirement: &Requirement) -> Option<&uv_pep440::VersionSpecifiers> { + match requirement.version_or_url.as_ref()? { + VersionOrUrl::VersionSpecifier(specifiers) => Some(specifiers), + VersionOrUrl::Url(_) => None, + } +} + +#[cfg(test)] +mod tests { + use super::{ + TemplateKind, check_generated_file, compile_requirements, load_scenarios, + load_scenarios_from, render, + }; + + #[test] + fn compile_requirements_preserve_multiple_root_entries() { + let requirements = [ + "a==1.0.0".parse().expect("valid requirement"), + "b>=2.0.0".parse().expect("valid requirement"), + ]; + + assert_eq!(compile_requirements(&requirements), "a==1.0.0\nb>=2.0.0"); + } + + #[test] + fn scenario_suites_require_pypi_feature() { + for (template, expected_gate) in [ + ( + TemplateKind::Install, + r#"#![cfg(all(feature = "test-python", feature = "test-pypi", unix))]"#, + ), + ( + TemplateKind::Compile, + r#"#![cfg(all(feature = "test-python", feature = "test-pypi", unix))]"#, + ), + ( + TemplateKind::Lock, + r#"#![cfg(all(feature = "test-python", feature = "test-pypi"))]"#, + ), + ] { + let output = render(template, &[]).expect("empty scenario suite should render"); + let gate = output + .lines() + .find(|line| line.starts_with("#![cfg")) + .expect("scenario suite should contain a feature gate"); + assert_eq!(gate, expected_gate); + } + } + + #[test] + fn missing_scenarios_directory_is_an_error() { + let temp_dir = tempfile::tempdir().expect("temporary directory should be created"); + let directory = temp_dir.path().join("missing"); + + assert!(load_scenarios_from(&directory).is_err()); + } + + #[test] + fn vendored_scenarios_parse() { + let scenarios = load_scenarios().expect("vendored scenarios should parse"); + + assert!(!scenarios.is_empty()); + } + + #[test] + fn stale_generated_file_is_an_error() { + let temporary_directory = + tempfile::tempdir().expect("temporary directory should be created"); + let path = temporary_directory.path().join("scenario.rs"); + fs_err::write(&path, "old contents").expect("temporary file should be written"); + + let error = + check_generated_file(&path, "new contents").expect_err("stale file should fail"); + let error = error + .to_string() + .replace( + temporary_directory.path().to_string_lossy().as_ref(), + "[TEMP_DIR]", + ) + .replace('\\', "/"); + assert_eq!( + error + .lines() + .next() + .expect("error should include a summary"), + "[TEMP_DIR]/scenario.rs changed, please run `cargo dev generate-scenario-tests`:" + ); + } + + #[test] + fn accepted_snapshots_do_not_make_generated_file_stale() { + let temporary_directory = + tempfile::tempdir().expect("temporary directory should be created"); + let path = temporary_directory.path().join("scenario.rs"); + fs_err::write( + &path, + "/// Literal @\"accepted\" remains significant.\nfn scenario() {\n uv_snapshot!(filters, cmd, @\"\naccepted output\n\");\n}\n", + ) + .expect("temporary file should be written"); + + check_generated_file( + &path, + "/// Literal @\"accepted\" remains significant.\nfn scenario() {\n uv_snapshot!(filters, cmd, @r#\"\n\"#);\n}\n", + ) + .expect("accepted snapshot should not make generated file stale"); + } + + #[test] + fn snapshot_like_documentation_changes_make_generated_file_stale() { + let temporary_directory = + tempfile::tempdir().expect("temporary directory should be created"); + let path = temporary_directory.path().join("scenario.rs"); + fs_err::write( + &path, + "/// Literal @\"current\" remains significant.\nfn scenario() {\n uv_snapshot!(filters, cmd, @\"\naccepted output\n\");\n}\n", + ) + .expect("temporary file should be written"); + + check_generated_file( + &path, + "/// Literal @\"generated\" remains significant.\nfn scenario() {\n uv_snapshot!(filters, cmd, @r#\"\n\"#);\n}\n", + ) + .expect_err("documentation changes should make generated output stale"); + } +} diff --git a/crates/uv-dev/src/lib.rs b/crates/uv-dev/src/lib.rs index ed61843a8f..c29efc765c 100644 --- a/crates/uv-dev/src/lib.rs +++ b/crates/uv-dev/src/lib.rs @@ -13,6 +13,7 @@ use crate::generate_cli_reference::Args as GenerateCliReferenceArgs; use crate::generate_env_vars_reference::Args as GenerateEnvVarsReferenceArgs; use crate::generate_json_schema::Args as GenerateJsonSchemaArgs; use crate::generate_options_reference::Args as GenerateOptionsReferenceArgs; +use crate::generate_scenarios::Args as GenerateScenarioTestsArgs; use crate::generate_sysconfig_mappings::Args as GenerateSysconfigMetadataArgs; use crate::list_packages::ListPackagesArgs; #[cfg(feature = "render")] @@ -27,6 +28,7 @@ mod generate_cli_reference; mod generate_env_vars_reference; mod generate_json_schema; mod generate_options_reference; +mod generate_scenarios; mod generate_sysconfig_mappings; mod list_packages; mod render_benchmarks; @@ -57,6 +59,8 @@ enum Cli { GenerateCliReference(GenerateCliReferenceArgs), /// Generate the environment variables reference for the documentation. GenerateEnvVarsReference(GenerateEnvVarsReferenceArgs), + /// Generate the Packse scenario integration tests. + GenerateScenarioTests(GenerateScenarioTestsArgs), /// Generate the sysconfig metadata from derived targets. GenerateSysconfigMetadata(GenerateSysconfigMetadataArgs), #[cfg(feature = "render")] @@ -79,9 +83,25 @@ pub async fn run() -> Result<()> { Cli::GenerateOptionsReference(args) => generate_options_reference::main(&args)?, Cli::GenerateCliReference(args) => generate_cli_reference::main(&args)?, Cli::GenerateEnvVarsReference(args) => generate_env_vars_reference::main(&args)?, + Cli::GenerateScenarioTests(args) => generate_scenarios::main(&args)?, Cli::GenerateSysconfigMetadata(args) => generate_sysconfig_mappings::main(&args).await?, #[cfg(feature = "render")] Cli::RenderBenchmarks(args) => render_benchmarks::render_benchmarks(&args)?, } Ok(()) } + +#[cfg(test)] +mod tests { + use clap::CommandFactory; + + use super::Cli; + + #[test] + fn scenario_tests_command_uses_explicit_name() { + let command = Cli::command(); + + assert!(command.find_subcommand("generate-scenario-tests").is_some()); + assert!(command.find_subcommand("generate-scenarios").is_none()); + } +} diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs index 519b76ce0c..a02ac2c00c 100644 --- a/crates/uv-static/src/env_vars.rs +++ b/crates/uv-static/src/env_vars.rs @@ -1202,11 +1202,6 @@ impl EnvVars { #[attr_added_in("0.7.21")] pub const UV_TEST_NO_HTTP_RETRY_DELAY: &'static str = "UV_TEST_NO_HTTP_RETRY_DELAY"; - /// Used to set a packse index url for tests. - #[attr_hidden] - #[attr_added_in("0.2.12")] - pub const UV_TEST_PACKSE_INDEX: &'static str = "UV_TEST_PACKSE_INDEX"; - /// Used for testing named indexes in tests. #[attr_hidden] #[attr_added_in("0.5.21")] diff --git a/crates/uv-test/Cargo.toml b/crates/uv-test/Cargo.toml index a4887841a6..08389119ec 100644 --- a/crates/uv-test/Cargo.toml +++ b/crates/uv-test/Cargo.toml @@ -11,7 +11,6 @@ license = { workspace = true } [lib] doctest = false -test = false [lints] workspace = true @@ -26,16 +25,23 @@ tracing-durations-export = [] uv-cache = { workspace = true, features = ["clap"] } uv-client = { workspace = true } uv-configuration = { workspace = true } -uv-fs = { workspace = true } +uv-distribution-filename = { workspace = true } +uv-fs = { workspace = true, features = ["tokio"] } +uv-normalize = { workspace = true } +uv-pep440 = { workspace = true } +uv-pep508 = { workspace = true } uv-preview = { workspace = true } uv-python = { workspace = true } uv-static = { workspace = true } uv-version = { workspace = true } anyhow = { workspace = true } +astral-tokio-tar = { workspace = true } assert_cmd = { workspace = true } assert_fs = { workspace = true } +async_zip = { workspace = true } base64 = { workspace = true } +flate2 = { workspace = true } fs-err = { workspace = true, features = ["tokio"] } futures = { workspace = true } ignore = { workspace = true } @@ -45,6 +51,12 @@ itertools = { workspace = true } predicates = { workspace = true } regex = { workspace = true } reqwest = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sha2 = { workspace = true } similar = { workspace = true } tempfile = { workspace = true } tokio = { workspace = true } +tokio-util = { workspace = true } +toml = { workspace = true } +wiremock = { workspace = true } diff --git a/crates/uv-test/src/find_links.rs b/crates/uv-test/src/find_links.rs new file mode 100644 index 0000000000..e95f883322 --- /dev/null +++ b/crates/uv-test/src/find_links.rs @@ -0,0 +1,134 @@ +//! A local HTTP server that serves a directory of files as a PEP 503 flat link page. +//! +//! Useful for testing `--find-links` with an HTTP URL. + +use std::collections::HashMap; +use std::path::Path; +use std::sync::Arc; + +use wiremock::{Request, ResponseTemplate}; + +use crate::http_server::{HttpServer, content_type_for_filename}; +use crate::vendor::{VendorArtifact, vendor_artifacts}; + +enum FileData { + Bytes(Arc<[u8]>), + Vendor(&'static VendorArtifact), +} + +impl FileData { + fn bytes(&self) -> anyhow::Result> { + match self { + Self::Bytes(bytes) => Ok(Arc::clone(bytes)), + Self::Vendor(artifact) => artifact.bytes(), + } + } +} + +/// A running HTTP server that serves files from a directory as a flat links page. +pub struct FindLinksServer { + server: HttpServer, +} + +impl FindLinksServer { + /// Start a server that serves all files in the given directory. + pub fn new(directory: &Path) -> Self { + let mut files: HashMap = HashMap::new(); + let mut filenames: Vec = Vec::new(); + + for entry in fs_err::read_dir(directory).expect("failed to read find-links directory") { + let entry = entry.expect("failed to read directory entry"); + let path = entry.path(); + if !path.is_file() { + continue; + } + let Some(filename) = path.file_name().map(|n| n.to_string_lossy().to_string()) else { + continue; + }; + let bytes = fs_err::read(&path).expect("failed to read file"); + files.insert(filename.clone(), FileData::Bytes(bytes.into())); + filenames.push(filename); + } + filenames.sort(); + + let files = Arc::new(files); + let filenames = Arc::new(filenames); + let server = HttpServer::start(move |request, server_uri| { + handle_request(request, server_uri, &files, &filenames) + }); + + Self { server } + } + + /// Start a server that serves the pinned registry artifacts used by tests. + pub fn vendor() -> Self { + let mut files: HashMap = HashMap::new(); + let mut filenames: Vec = Vec::new(); + + for artifact in vendor_artifacts() { + files.insert(artifact.filename.to_string(), FileData::Vendor(artifact)); + filenames.push(artifact.filename.to_string()); + } + filenames.sort(); + + let files = Arc::new(files); + let filenames = Arc::new(filenames); + let server = HttpServer::start(move |request, server_uri| { + handle_request(request, server_uri, &files, &filenames) + }); + + Self { server } + } + + /// The base URL of the server (for use with `--find-links`). + pub fn url(&self) -> &str { + self.server.url() + } +} + +fn handle_request( + request: &Request, + server_uri: &str, + files: &HashMap, + filenames: &[String], +) -> ResponseTemplate { + let path = request.url.path(); + + if path == "/" { + let links = filenames + .iter() + .map(|filename| format!("{filename}")) + .collect::>() + .join("\n"); + let html = format!("\n\n{links}\n"); + return ResponseTemplate::new(200).set_body_raw(html, "text/html"); + } + + let filename = path.trim_start_matches('/'); + if let Some(file) = files.get(filename) { + return match file.bytes() { + Ok(bytes) => ResponseTemplate::new(200) + .set_body_raw(bytes.to_vec(), content_type_for_filename(filename)), + Err(error) => ResponseTemplate::new(500).set_body_string(format!("{error:#}")), + }; + } + + ResponseTemplate::new(404) +} + +#[cfg(test)] +mod tests { + use super::FindLinksServer; + use crate::vendor::vendor_artifacts; + + #[test] + fn vendor_server_construction_does_not_load_artifacts() { + let _server = FindLinksServer::vendor(); + + assert!( + vendor_artifacts() + .iter() + .all(|artifact| !artifact.is_loaded()) + ); + } +} diff --git a/crates/uv-test/src/http_server.rs b/crates/uv-test/src/http_server.rs new file mode 100644 index 0000000000..f4301035b6 --- /dev/null +++ b/crates/uv-test/src/http_server.rs @@ -0,0 +1,76 @@ +use std::path::Path; +use std::thread; +use std::time::Duration; + +use wiremock::matchers::any; +use wiremock::{Mock, MockServer, Request, ResponseTemplate}; + +/// Background wiremock server shared by the local test indexes in this crate. +pub(crate) struct HttpServer { + url: String, + shutdown: Option>, + thread: Option>, +} + +impl HttpServer { + pub(crate) fn start( + handler: impl Fn(&Request, &str) -> ResponseTemplate + Send + Sync + 'static, + ) -> Self { + let (url_tx, url_rx) = std::sync::mpsc::channel::(); + let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>(); + + let thread = thread::spawn(move || { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("failed to create tokio runtime for local HTTP test server"); + + runtime.block_on(async move { + let server = MockServer::start().await; + let server_uri = server.uri(); + + Mock::given(any()) + .respond_with(move |request: &Request| handler(request, &server_uri)) + .mount(&server) + .await; + + url_tx.send(server.uri()).ok(); + let _ = shutdown_rx.await; + }); + }); + + let url = url_rx + .recv_timeout(Duration::from_secs(30)) + .expect("timed out waiting for local HTTP test server to start"); + + Self { + url, + shutdown: Some(shutdown_tx), + thread: Some(thread), + } + } + + pub(crate) fn url(&self) -> &str { + &self.url + } +} + +pub(crate) fn content_type_for_filename(filename: &str) -> &'static str { + if Path::new(filename) + .extension() + .is_some_and(|extension| extension.eq_ignore_ascii_case("whl")) + { + "application/zip" + } else { + "application/gzip" + } +} + +impl Drop for HttpServer { + fn drop(&mut self) { + drop(self.shutdown.take()); + if let Some(thread) = self.thread.take() { + thread.join().ok(); + } + } +} diff --git a/crates/uv-test/src/lib.rs b/crates/uv-test/src/lib.rs index c7cbbbdfa6..3156dee51e 100755 --- a/crates/uv-test/src/lib.rs +++ b/crates/uv-test/src/lib.rs @@ -1,6 +1,11 @@ // The `unreachable_pub` is to silence false positives in RustRover. #![allow(dead_code, unreachable_pub)] +pub mod find_links; +mod http_server; +pub mod packse; +mod vendor; + use std::borrow::BorrowMut; use std::ffi::OsString; use std::io::Write as _; @@ -36,7 +41,6 @@ use uv_static::EnvVars; // Shared test timestamp for deterministic package availability and relative times. static TEST_TIMESTAMP: &str = "2024-03-25T00:00:00Z"; -pub const PACKSE_VERSION: &str = "0.3.59"; pub const DEFAULT_PYTHON_VERSION: &str = "3.12"; // The expected latest patch version for each Python minor version. @@ -47,26 +51,6 @@ pub const LATEST_PYTHON_3_12: &str = "3.12.13"; pub const LATEST_PYTHON_3_11: &str = "3.11.15"; pub const LATEST_PYTHON_3_10: &str = "3.10.20"; -/// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests -/// to prevent dependency confusion attacks against our test suite. -pub fn build_vendor_links_url() -> String { - env::var(EnvVars::UV_TEST_PACKSE_INDEX) - .map(|url| format!("{}/vendor/", url.trim_end_matches('/'))) - .ok() - .unwrap_or(format!( - "https://astral-sh.github.io/packse/{PACKSE_VERSION}/vendor/" - )) -} - -pub fn packse_index_url() -> String { - env::var(EnvVars::UV_TEST_PACKSE_INDEX) - .map(|url| format!("{}/simple-html/", url.trim_end_matches('/'))) - .ok() - .unwrap_or(format!( - "https://astral-sh.github.io/packse/{PACKSE_VERSION}/simple-html/" - )) -} - /// Create a new [`TestContext`] with the given Python version. /// /// Creates a virtual environment for the test. @@ -1057,19 +1041,6 @@ impl TestContext { // Destroy any remaining UNC prefixes (Windows only) filters.push((r"\\\\\?\\".to_string(), String::new())); - // Remove the version from the packse url in lockfile snapshots. This avoids having a huge - // diff any time we upgrade packse - filters.push(( - format!("https://astral-sh.github.io/packse/{PACKSE_VERSION}"), - "https://astral-sh.github.io/packse/PACKSE_VERSION".to_string(), - )); - // Developer convenience - if let Ok(packse_test_index) = env::var(EnvVars::UV_TEST_PACKSE_INDEX) { - filters.push(( - packse_test_index.trim_end_matches('/').to_string(), - "https://astral-sh.github.io/packse/PACKSE_VERSION".to_string(), - )); - } // For wiremock tests filters.push((r"127\.0\.0\.1:\d*".to_string(), "[LOCALHOST]".to_string())); // Avoid breaking the tests when bumping the uv version @@ -1202,6 +1173,8 @@ impl TestContext { .env_remove(EnvVars::VIRTUAL_ENV) // Disable wrapping of uv output for readability / determinism in snapshots. .env(EnvVars::UV_NO_WRAP, "1") + // Avoid reading host system configuration unless a test opts in. + .env(EnvVars::UV_NO_SYSTEM_CONFIG, "1") // While we disable wrapping in uv above, invoked tools may still wrap their output so // we set a fixed `COLUMNS` value for isolation from terminal width. .env(EnvVars::COLUMNS, "100") diff --git a/crates/uv-test/src/packse/mod.rs b/crates/uv-test/src/packse/mod.rs new file mode 100644 index 0000000000..d7ee7efcd3 --- /dev/null +++ b/crates/uv-test/src/packse/mod.rs @@ -0,0 +1,26 @@ +//! Local mock index for packse scenario tests. +//! +//! This module provides a [`PackseServer`] that reads packse scenario TOML definitions +//! and serves a PEP 691 Simple API + wheel/sdist downloads via a local wiremock server. +//! Each test gets its own server instance, so package names need no prefix mangling. + +pub mod scenario; +mod server; +mod wheel; + +use std::path::{Path, PathBuf}; + +pub use server::PackseServer; + +fn workspace_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(Path::parent) + .map(Path::to_path_buf) + .expect("CARGO_MANIFEST_DIR should be nested under workspace root") +} + +/// Base directory containing the vendored packse scenario TOML files. +fn scenarios_dir() -> PathBuf { + workspace_root().join("test").join("scenarios") +} diff --git a/crates/uv-test/src/packse/scenario.rs b/crates/uv-test/src/packse/scenario.rs new file mode 100644 index 0000000000..3654a2fe05 --- /dev/null +++ b/crates/uv-test/src/packse/scenario.rs @@ -0,0 +1,392 @@ +//! Typed representation of the vendored Packse scenario TOML files. +//! +//! The nested TOML tables map directly onto [`Scenario::packages`]: +//! `[packages..versions.]` becomes a [`PackageName`] key, then a [`Version`] key. + +use std::collections::BTreeMap; +use std::path::Path; +use std::str::FromStr; + +use anyhow::{Context, Result}; +use serde::Deserialize; + +use uv_configuration::TargetTriple; +use uv_distribution_filename::WheelFilename; +use uv_normalize::{ExtraName, PackageName}; +use uv_pep440::{Version, VersionSpecifiers}; +use uv_pep508::{MarkerTree, Requirement}; +use uv_python::PythonVersion; + +/// A complete packse scenario definition. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Scenario { + /// The scenario name (e.g., `"fork-basic"`). + pub name: String, + + /// Human-readable description. + #[serde(default)] + pub description: Option, + + /// Packages keyed by the TOML segment in `[packages.]`. + #[serde(default)] + pub packages: BTreeMap, + + /// The root (entrypoint) requirements. + pub root: RootPackage, + + /// What we expect the resolver to produce. + pub expected: Expected, + + /// Metadata about the Python environment. + #[serde(default)] + pub environment: Environment, + + /// Additional resolver options. + #[serde(default)] + pub resolver_options: ResolverOptions, +} + +impl Scenario { + /// Parse a single scenario from a TOML file path. + pub fn from_path(path: &Path) -> Result { + let contents = fs_err::read_to_string(path) + .with_context(|| format!("failed to read scenario file `{}`", path.display()))?; + toml::from_str(&contents) + .with_context(|| format!("failed to parse scenario file `{}`", path.display())) + } + + /// Construct an otherwise-empty scenario for indexes that should only expose vendored files. + pub fn empty() -> Self { + Self { + name: String::new(), + description: None, + packages: BTreeMap::new(), + root: RootPackage { + requires_python: None, + requires: Vec::new(), + }, + expected: Expected { + satisfiable: true, + packages: BTreeMap::new(), + explanation: None, + }, + environment: Environment::default(), + resolver_options: ResolverOptions::default(), + } + } +} + +/// A package with one or more versions. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Package { + pub versions: BTreeMap, +} + +/// Metadata for a single version of a package. +#[derive(Debug, Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct PackageMetadata { + /// The `Requires-Python` specifier. Defaults to `">=3.12"`. + #[serde(default = "default_requires_python")] + pub requires_python: Option, + + /// Dependency requirements. + #[serde(default)] + pub requires: Vec, + + /// Extra names mapped to their optional dependency requirements. + #[serde(default)] + pub extras: BTreeMap>, + + /// Whether to produce a source distribution. + #[serde(default = "default_true")] + pub sdist: bool, + + /// Whether to produce a wheel. + #[serde(default = "default_true")] + pub wheel: bool, + + /// Whether this version is yanked. + #[serde(default)] + pub yanked: bool, + + /// Specific wheel tags to produce (e.g., `["cp312-abi3-win_amd64"]`). + /// An empty list means produce only the default `py3-none-any` wheel. + #[serde(default)] + pub wheel_tags: Vec, +} + +/// A validated three-component compatibility tag for generated wheels. +#[derive(Clone, Debug)] +pub struct WheelTag(String); + +impl WheelTag { + /// Return the compatibility tag as it should appear in a wheel filename. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl FromStr for WheelTag { + type Err = String; + + fn from_str(tag: &str) -> Result { + if tag.split('-').count() != 3 { + return Err(format!( + "wheel tag `{tag}` must have exactly three components" + )); + } + WheelFilename::from_str(&format!("package-0-{tag}.whl")) + .map_err(|error| format!("wheel tag `{tag}` is invalid: {error}"))?; + Ok(Self(tag.to_string())) + } +} + +impl<'de> Deserialize<'de> for WheelTag { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let tag = String::deserialize(deserializer)?; + Self::from_str(&tag).map_err(serde::de::Error::custom) + } +} + +/// The root/entrypoint package. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RootPackage { + /// `Requires-Python` for the root. + #[serde(default = "default_requires_python")] + pub requires_python: Option, + + /// Top-level requirements. + #[serde(default)] + pub requires: Vec, +} + +/// Expected resolution outcome. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Expected { + /// Whether the scenario is satisfiable. + pub satisfiable: bool, + + /// Expected installed package names mapped to resolved versions. + #[serde(default)] + pub packages: BTreeMap, + + /// Optional explanation. + #[serde(default)] + pub explanation: Option, +} + +/// Python environment metadata. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Environment { + /// Active Python version. + #[serde(default = "default_python")] + pub python: PythonVersion, + + /// Additional Python versions available on the system. + #[serde(default)] + pub additional_python: Vec, +} + +impl Default for Environment { + fn default() -> Self { + Self { + python: default_python(), + additional_python: Vec::new(), + } + } +} + +/// Additional resolver options. +#[derive(Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ResolverOptions { + /// Python version override for resolution. + #[serde(default)] + pub python: Option, + + /// Enable pre-release selection. + #[serde(default)] + pub prereleases: bool, + + /// Packages that must use pre-built wheels (no building from source). + #[serde(default)] + pub no_build: Vec, + + /// Packages that must NOT use pre-built wheels (must build from source). + #[serde(default)] + pub no_binary: Vec, + + /// Universal (multi-platform) resolution mode. + #[serde(default)] + pub universal: bool, + + /// Python platform to resolve for. + #[serde(default)] + pub python_platform: Option, + + /// Required environments (platform markers). + #[serde(default)] + pub required_environments: Vec, +} + +#[expect(clippy::unnecessary_wraps)] // Must return `Option` for serde `default` +fn default_requires_python() -> Option { + Some(VersionSpecifiers::from_str(">=3.12").expect("default requires-python should be valid")) +} + +fn default_true() -> bool { + true +} + +fn default_python() -> PythonVersion { + PythonVersion::from_str("3.12").expect("default Python version should be valid") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_basic_scenario() { + let toml = r#" +name = "fork-basic" +description = "An extremely basic test." + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] +"#; + let scenario: Scenario = toml::from_str(toml).expect("scenario should parse"); + let package_name = PackageName::from_str("a").expect("valid package name"); + assert_eq!(scenario.name, "fork-basic"); + assert!(scenario.resolver_options.universal); + assert_eq!(scenario.packages.len(), 1); + assert_eq!(scenario.packages[&package_name].versions.len(), 2); + } + + #[test] + fn parse_extras_scenario() { + let toml = r#" +name = "all-extras-required" +description = "Multiple optional dependencies." + +[root] +requires = ["a[all]"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" +b = "1.0.0" +c = "1.0.0" + +[packages.b.versions."1.0.0"] +[packages.c.versions."1.0.0"] + +[packages.a.versions."1.0.0".extras] +all = ["a[extra_b]", "a[extra_c]"] +extra_b = ["b"] +extra_c = ["c"] +"#; + let scenario: Scenario = toml::from_str(toml).expect("scenario should parse"); + let package_name = PackageName::from_str("a").expect("valid package name"); + let version = Version::from_str("1.0.0").expect("valid version"); + let extra_name = ExtraName::from_str("extra_b").expect("valid extra name"); + assert_eq!(scenario.name, "all-extras-required"); + let a_meta = &scenario.packages[&package_name].versions[&version]; + assert_eq!(a_meta.extras.len(), 3); + assert_eq!( + a_meta.extras[&extra_name], + vec![Requirement::from_str("b").expect("valid requirement")] + ); + } + + #[test] + fn reject_invalid_requires_python() { + let toml = r#" +name = "invalid-requires-python" + +[root] +requires = [] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +requires_python = "not a specifier" +"#; + + assert!(toml::from_str::(toml).is_err()); + } + + #[test] + fn reject_unknown_metadata_field() { + let toml = r#" +name = "unknown-metadata-field" + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +wheels = false +"#; + + assert!(toml::from_str::(toml).is_err()); + } + + #[test] + fn reject_invalid_wheel_tag() { + let toml = r#" +name = "invalid-wheel-tag" + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +wheel_tags = ["1-py3-none-any"] +"#; + + assert!(toml::from_str::(toml).is_err()); + } + + #[test] + fn path_is_included_in_parse_errors() { + let temporary_directory = + tempfile::tempdir().expect("temporary directory should be created"); + let path = temporary_directory.path().join("invalid.toml"); + fs_err::write(&path, "not valid TOML = [").expect("invalid scenario should be written"); + + let error = Scenario::from_path(&path).expect_err("scenario should fail to parse"); + insta::assert_snapshot!( + error + .to_string() + .replace(temporary_directory.path().to_string_lossy().as_ref(), "[TEMP_DIR]") + .replace('\\', "/"), + @"failed to parse scenario file `[TEMP_DIR]/invalid.toml`" + ); + } +} diff --git a/crates/uv-test/src/packse/server.rs b/crates/uv-test/src/packse/server.rs new file mode 100644 index 0000000000..7adcf28204 --- /dev/null +++ b/crates/uv-test/src/packse/server.rs @@ -0,0 +1,304 @@ +//! A per-test wiremock server that serves packse scenario packages. +//! +//! Each [`PackseServer`] reads a single scenario TOML file and serves: +//! - PEP 691 Simple API at `/simple/{package}/` +//! - Distribution downloads at `/files/{filename}` +//! +//! Cached build dependencies are exposed through the same `/simple/*` and +//! `/files/*` routes as scenario packages. + +use std::collections::HashMap; +use std::path::Path; +use std::str::FromStr; +use std::sync::Arc; + +use serde_json::json; +use wiremock::{Request, ResponseTemplate}; + +use uv_distribution_filename::WheelFilename; +use uv_normalize::PackageName; +use uv_pep440::VersionSpecifiers; + +use crate::http_server::{HttpServer, content_type_for_filename}; +use crate::vendor::{VendorArtifact, vendor_artifacts}; + +use super::scenario::{Scenario, WheelTag}; +use super::scenarios_dir; +use super::wheel::{generate_sdist, generate_wheel, sha256_hex}; + +const PACKSE_UPLOAD_TIME: &str = "2024-03-24T00:00:00Z"; + +/// Information about a single distribution file (metadata for the Simple API). +struct DistInfo { + filename: String, + sha256: String, + requires_python: Option, + upload_time: &'static str, + yanked: bool, +} + +/// All distributions for a given package name, across versions. +struct PackageEntry { + dists: Vec, +} + +enum FileData { + Bytes(Arc<[u8]>), + Vendor(&'static VendorArtifact), +} + +impl FileData { + fn bytes(&self) -> anyhow::Result> { + match self { + Self::Bytes(bytes) => Ok(Arc::clone(bytes)), + Self::Vendor(artifact) => artifact.bytes(), + } + } +} + +/// The complete pre-indexed database for a server instance. +struct ServerIndex { + /// Simple API: normalized package name → list of distribution metadata. + packages: HashMap, + /// File downloads: filename → generated bytes or a lazy vendored artifact. + files: HashMap, +} + +/// A running mock PyPI server for a single packse scenario. +/// +/// The server runs on a background thread with its own single-threaded tokio runtime. +/// When [`PackseServer`] is dropped, the background thread and server are shut down. +pub struct PackseServer { + server: HttpServer, +} + +impl PackseServer { + /// Load a scenario from a TOML path (relative to the vendored scenarios directory) + /// and start a mock server for it. + pub fn new(scenario_path: &str) -> Self { + let full_path = scenarios_dir().join(scenario_path); + let scenario = + Scenario::from_path(&full_path).expect("vendored Packse scenario should parse"); + Self::from_scenario(&scenario) + } + + /// Start a mock server with no packages (only cached build dependencies). + /// + /// Useful as a dummy index that will 404 for any non-cached package lookup. + pub fn empty() -> Self { + Self::from_scenario(&Scenario::empty()) + } + + /// Start a mock server for the given scenario. + pub fn from_scenario(scenario: &Scenario) -> Self { + let index = Arc::new(build_server_index(scenario)); + let server = HttpServer::start(move |request, server_uri| { + handle_request(request, server_uri, &index) + }); + + Self { server } + } + + /// The Simple API index URL (e.g., `http://127.0.0.1:PORT/simple/`). + pub fn index_url(&self) -> String { + format!("{}/simple/", self.server.url()) + } +} + +/// Build the complete [`ServerIndex`] from a scenario and cached build dependencies. +fn build_server_index(scenario: &Scenario) -> ServerIndex { + let mut packages = HashMap::new(); + let mut files: HashMap = HashMap::new(); + + for (package_name, package) in &scenario.packages { + let mut dists = Vec::new(); + + for (version, meta) in &package.versions { + if meta.wheel { + let tags = if meta.wheel_tags.is_empty() { + vec!["py3-none-any"] + } else { + meta.wheel_tags.iter().map(WheelTag::as_str).collect() + }; + + for tag in tags { + let (filename, bytes) = generate_wheel( + package_name, + version, + &meta.requires, + &meta.extras, + meta.requires_python.as_ref(), + tag, + ); + let sha256 = sha256_hex(&bytes); + files.insert(filename.clone(), FileData::Bytes(bytes.into())); + dists.push(DistInfo { + filename, + sha256, + requires_python: meta.requires_python.clone(), + upload_time: PACKSE_UPLOAD_TIME, + yanked: meta.yanked, + }); + } + } + + if meta.sdist { + let (filename, bytes) = generate_sdist( + package_name, + version, + &meta.requires, + &meta.extras, + meta.requires_python.as_ref(), + ); + let sha256 = sha256_hex(&bytes); + files.insert(filename.clone(), FileData::Bytes(bytes.into())); + dists.push(DistInfo { + filename, + sha256, + requires_python: meta.requires_python.clone(), + upload_time: PACKSE_UPLOAD_TIME, + yanked: meta.yanked, + }); + } + } + + packages.insert(package_name.clone(), PackageEntry { dists }); + } + + for artifact in vendor_artifacts() { + if !Path::new(artifact.filename) + .extension() + .is_some_and(|extension| extension.eq_ignore_ascii_case("whl")) + { + continue; + } + + let wheel_filename = + WheelFilename::from_str(artifact.filename).expect("invalid vendor wheel filename"); + + files.insert(artifact.filename.to_string(), FileData::Vendor(artifact)); + packages + .entry(wheel_filename.name) + .or_insert_with(|| PackageEntry { dists: Vec::new() }) + .dists + .push(DistInfo { + filename: artifact.filename.to_string(), + sha256: artifact.sha256.to_string(), + requires_python: None, + upload_time: PACKSE_UPLOAD_TIME, + yanked: false, + }); + } + + ServerIndex { packages, files } +} + +fn handle_request(req: &Request, server_uri: &str, index: &ServerIndex) -> ResponseTemplate { + let path = req.url.path(); + + if let Some(pkg) = extract_package_name(path) { + let Ok(package_name) = PackageName::from_str(pkg) else { + return ResponseTemplate::new(404); + }; + + if let Some(entry) = index.packages.get(&package_name) { + return build_simple_api_response(pkg, entry, server_uri); + } + return ResponseTemplate::new(404); + } + + if let Some(filename) = path.strip_prefix("/files/") { + if let Some(file) = index.files.get(filename) { + return match file.bytes() { + Ok(bytes) => ResponseTemplate::new(200) + .set_body_raw(bytes.to_vec(), content_type_for_filename(filename)), + Err(error) => ResponseTemplate::new(500).set_body_string(format!("{error:#}")), + }; + } + return ResponseTemplate::new(404); + } + + ResponseTemplate::new(404) +} + +/// Build PEP 691 JSON response for a package. +fn build_simple_api_response( + package_name: &str, + entry: &PackageEntry, + server_uri: &str, +) -> ResponseTemplate { + let files: Vec = entry + .dists + .iter() + .map(|dist| { + let url = format!("{server_uri}/files/{}", dist.filename); + let mut file_obj = json!({ + "filename": dist.filename, + "url": url, + "hashes": { + "sha256": dist.sha256, + }, + "upload-time": dist.upload_time, + }); + if let Some(rp) = &dist.requires_python { + file_obj["requires-python"] = json!(rp); + } + if dist.yanked { + file_obj["yanked"] = json!(true); + } + file_obj + }) + .collect(); + + let body = json!({ + "meta": { "api-version": "1.1" }, + "name": package_name, + "files": files, + }); + + let body_str = body.to_string(); + ResponseTemplate::new(200) + .insert_header("Content-Type", "application/vnd.pypi.simple.v1+json") + .set_body_raw(body_str, "application/vnd.pypi.simple.v1+json") +} + +/// Extract the package name from `/simple/{package}` or `/simple/{package}/`. +fn extract_package_name(path: &str) -> Option<&str> { + let rest = path.strip_prefix("/simple/")?; + let pkg = rest.strip_suffix('/').unwrap_or(rest); + if pkg.is_empty() || pkg.contains('/') { + return None; + } + Some(pkg) +} + +#[cfg(test)] +mod tests { + use crate::vendor::vendor_artifacts; + + use super::{Scenario, build_server_index, extract_package_name}; + + #[test] + fn extract_package_name_accepts_with_or_without_trailing_slash() { + assert_eq!(extract_package_name("/simple/foo/"), Some("foo")); + assert_eq!(extract_package_name("/simple/foo"), Some("foo")); + } + + #[test] + fn extract_package_name_rejects_invalid_paths() { + assert_eq!(extract_package_name("/simple/"), None); + assert_eq!(extract_package_name("/simple"), None); + assert_eq!(extract_package_name("/simple/foo/bar"), None); + } + + #[test] + fn server_index_construction_does_not_load_vendor_artifacts() { + let _index = build_server_index(&Scenario::empty()); + + assert!( + vendor_artifacts() + .iter() + .all(|artifact| !artifact.is_loaded()) + ); + } +} diff --git a/crates/uv-test/src/packse/wheel.rs b/crates/uv-test/src/packse/wheel.rs new file mode 100644 index 0000000000..6df380f1c3 --- /dev/null +++ b/crates/uv-test/src/packse/wheel.rs @@ -0,0 +1,419 @@ +//! Generate minimal Python wheels and source distributions in memory. +//! +//! Packse scenario packages are trivial: they contain only metadata and a stub +//! `__init__.py`. We generate them directly without invoking a Python build backend. + +use std::collections::BTreeMap; +use std::fmt::Write as _; +use std::io::Cursor; + +use async_zip::base::write::ZipFileWriter; +use async_zip::{Compression as ZipCompression, ZipEntryBuilder}; +use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD as base64}; +use flate2::Compression; +use flate2::write::GzEncoder; +use futures::executor::block_on; +use futures::io::AllowStdIo; +use indoc::formatdoc; +use sha2::{Digest, Sha256}; +use tokio_util::compat::{FuturesAsyncReadCompatExt, FuturesAsyncWriteCompatExt}; + +use uv_normalize::{ExtraName, PackageName}; +use uv_pep440::{Version, VersionSpecifiers}; +use uv_pep508::Requirement; + +/// Generate a wheel (`.whl`) as an in-memory ZIP archive. +/// +/// Returns `(filename, bytes)`. +pub fn generate_wheel( + name: &PackageName, + version: &Version, + requires: &[Requirement], + extras: &BTreeMap>, + requires_python: Option<&VersionSpecifiers>, + tag: &str, +) -> (String, Vec) { + let normalized = name.as_dist_info_name(); + let dist_info = format!("{normalized}-{version}.dist-info"); + + let mut zip = ZipFileWriter::new(Vec::new()); + + let entries = [ + ( + format!("{normalized}/__init__.py"), + format!("__version__ = \"{version}\"\n"), + ), + ( + format!("{dist_info}/METADATA"), + build_metadata(name, version, requires, extras, requires_python), + ), + ( + format!("{dist_info}/WHEEL"), + format!( + "Wheel-Version: 1.0\n\ + Generator: uv-test\n\ + Root-Is-Purelib: true\n\ + Tag: {tag}\n" + ), + ), + ]; + for (path, contents) in &entries { + let entry = ZipEntryBuilder::new(path.clone().into(), ZipCompression::Stored); + block_on(zip.write_entry_whole(entry, contents.as_bytes())) + .expect("failed to write wheel file"); + } + + let record = build_record(&dist_info, &entries); + let record_entry = + ZipEntryBuilder::new(format!("{dist_info}/RECORD").into(), ZipCompression::Stored); + block_on(zip.write_entry_whole(record_entry, record.as_bytes())) + .expect("failed to write RECORD file"); + + let bytes = block_on(zip.close()).expect("failed to finish in-memory wheel"); + let filename = format!("{normalized}-{version}-{tag}.whl"); + (filename, bytes) +} + +/// Build the `RECORD` metadata for a generated wheel. +fn build_record(dist_info: &str, entries: &[(String, String)]) -> String { + let mut record = String::new(); + for (path, contents) in entries { + let contents = contents.as_bytes(); + let hash = base64.encode(Sha256::digest(contents)); + writeln!(&mut record, "{path},sha256={hash},{}", contents.len()) + .expect("writing RECORD metadata into a string should succeed"); + } + writeln!(&mut record, "{dist_info}/RECORD,,") + .expect("writing RECORD metadata into a string should succeed"); + record +} + +/// Generate a source distribution (`.tar.gz`) as an in-memory tarball. +/// +/// The sdist contains a `pyproject.toml` using `hatchling` as build backend, +/// `PKG-INFO` with full metadata, and a stub module. +/// +/// Returns `(filename, bytes)`. +pub fn generate_sdist( + name: &PackageName, + version: &Version, + requires: &[Requirement], + extras: &BTreeMap>, + requires_python: Option<&VersionSpecifiers>, +) -> (String, Vec) { + let normalized = name.as_dist_info_name(); + let prefix = format!("{normalized}-{version}"); + + let buf = Vec::new(); + let encoder = GzEncoder::new(buf, Compression::fast()); + let mut tar = tokio_tar::Builder::new_non_terminated(AllowStdIo::new(encoder).compat_write()); + + let pyproject = build_pyproject_toml(name, version, requires, extras, requires_python); + append_tar_file( + &mut tar, + &format!("{prefix}/pyproject.toml"), + pyproject.as_bytes(), + ); + + let pkg_info = build_metadata(name, version, requires, extras, requires_python); + append_tar_file(&mut tar, &format!("{prefix}/PKG-INFO"), pkg_info.as_bytes()); + + let init_py = format!("__version__ = \"{version}\"\n"); + append_tar_file( + &mut tar, + &format!("{prefix}/src/{normalized}/__init__.py"), + init_py.as_bytes(), + ); + + let writer = block_on(tar.into_inner()).expect("failed to finish in-memory source archive"); + let encoder = writer.into_inner().into_inner(); + let bytes = encoder + .finish() + .expect("failed to finish in-memory gzip stream"); + let filename = format!("{normalized}-{version}.tar.gz"); + (filename, bytes) +} + +/// Build PEP 566 / PEP 643 metadata content. +fn build_metadata( + name: &PackageName, + version: &Version, + requires: &[Requirement], + extras: &BTreeMap>, + requires_python: Option<&VersionSpecifiers>, +) -> String { + let mut metadata = String::new(); + writeln!(&mut metadata, "Metadata-Version: 2.3") + .expect("writing metadata into a string should succeed"); + writeln!(&mut metadata, "Name: {name}").expect("writing metadata into a string should succeed"); + writeln!(&mut metadata, "Version: {version}") + .expect("writing metadata into a string should succeed"); + if let Some(requires_python) = requires_python { + writeln!(&mut metadata, "Requires-Python: {requires_python}") + .expect("writing metadata into a string should succeed"); + } + + for extra_name in extras.keys() { + writeln!(&mut metadata, "Provides-Extra: {extra_name}") + .expect("writing metadata into a string should succeed"); + } + + for requirement in requires { + writeln!(&mut metadata, "Requires-Dist: {requirement}") + .expect("writing metadata into a string should succeed"); + } + for (extra_name, extra_requirements) in extras { + for requirement in extra_requirements { + let requirement = requirement.clone().with_extra_marker(extra_name); + writeln!(&mut metadata, "Requires-Dist: {requirement}") + .expect("writing metadata into a string should succeed"); + } + } + + metadata +} + +/// Build a minimal `pyproject.toml` for an sdist using hatchling. +fn build_pyproject_toml( + name: &PackageName, + version: &Version, + requires: &[Requirement], + extras: &BTreeMap>, + requires_python: Option<&VersionSpecifiers>, +) -> String { + let normalized = name.as_dist_info_name(); + let dependencies = if requires.is_empty() { + "dependencies = []\n".to_string() + } else { + let mut dependencies = String::from("dependencies = [\n"); + for requirement in requires { + writeln!(&mut dependencies, " \"{requirement}\",") + .expect("writing dependencies into a string should succeed"); + } + dependencies.push_str("]\n"); + dependencies + }; + let requires_python = requires_python + .map(|requires_python| format!("requires-python = \"{requires_python}\"\n")) + .unwrap_or_default(); + let optional_dependencies = if extras.is_empty() { + String::new() + } else { + let mut optional_dependencies = String::from("\n[project.optional-dependencies]\n"); + for (extra_name, extra_requirements) in extras { + writeln!(&mut optional_dependencies, "{extra_name} = [") + .expect("writing optional dependencies into a string should succeed"); + for requirement in extra_requirements { + writeln!(&mut optional_dependencies, " \"{requirement}\",") + .expect("writing optional dependencies into a string should succeed"); + } + optional_dependencies.push_str("]\n"); + } + optional_dependencies + }; + + formatdoc! { + r#" + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + + [tool.hatch.build.targets.wheel] + packages = ["src/{normalized}"] + + [tool.hatch.build.targets.sdist] + only-include = ["src/{normalized}"] + + [project] + name = "{name}" + version = "{version}" + {dependencies}{requires_python}{optional_dependencies} + "# + } +} + +/// Append a file entry to a tar archive from a byte slice. +fn append_tar_file(tar: &mut tokio_tar::Builder, path: &str, data: &[u8]) +where + W: tokio::io::AsyncWrite + Unpin + Send, +{ + let mut header = tokio_tar::Header::new_gnu(); + header.set_entry_type(tokio_tar::EntryType::Regular); + header.set_size(data.len() as u64); + header.set_mode(0o644); + header.set_cksum(); + block_on(tar.append_data( + &mut header, + path, + AllowStdIo::new(Cursor::new(data)).compat(), + )) + .expect("failed to append file to in-memory source archive"); +} + +/// Compute the SHA-256 hex digest of a byte slice. +pub fn sha256_hex(data: &[u8]) -> String { + let mut hasher = Sha256::new(); + hasher.update(data); + format!("{:x}", hasher.finalize()) +} + +#[cfg(test)] +mod tests { + use std::pin::Pin; + use std::str::FromStr; + + use futures::StreamExt; + + use super::*; + + #[test] + fn generate_simple_wheel() { + let requires = vec![Requirement::from_str("dep>=1.0").expect("valid requirement")]; + let requires_python = + VersionSpecifiers::from_str(">=3.12").expect("valid version specifier"); + let (filename, bytes) = generate_wheel( + &PackageName::from_str("my-package").expect("valid package name"), + &Version::from_str("1.0.0").expect("valid version"), + &requires, + &BTreeMap::new(), + Some(&requires_python), + "py3-none-any", + ); + assert_eq!(filename, "my_package-1.0.0-py3-none-any.whl"); + + let archive = block_on(async_zip::base::read::mem::ZipFileReader::new(bytes)) + .expect("wheel should be a valid zip"); + let names: Vec<_> = archive + .file() + .entries() + .iter() + .map(|entry| { + entry + .filename() + .as_str() + .expect("wheel path should be UTF-8") + }) + .collect(); + assert!(names.contains(&"my_package/__init__.py")); + assert!(names.contains(&"my_package-1.0.0.dist-info/METADATA")); + assert!(names.contains(&"my_package-1.0.0.dist-info/WHEEL")); + assert!(names.contains(&"my_package-1.0.0.dist-info/RECORD")); + + let record_index = archive + .file() + .entries() + .iter() + .position(|entry| { + entry + .filename() + .as_str() + .is_ok_and(|name| name == "my_package-1.0.0.dist-info/RECORD") + }) + .expect("wheel RECORD should exist"); + let mut record = String::new(); + block_on(async { + let mut reader = archive + .reader_with_entry(record_index) + .await + .expect("wheel RECORD should be readable"); + reader + .read_to_string_checked(&mut record) + .await + .expect("wheel RECORD should be valid UTF-8"); + }); + insta::assert_snapshot!(record, @" + my_package/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22 + my_package-1.0.0.dist-info/METADATA,sha256=5SmEsY5VDieqZOfjFTk5WyL4HQZueAVyhthkJaH2xC4,102 + my_package-1.0.0.dist-info/WHEEL,sha256=ujr00BDMtYYidJ71ulklWmNFpiGqy5NyjK1fX-JwFO4,78 + my_package-1.0.0.dist-info/RECORD,, + "); + } + + #[test] + fn generate_simple_sdist() { + let requires = vec![Requirement::from_str("dep>=1.0").expect("valid requirement")]; + let requires_python = + VersionSpecifiers::from_str(">=3.12").expect("valid version specifier"); + let (filename, bytes) = generate_sdist( + &PackageName::from_str("my-package").expect("valid package name"), + &Version::from_str("1.0.0").expect("valid version"), + &requires, + &BTreeMap::new(), + Some(&requires_python), + ); + assert_eq!(filename, "my_package-1.0.0.tar.gz"); + + let decoder = flate2::read::GzDecoder::new(Cursor::new(bytes)); + let mut archive = tokio_tar::Archive::new(AllowStdIo::new(decoder).compat()); + let names = block_on(async { + let mut entries = archive.entries().expect("sdist archive should be readable"); + let mut entries = Pin::new(&mut entries); + let mut names = Vec::new(); + while let Some(entry) = entries.next().await { + let entry = entry.expect("sdist archive entry should be readable"); + names.push( + entry + .path() + .expect("sdist archive entry should have a path") + .to_string_lossy() + .to_string(), + ); + } + names + }); + + assert!(names.contains(&"my_package-1.0.0/pyproject.toml".to_string())); + assert!(names.contains(&"my_package-1.0.0/PKG-INFO".to_string())); + assert!(names.contains(&"my_package-1.0.0/src/my_package/__init__.py".to_string())); + } + + #[test] + fn extra_deps_with_markers_include_the_extra_marker() { + let mut extras = BTreeMap::new(); + extras.insert( + ExtraName::from_str("binary").expect("valid extra name"), + vec![ + Requirement::from_str("dep-binary; implementation_name != 'pypy'") + .expect("valid requirement"), + ], + ); + let metadata = build_metadata( + &PackageName::from_str("pkg").expect("valid package name"), + &Version::from_str("1.0.0").expect("valid version"), + &[], + &extras, + None, + ); + assert!( + metadata.contains( + "Requires-Dist: dep-binary ; implementation_name != 'pypy' and extra == 'binary'" + ), + "metadata should retain the existing marker and add the extra marker:\n{metadata}" + ); + } + + #[test] + fn extra_deps_with_or_markers_preserve_precedence() { + let mut extras = BTreeMap::new(); + extras.insert( + ExtraName::from_str("compat").expect("valid extra name"), + vec![ + Requirement::from_str("dep; sys_platform == 'linux' or sys_platform == 'darwin'") + .expect("valid requirement"), + ], + ); + let metadata = build_metadata( + &PackageName::from_str("pkg").expect("valid package name"), + &Version::from_str("1.0.0").expect("valid version"), + &[], + &extras, + None, + ); + assert!( + metadata.contains( + "Requires-Dist: dep ; (sys_platform == 'darwin' and extra == 'compat') or (sys_platform == 'linux' and extra == 'compat')" + ), + "metadata should preserve the original or-marker semantics:\n{metadata}" + ); + } +} diff --git a/crates/uv-test/src/vendor.rs b/crates/uv-test/src/vendor.rs new file mode 100644 index 0000000000..f624e6cd3f --- /dev/null +++ b/crates/uv-test/src/vendor.rs @@ -0,0 +1,336 @@ +//! Pinned registry artifacts used by local test servers. + +use std::io::Write as _; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::{Arc, OnceLock}; + +use anyhow::{Context, Result, anyhow, bail}; +use sha2::{Digest, Sha256}; + +use uv_configuration::TrustedHost; +use uv_fs::{LockedFile, LockedFileMode, persist_with_retry_sync, tempfile_in}; +use uv_static::EnvVars; + +use crate::TestContext; + +pub(crate) struct VendorArtifact { + pub(crate) filename: &'static str, + url: &'static str, + pub(crate) sha256: &'static str, + bytes: OnceLock>, +} + +impl VendorArtifact { + pub(crate) fn bytes(&self) -> Result> { + if let Some(bytes) = self.bytes.get() { + return Ok(Arc::clone(bytes)); + } + + let bytes = load_vendor_file(self)?; + Ok(Arc::clone(self.bytes.get_or_init(|| bytes))) + } + + #[cfg(test)] + pub(crate) fn is_loaded(&self) -> bool { + self.bytes.get().is_some() + } +} + +static VENDOR_ARTIFACTS: [VendorArtifact; 17] = [ + VendorArtifact { + filename: "calver-2022.6.26-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/f7/39/e421c06f42ca00fa9cf8929c2466e58a837e8e97b8ab3ff4f4ff9a15e33e/calver-2022.6.26-py3-none-any.whl", + sha256: "a1d7fcdd67797afc52ee36ffb8c8adf6643173864306547bfd1380cbce6310a0", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "editables-0.5-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/6b/be/0f2f4a5e8adc114a02b63d92bf8edbfa24db6fc602fca83c885af2479e0e/editables-0.5-py3-none-any.whl", + sha256: "61e5ffa82629e0d8bfe09bc44a07db3c1ab8ed1ce78a6980732870f19b5e7d4c", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "flit_core-3.9.0-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/38/45/618e84e49a6c51e5dd15565ec2fcd82ab273434f236b8f108f065ded517a/flit_core-3.9.0-py3-none-any.whl", + sha256: "7aada352fb0c7f5538c4fafeddf314d3a6a92ee8e2b1de70482329e42de70301", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "futzed_bz2-0.1.0-py3-none-any.whl", + url: "https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_bz2-0.1.0-py3-none-any.whl", + sha256: "3c106ea0433a90f767271c322a8cb8bf5145f210adf8a357152796475b00b316", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "futzed_lzma-0.1.0-py3-none-any.whl", + url: "https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_lzma-0.1.0-py3-none-any.whl", + sha256: "9f39b4686926ffbed723450d700cca524001af259949eb5e4b973e7846df23d2", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "hatchling-1.20.0-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/87/4a/7d22a92b55809c579d8deafb0dabeb102b411a0ef9439949cccef3071527/hatchling-1.20.0-py3-none-any.whl", + sha256: "872c63aa7e8aca85e8dba07b05c6a9b28d5a149fe00638f1a47e36930197248f", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "packaging-23.2-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", + sha256: "8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "packaging-23.2.tar.gz", + url: "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", + sha256: "048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "pathspec-0.12.1-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", + sha256: "a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "pip-24.0-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl", + sha256: "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "pluggy-1.3.0-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/05/b8/42ed91898d4784546c5f06c60506400548db3f7a4b3fb441cba4e5c17952/pluggy-1.3.0-py3-none-any.whl", + sha256: "d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "setuptools-69.0.2-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/bb/e1/ed2dd0850446b8697ad28d118df885ad04140c64ace06c4bd559f7c8a94f/setuptools-69.0.2-py3-none-any.whl", + sha256: "1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "setuptools_scm-8.0.4-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/0e/a3/b9a8b0adfe672bf0df5901707aa929d30a97ee390ba651910186776746d2/setuptools_scm-8.0.4-py3-none-any.whl", + sha256: "b47844cd2a84b83b3187a5782c71128c28b4c94cad8bfb871da2784a5cb54c4f", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "tomli-2.0.1-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", + sha256: "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "trove_classifiers-2023.11.29-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/f8/50/e223fe762fe21fefb7a3f37c10e9693ea5f63cb54b5ae39daa876b780abc/trove_classifiers-2023.11.29-py3-none-any.whl", + sha256: "02307750cbbac2b3d13078662f8a5bf077732bf506e9c33c97204b7f68f3699e", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "typing_extensions-4.9.0-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/b7/f4/6a90020cd2d93349b442bfcb657d0dc91eee65491600b2cb1d388bc98e6b/typing_extensions-4.9.0-py3-none-any.whl", + sha256: "af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd", + bytes: OnceLock::new(), + }, + VendorArtifact { + filename: "wheel-0.42.0-py3-none-any.whl", + url: "https://files.pythonhosted.org/packages/c7/c3/55076fc728723ef927521abaa1955213d094933dc36d4a2008d5101e1af5/wheel-0.42.0-py3-none-any.whl", + sha256: "177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d", + bytes: OnceLock::new(), + }, +]; + +pub(crate) fn vendor_artifacts() -> &'static [VendorArtifact] { + &VENDOR_ARTIFACTS +} + +fn vendor_cache_dir() -> PathBuf { + TestContext::test_bucket_dir().join("vendor") +} + +fn load_vendor_file(artifact: &VendorArtifact) -> Result> { + // HTTP responders run in a Tokio runtime; load on another thread instead of nesting runtimes. + let bytes = std::thread::scope(|scope| { + scope + .spawn(|| load_vendor_file_async(artifact)) + .join() + .map_err(|_| anyhow!("vendor artifact loader panicked")) + })??; + + Ok(bytes.into()) +} + +#[tokio::main(flavor = "current_thread")] +async fn load_vendor_file_async(artifact: &VendorArtifact) -> Result> { + let cache_dir = vendor_cache_dir(); + fs_err::create_dir_all(&cache_dir) + .with_context(|| format!("failed to create vendor cache at `{}`", cache_dir.display()))?; + let path = cache_dir.join(artifact.filename); + ensure_cached_artifact(artifact, &path).await?; + let bytes = fs_err::read(&path) + .with_context(|| format!("failed to read cached vendor artifact `{}`", path.display()))?; + verify_bytes(artifact, &bytes)?; + Ok(bytes) +} + +async fn ensure_cached_artifact(artifact: &VendorArtifact, path: &Path) -> Result<()> { + if cached_artifact_matches(artifact, path) { + return Ok(()); + } + + let lock_path = artifact_lock_path(path)?; + let _lock = LockedFile::acquire( + &lock_path, + LockedFileMode::Exclusive, + format_args!("vendor artifact `{}`", artifact.filename), + ) + .await + .with_context(|| { + format!( + "failed to lock cached vendor artifact `{}`", + artifact.filename + ) + })?; + + if cached_artifact_matches(artifact, path) { + return Ok(()); + } + + let trusted_hosts = std::env::var(EnvVars::UV_INSECURE_HOST) + .unwrap_or_default() + .split(' ') + .filter(|host| !host.is_empty()) + .map(TrustedHost::from_str) + .collect::, _>>()?; + let client = uv_client::BaseClientBuilder::default() + .allow_insecure_host(trusted_hosts) + .build() + .context("failed to build vendor artifact client")?; + + let url = artifact + .url + .parse() + .with_context(|| format!("invalid vendor artifact URL `{}`", artifact.url))?; + let response = client + .for_host(&url) + .get(reqwest::Url::from(url)) + .send() + .await + .with_context(|| format!("failed to download `{}`", artifact.url))? + .error_for_status() + .with_context(|| format!("failed to download `{}`", artifact.url))?; + let bytes = response + .bytes() + .await + .with_context(|| format!("failed to read `{}`", artifact.url))?; + verify_bytes(artifact, &bytes)?; + + let parent = path + .parent() + .context("vendor artifact cache path should have a parent")?; + let mut temp = tempfile_in(parent).with_context(|| { + format!( + "failed to create vendor temp file in `{}`", + parent.display() + ) + })?; + temp.write_all(&bytes) + .with_context(|| format!("failed to write `{}`", artifact.filename))?; + temp.as_file_mut() + .sync_all() + .with_context(|| format!("failed to sync `{}`", artifact.filename))?; + + match persist_with_retry_sync(temp, path) { + Ok(()) => Ok(()), + Err(error) => { + if let Ok(bytes) = fs_err::read(path) + && verify_bytes(artifact, &bytes).is_ok() + { + Ok(()) + } else { + Err(error).with_context(|| { + format!( + "failed to persist cached vendor artifact `{}`", + path.display() + ) + }) + } + } + } +} + +fn cached_artifact_matches(artifact: &VendorArtifact, path: &Path) -> bool { + fs_err::read(path).is_ok_and(|bytes| verify_bytes(artifact, &bytes).is_ok()) +} + +fn artifact_lock_path(path: &Path) -> Result { + let parent = path + .parent() + .context("vendor artifact cache path should have a parent")?; + let mut filename = path + .file_name() + .context("vendor artifact cache path should have a filename")? + .to_os_string(); + filename.push(".lock"); + Ok(parent.join(filename)) +} + +fn verify_bytes(artifact: &VendorArtifact, bytes: &[u8]) -> Result<()> { + let actual = format!("{:x}", Sha256::digest(bytes)); + if actual == artifact.sha256 { + Ok(()) + } else { + bail!( + "hash mismatch for cached vendor artifact `{}`: expected `{}`, found `{}`", + artifact.filename, + artifact.sha256, + actual + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn artifact_lock_path_is_per_artifact() { + let path = Path::new("vendor").join("example-1.0.0-py3-none-any.whl"); + + assert_eq!( + artifact_lock_path(&path).expect("path should have a parent"), + Path::new("vendor").join("example-1.0.0-py3-none-any.whl.lock") + ); + } + + #[test] + fn cached_bytes_are_held_per_artifact() { + let bytes = OnceLock::new(); + bytes + .set(Arc::from(b"available".as_slice())) + .expect("artifact should not already have cached bytes"); + let loaded = VendorArtifact { + filename: "available.whl", + url: "unused", + sha256: "unused", + bytes, + }; + let unloaded = VendorArtifact { + filename: "unrequested.whl", + url: "unused", + sha256: "unused", + bytes: OnceLock::new(), + }; + + assert_eq!( + loaded + .bytes() + .expect("preloaded bytes should be returned") + .as_ref(), + b"available" + ); + assert!(unloaded.bytes.get().is_none()); + } +} diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index 1df5610681..0442f6109d 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -26,7 +26,7 @@ use uv_cache_key::{RepositoryUrl, cache_digest}; use uv_fs::Simplified; use uv_static::EnvVars; -use uv_test::{packse_index_url, uv_snapshot, venv_bin_path}; +use uv_test::{uv_snapshot, venv_bin_path}; /// Add a PyPI requirement. #[test] @@ -5106,6 +5106,7 @@ fn add_lower_bound_optional() -> Result<()> { /// Omit the local segment when adding dependencies (since `>=1.2.3+local` is invalid). #[test] fn add_lower_bound_local() -> Result<()> { + let server = uv_test::packse::PackseServer::new("local/local-simple.toml"); let context = uv_test::test_context!("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -5117,8 +5118,8 @@ fn add_lower_bound_local() -> Result<()> { dependencies = [] "#})?; - // Adding `torch` should include a lower-bound, but no local segment. - uv_snapshot!(context.filters(), context.add().arg("local-simple-a").arg("--index").arg(packse_index_url()).env_remove(EnvVars::UV_EXCLUDE_NEWER), @" + // Adding `a` should include a lower-bound, but no local segment. + uv_snapshot!(context.filters(), context.add().arg("a").arg("--index").arg(server.index_url()).env_remove(EnvVars::UV_EXCLUDE_NEWER), @" success: true exit_code: 0 ----- stdout ----- @@ -5127,7 +5128,7 @@ fn add_lower_bound_local() -> Result<()> { Resolved 2 packages in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + local-simple-a==1.2.3+foo + + a==1.2.3+foo "); let pyproject_toml = context.read("pyproject.toml"); @@ -5142,11 +5143,11 @@ fn add_lower_bound_local() -> Result<()> { version = "0.1.0" requires-python = ">=3.12" dependencies = [ - "local-simple-a>=1.2.3", + "a>=1.2.3", ] [[tool.uv.index]] - url = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" + url = "http://[LOCALHOST]/simple/" "# ); }); @@ -5163,12 +5164,12 @@ fn add_lower_bound_local() -> Result<()> { requires-python = ">=3.12" [[package]] - name = "local-simple-a" + name = "a" version = "1.2.3+foo" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/local_simple_a-1.2.3+foo.tar.gz", hash = "sha256:cd1855a98a7b0dce1f4617f2f2089906936344392d4bdd7720503e9f3c0b1544" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.2.3+foo.tar.gz", hash = "sha256:d8c42806a0bf7b47451778ed2a909a2f9d22fe3a4bac673934fc8fb2537a886a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/local_simple_a-1.2.3+foo-py3-none-any.whl", hash = "sha256:9a430e6d5e9cd3ab906ea412b00ea8a1bad7c59fd64df2278a2527a60a665751" }, + { url = "http://[LOCALHOST]/files/a-1.2.3+foo-py3-none-any.whl", hash = "sha256:2fd8eec176cad72b6c4372682485914aff65d215fb8cd71c85e9ed4511fa8bbe", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -5176,11 +5177,11 @@ fn add_lower_bound_local() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "local-simple-a" }, + { name = "a" }, ] [package.metadata] - requires-dist = [{ name = "local-simple-a", specifier = ">=1.2.3" }] + requires-dist = [{ name = "a", specifier = ">=1.2.3" }] "# ); }); diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index efdfa18474..c63431ed90 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -7,11 +7,10 @@ use url::Url; use uv_fs::Simplified; use uv_static::EnvVars; +use uv_test::packse::PackseServer; #[cfg(feature = "test-git")] use uv_test::{READ_ONLY_GITHUB_TOKEN, decode_token}; -use uv_test::{ - build_vendor_links_url, download_to_disk, packse_index_url, uv_snapshot, venv_bin_path, -}; +use uv_test::{download_to_disk, uv_snapshot, venv_bin_path}; #[test] fn lock_wheel_registry() -> Result<()> { @@ -12535,12 +12534,13 @@ fn lock_upgrade_group_no_project_table() -> Result<()> { #[test] fn lock_upgrade_drop_fork_markers() -> Result<()> { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("fork/fork-upgrade.toml"); let requirements = r#"[project] name = "forking" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["fork-upgrade-foo==1"] + dependencies = ["foo==1"] [build-system] requires = ["flit_core>=3.8,<4"] @@ -12552,7 +12552,7 @@ fn lock_upgrade_drop_fork_markers() -> Result<()> { context .lock() .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .env_remove(EnvVars::UV_EXCLUDE_NEWER) .assert() .success(); @@ -12560,11 +12560,11 @@ fn lock_upgrade_drop_fork_markers() -> Result<()> { assert!(lock.contains("resolution-markers")); // Remove the bound and lock with `--upgrade`. - pyproject_toml.write_str(&requirements.replace("fork-upgrade-foo==1", "fork-upgrade-foo"))?; + pyproject_toml.write_str(&requirements.replace("foo==1", "foo"))?; context .lock() .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--upgrade") .assert() @@ -13072,6 +13072,7 @@ fn lock_find_links_local_sdist() -> Result<()> { #[test] fn lock_find_links_http_wheel() -> Result<()> { let context = uv_test::test_context!("3.12"); + let vendor = uv_test::find_links::FindLinksServer::vendor(); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(&formatdoc! { r#" @@ -13082,10 +13083,10 @@ fn lock_find_links_http_wheel() -> Result<()> { dependencies = ["packaging==23.2"] [tool.uv] - find-links = ["{}"] + find-links = ["{vendor_url}"] no-build-package = ["packaging"] "#, - build_vendor_links_url() + vendor_url = vendor.url() })?; uv_snapshot!(context.filters(), context.lock(), @" @@ -13114,10 +13115,10 @@ fn lock_find_links_http_wheel() -> Result<()> { [[package]] name = "packaging" version = "23.2" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/vendor/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/vendor/build/packaging-23.2.tar.gz" } + source = { registry = "http://[LOCALHOST]/" } + sdist = { url = "http://[LOCALHOST]/packaging-23.2.tar.gz" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/vendor/build/packaging-23.2-py3-none-any.whl" }, + { url = "http://[LOCALHOST]/packaging-23.2-py3-none-any.whl" }, ] [[package]] @@ -13163,6 +13164,7 @@ fn lock_find_links_http_wheel() -> Result<()> { #[test] fn lock_find_links_http_sdist() -> Result<()> { let context = uv_test::test_context!("3.12"); + let vendor = uv_test::find_links::FindLinksServer::vendor(); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(&formatdoc! { r#" @@ -13173,10 +13175,10 @@ fn lock_find_links_http_sdist() -> Result<()> { dependencies = ["packaging==23.2"] [tool.uv] - find-links = ["{}"] + find-links = ["{vendor_url}"] no-binary-package = ["packaging"] "#, - build_vendor_links_url() + vendor_url = vendor.url() })?; uv_snapshot!(context.filters(), context.lock(), @" @@ -13205,10 +13207,10 @@ fn lock_find_links_http_sdist() -> Result<()> { [[package]] name = "packaging" version = "23.2" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/vendor/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/vendor/build/packaging-23.2.tar.gz" } + source = { registry = "http://[LOCALHOST]/" } + sdist = { url = "http://[LOCALHOST]/packaging-23.2.tar.gz" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/vendor/build/packaging-23.2-py3-none-any.whl" }, + { url = "http://[LOCALHOST]/packaging-23.2-py3-none-any.whl" }, ] [[package]] @@ -21302,9 +21304,10 @@ fn lock_repeat_named_index_cli() -> Result<()> { ); }); - // Resolve to PyPI, since the PyTorch index is replaced by the Packse index, which doesn't + // Resolve to PyPI, since the PyTorch index is replaced by an empty index, which doesn't // include `jinja2`. - uv_snapshot!(context.filters(), context.lock().arg("--index").arg(format!("pytorch={}", packse_index_url())), @" + let empty_index = PackseServer::empty(); + uv_snapshot!(context.filters(), context.lock().arg("--index").arg(format!("pytorch={}", empty_index.index_url())), @" success: true exit_code: 0 ----- stdout ----- @@ -35615,6 +35618,7 @@ fn lock_unsupported_wheel_url_required_platform() -> Result<()> { #[test] fn lock_required_environment_cycle_reports_resolution_error() -> Result<()> { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-sdist-no-wheels-with-matching-platform.toml"); context .temp_dir @@ -35635,7 +35639,7 @@ fn lock_required_environment_cycle_reports_resolution_error() -> Result<()> { url = "{}" default = true "#, - packse_index_url() + server.index_url() })?; let pkg_a = context.temp_dir.child("pkg-a"); @@ -35646,7 +35650,7 @@ fn lock_required_environment_cycle_reports_resolution_error() -> Result<()> { version = "0.1.0" requires-python = ">=3.12" dependencies = [ - "no-sdist-no-wheels-with-matching-platform-a", + "a", "pkg-b", ] @@ -35691,8 +35695,8 @@ fn lock_required_environment_cycle_reports_resolution_error() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies for split (markers: platform_machine == 'arm64'): - ╰─▶ Because no-sdist-no-wheels-with-matching-platform-a==1.0.0 has no `platform_machine == 'arm64'`-compatible wheels and only no-sdist-no-wheels-with-matching-platform-a==1.0.0 is available, we can conclude that all versions of no-sdist-no-wheels-with-matching-platform-a cannot be used. - And because pkg-a depends on no-sdist-no-wheels-with-matching-platform-a and your workspace requires pkg-a, we can conclude that your workspace's requirements are unsatisfiable. + ╰─▶ Because a==1.0.0 has no `platform_machine == 'arm64'`-compatible wheels and only a==1.0.0 is available, we can conclude that all versions of a cannot be used. + And because pkg-a depends on a and your workspace requires pkg-a, we can conclude that your workspace's requirements are unsatisfiable. " ); diff --git a/crates/uv/tests/it/lock_scenarios.rs b/crates/uv/tests/it/lock_scenarios.rs index 46545c808e..5e4aca5aaf 100644 --- a/crates/uv/tests/it/lock_scenarios.rs +++ b/crates/uv/tests/it/lock_scenarios.rs @@ -1,7 +1,7 @@ //! DO NOT EDIT //! -//! Generated with `./scripts/sync_scenarios.sh` -//! Scenarios from +//! Generated with `cargo dev generate-scenario-tests` +//! Scenarios from //! #![cfg(all(feature = "test-python", feature = "test-pypi"))] #![expect(clippy::needless_raw_string_hashes)] @@ -13,8 +13,8 @@ use assert_fs::prelude::*; use insta::assert_snapshot; use uv_static::EnvVars; - -use uv_test::{packse_index_url, uv_snapshot}; +use uv_test::packse::PackseServer; +use uv_test::uv_snapshot; /// There are two packages, `a` and `b`. We select `a` with `a==2.0.0` first, and then `b`, but `a==2.0.0` conflicts with all new versions of `b`, so we backtrack through versions of `b`. /// @@ -83,10 +83,7 @@ use uv_test::{packse_index_url, uv_snapshot}; #[test] fn wrong_backtracking_basic() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"wrong-backtracking-basic-", "package-")); + let server = PackseServer::new("backtracking/wrong-backtracking-basic.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -95,16 +92,18 @@ fn wrong_backtracking_basic() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''wrong-backtracking-basic-a''', - '''wrong-backtracking-basic-b''', + '''a''', + '''b''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -125,40 +124,40 @@ fn wrong_backtracking_basic() -> Result<()> { revision = 3 requires-python = ">=3.12" + [[package]] + name = "a" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "b" + version = "2.0.9" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "a" }, + ] + sdist = { url = "http://[LOCALHOST]/files/b-2.0.9.tar.gz", hash = "sha256:f9d404650ef15d09f718b4f99d911962f653516974fa1e01cf8dba7afa695caa", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/b-2.0.9-py3-none-any.whl", hash = "sha256:fb91402b66338aaf9408407aa3681dcdd0984b9774ecf46632bc3761198399fa", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, - { name = "package-b" }, + { name = "a" }, + { name = "b" }, ] [package.metadata] requires-dist = [ - { name = "package-a" }, - { name = "package-b" }, - ] - - [[package]] - name = "package-a" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_basic_a-1.0.0.tar.gz", hash = "sha256:b4abd2c802ca129d5855225fe456f2a36068c50d0ae545e37a8e08ef0f580b38" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_basic_a-1.0.0-py3-none-any.whl", hash = "sha256:d669cb8614076ad7fc83f46b97abb94d86ada4ad5341d070874e96640ef808ad" }, - ] - - [[package]] - name = "package-b" - version = "2.0.9" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-a" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_basic_b-2.0.9.tar.gz", hash = "sha256:aec746d9adae60458015ad7c11b1b9c589031928c07d9c13f1dff23d473b2480" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_basic_b-2.0.9-py3-none-any.whl", hash = "sha256:30c0b2450c13c06d70ccb8804e41d3be9dacc911e27f30ac58b7880d8fe8e705" }, + { name = "a" }, + { name = "b" }, ] "# ); @@ -170,7 +169,7 @@ fn wrong_backtracking_basic() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -250,10 +249,7 @@ fn wrong_backtracking_basic() -> Result<()> { #[test] fn wrong_backtracking_indirect() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"wrong-backtracking-indirect-", "package-")); + let server = PackseServer::new("backtracking/wrong-backtracking-indirect.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -262,16 +258,18 @@ fn wrong_backtracking_indirect() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''wrong-backtracking-indirect-a''', - '''wrong-backtracking-indirect-b''', + '''a''', + '''b''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -292,61 +290,61 @@ fn wrong_backtracking_indirect() -> Result<()> { revision = 3 requires-python = ">=3.12" + [[package]] + name = "a" + version = "2.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "b" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "b-inner" }, + ] + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:d002926c2038325d9c4287f87bc5c12d7336a32c5bbff9925ac474fa4341149d", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:e64d7b65e2bc771f36c53ea70d805ebf643322fa2d1761a0dc45b75d0374e2fb", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "b-inner" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "too-old" }, + ] + sdist = { url = "http://[LOCALHOST]/files/b_inner-1.0.0.tar.gz", hash = "sha256:73cb22e45889937a3a26ec58d668e92e488c133a6ecc4515f067532380980631", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/b_inner-1.0.0-py3-none-any.whl", hash = "sha256:dc550a3821df74da8f99f92aeff395eae8f050034fe8403a13043d42dca06a95", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, - { name = "package-b" }, + { name = "a" }, + { name = "b" }, ] [package.metadata] requires-dist = [ - { name = "package-a" }, - { name = "package-b" }, + { name = "a" }, + { name = "b" }, ] [[package]] - name = "package-a" - version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_a-2.0.0.tar.gz", hash = "sha256:8f28371115dab396e098ce46f514d6bdf15a42c81cc75aa78c675db61e1ed67e" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_a-2.0.0-py3-none-any.whl", hash = "sha256:ea2a2e5008c2ca39195650f532c8ff6c129a91ca92018490fe53f9f0d323414e" }, - ] - - [[package]] - name = "package-b" + name = "too-old" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-b-inner" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_b-1.0.0.tar.gz", hash = "sha256:2463de4ba18fe6b1f03b8458724a399c85c98a354a1861ea02e485757f096e3b" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/too_old-1.0.0.tar.gz", hash = "sha256:98267dfd9af634cfcbfa079c8a5cdcbb5169836904a5cb4441b71333467dc682", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_b-1.0.0-py3-none-any.whl", hash = "sha256:cf4fc24449def13876f05f0c8cae59e4104ab9694b58ad6e92cc3fe25ecea6b3" }, - ] - - [[package]] - name = "package-b-inner" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-too-old" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_b_inner-1.0.0.tar.gz", hash = "sha256:e1ddc7be17380b754483067727ad9fa4e40f2f9b837982e9b9124ee9425ad72e" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_b_inner-1.0.0-py3-none-any.whl", hash = "sha256:c58bcad2e57e160ec81d3e7a057f2c9a1a5fb74a3e3d18d82d79ab0dc5ce85dd" }, - ] - - [[package]] - name = "package-too-old" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_too_old-1.0.0.tar.gz", hash = "sha256:de078b8acaad7f58f194407633aac7fda37550f5fbe778ecd837599ed3872a4d" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/wrong_backtracking_indirect_too_old-1.0.0-py3-none-any.whl", hash = "sha256:d6ddc9421418ce70582869cf38c4f0322fc4061be36edc501555a616d560e7ba" }, + { url = "http://[LOCALHOST]/files/too_old-1.0.0-py3-none-any.whl", hash = "sha256:7efb79d455d0a679335ce5abee7d3bf298cac8c6e0aa19654b7c033d603c93ef", upload-time = "2024-03-24T00:00:00Z" }, ] "# ); @@ -358,7 +356,7 @@ fn wrong_backtracking_indirect() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -375,15 +373,16 @@ fn wrong_backtracking_indirect() -> Result<()> { /// leaves the `a>=1` fork free to choose `a==2.0.0` since it behaves as if /// the `a<2` constraint doesn't exist. /// +/// /// ```text /// fork-allows-non-conflicting-non-overlapping-dependencies /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=1; sys_platform == "linux" +/// │ ├── requires a>=1 ; sys_platform == 'linux' /// │ │ ├── satisfied by a-1.0.0 /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// └── a /// ├── a-1.0.0 @@ -392,13 +391,7 @@ fn wrong_backtracking_indirect() -> Result<()> { #[test] fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"fork-allows-non-conflicting-non-overlapping-dependencies-", - "package-", - )); + let server = PackseServer::new("fork/allows-non-conflicting-non-overlapping-dependencies.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -407,16 +400,18 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-allows-non-conflicting-non-overlapping-dependencies-a>=1; sys_platform == "linux"''', - '''fork-allows-non-conflicting-non-overlapping-dependencies-a<2; sys_platform == "darwin"''', + '''a>=1 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" - "### + "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -443,12 +438,12 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_allows_non_conflicting_non_overlapping_dependencies_a-1.0.0.tar.gz", hash = "sha256:836b578e798d4aaba37ea42ebde338fd422d61d2bc4a93524b9c9cf77a7539d7" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_allows_non_conflicting_non_overlapping_dependencies_a-1.0.0-py3-none-any.whl", hash = "sha256:fce4343aac09c16fee45735fd638efc462aa97496c3e3332b6f8babdbd1e1e4d" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -456,13 +451,13 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "a", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=1" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=1" }, ] "# ); @@ -474,7 +469,7 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -493,6 +488,7 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { /// only occur when there are duplicate dependency specifications with /// disjoint marker expressions. /// +/// /// ```text /// fork-allows-non-conflicting-repeated-dependencies /// ├── environment @@ -510,13 +506,7 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { #[test] fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"fork-allows-non-conflicting-repeated-dependencies-", - "package-", - )); + let server = PackseServer::new("fork/allows-non-conflicting-repeated-dependencies.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -525,16 +515,18 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-allows-non-conflicting-repeated-dependencies-a>=1''', - '''fork-allows-non-conflicting-repeated-dependencies-a<2''', + '''a>=1''', + '''a<2''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -556,12 +548,12 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { requires-python = ">=3.12" [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_allows_non_conflicting_repeated_dependencies_a-1.0.0.tar.gz", hash = "sha256:4666c8498ab4aa641bacb39c2fb379ed87730d4de89bd7797c388a3c748f9f89" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_allows_non_conflicting_repeated_dependencies_a-1.0.0-py3-none-any.whl", hash = "sha256:77f0eb64e8c3bef8dec459d90c0394069eccb40d0c6c978d97bc1c4089d7d626" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -569,13 +561,13 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, + { name = "a" }, ] [package.metadata] requires-dist = [ - { name = "package-a", specifier = "<2" }, - { name = "package-a", specifier = ">=1" }, + { name = "a", specifier = "<2" }, + { name = "a", specifier = ">=1" }, ] "# ); @@ -587,7 +579,7 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -597,14 +589,15 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { /// An extremely basic test of universal resolution. In this case, the resolution /// should contain two distinct versions of `a` depending on `sys_platform`. /// +/// /// ```text /// fork-basic /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// └── a /// ├── a-1.0.0 @@ -613,10 +606,7 @@ fn fork_allows_non_conflicting_repeated_dependencies() -> Result<()> { #[test] fn fork_basic() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-basic-", "package-")); + let server = PackseServer::new("fork/basic.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -625,16 +615,18 @@ fn fork_basic() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-basic-a>=2; sys_platform == "linux"''', - '''fork-basic-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -661,27 +653,27 @@ fn fork_basic() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-1.0.0.tar.gz", hash = "sha256:a81cba8fd1453d8fdf35ba4b3d8c536d2f9fa4e2ceb6312f497ec608a5262663" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-1.0.0-py3-none-any.whl", hash = "sha256:900f299c08b6e0c1ec013259c13ff9279f5672fb395418fd00f937a3830a7edb" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-2.0.0.tar.gz", hash = "sha256:3bbd7b86a9b7870ddcfdf343ed8555f414729053b171b3b072c1fef21478feb2" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-2.0.0-py3-none-any.whl", hash = "sha256:4fa08d0429882d46c4c5262630b174b44aa633fc9d0c3f6a90e17406d6d7eb5a" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -689,14 +681,14 @@ fn fork_basic() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -708,7 +700,7 @@ fn fork_basic() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -717,14 +709,15 @@ fn fork_basic() -> Result<()> { /// We have a conflict after forking. This scenario exists to test the error message. /// +/// /// ```text /// conflict-in-fork /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "os1" +/// │ ├── requires a>=2 ; sys_platform == 'os1' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "os2" +/// │ └── requires a<2 ; sys_platform == 'os2' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 @@ -748,10 +741,7 @@ fn fork_basic() -> Result<()> { #[test] fn conflict_in_fork() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"conflict-in-fork-", "package-")); + let server = PackseServer::new("fork/conflict-in-fork.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -760,16 +750,18 @@ fn conflict_in_fork() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''conflict-in-fork-a>=2; sys_platform == "os1"''', - '''conflict-in-fork-a<2; sys_platform == "os2"''', + '''a>=2 ; sys_platform == 'os1'''', + '''a<2 ; sys_platform == 'os2'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: false exit_code: 1 @@ -777,13 +769,13 @@ fn conflict_in_fork() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies for split (markers: sys_platform == 'os2'): - ╰─▶ Because only package-b==1.0.0 is available and package-b==1.0.0 depends on package-d==1, we can conclude that all versions of package-b depend on package-d==1. - And because package-c==1.0.0 depends on package-d==2 and only package-c==1.0.0 is available, we can conclude that all versions of package-b and all versions of package-c are incompatible. - And because package-a==1.0.0 depends on package-b and package-c, we can conclude that package-a==1.0.0 cannot be used. - And because only the following versions of package-a{sys_platform == 'os2'} are available: - package-a{sys_platform == 'os2'}==1.0.0 - package-a{sys_platform == 'os2'}>=2 - and your project depends on package-a{sys_platform == 'os2'}<2, we can conclude that your project's requirements are unsatisfiable. + ╰─▶ Because only b==1.0.0 is available and b==1.0.0 depends on d==1, we can conclude that all versions of b depend on d==1. + And because c==1.0.0 depends on d==2 and only c==1.0.0 is available, we can conclude that all versions of b and all versions of c are incompatible. + And because a==1.0.0 depends on b and c, we can conclude that a==1.0.0 cannot be used. + And because only the following versions of a{sys_platform == 'os2'} are available: + a{sys_platform == 'os2'}==1.0.0 + a{sys_platform == 'os2'}>=2 + and your project depends on a{sys_platform == 'os2'}<2, we can conclude that your project's requirements are unsatisfiable. hint: The resolution failed for an environment that is not the current one, consider limiting the environments with `tool.uv.environments`. " @@ -800,6 +792,7 @@ fn conflict_in_fork() -> Result<()> { /// overlapping. (Well, there aren't any marker expressions here, which /// means they are both unconditional.) /// +/// /// ```text /// fork-conflict-unsatisfiable /// ├── environment @@ -818,10 +811,7 @@ fn conflict_in_fork() -> Result<()> { #[test] fn fork_conflict_unsatisfiable() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-conflict-unsatisfiable-", "package-")); + let server = PackseServer::new("fork/conflict-unsatisfiable.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -830,16 +820,18 @@ fn fork_conflict_unsatisfiable() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-conflict-unsatisfiable-a>=2''', - '''fork-conflict-unsatisfiable-a<2''', + '''a>=2''', + '''a<2''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: false exit_code: 1 @@ -847,7 +839,7 @@ fn fork_conflict_unsatisfiable() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because your project depends on package-a>=2 and package-a<2, we can conclude that your project's requirements are unsatisfiable. + ╰─▶ Because your project depends on a>=2 and a<2, we can conclude that your project's requirements are unsatisfiable. " ); @@ -868,18 +860,19 @@ fn fork_conflict_unsatisfiable() -> Result<()> { /// The correct thing to do here is to ensure that `b` is only part of the /// `a==4.4.0` fork and `c` is only par of the `a==4.3.0` fork. /// +/// /// ```text /// fork-filter-sibling-dependencies /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a==4.4.0; sys_platform == "linux" +/// │ ├── requires a==4.4.0 ; sys_platform == 'linux' /// │ │ └── satisfied by a-4.4.0 -/// │ ├── requires a==4.3.0; sys_platform == "darwin" +/// │ ├── requires a==4.3.0 ; sys_platform == 'darwin' /// │ │ └── satisfied by a-4.3.0 -/// │ ├── requires b==1.0.0; sys_platform == "linux" +/// │ ├── requires b==1.0.0 ; sys_platform == 'linux' /// │ │ └── satisfied by b-1.0.0 -/// │ └── requires c==1.0.0; sys_platform == "darwin" +/// │ └── requires c==1.0.0 ; sys_platform == 'darwin' /// │ └── satisfied by c-1.0.0 /// ├── a /// │ ├── a-4.3.0 @@ -899,10 +892,7 @@ fn fork_conflict_unsatisfiable() -> Result<()> { #[test] fn fork_filter_sibling_dependencies() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-filter-sibling-dependencies-", "package-")); + let server = PackseServer::new("fork/filter-sibling-dependencies.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -911,18 +901,20 @@ fn fork_filter_sibling_dependencies() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-filter-sibling-dependencies-a==4.4.0; sys_platform == "linux"''', - '''fork-filter-sibling-dependencies-a==4.3.0; sys_platform == "darwin"''', - '''fork-filter-sibling-dependencies-b==1.0.0; sys_platform == "linux"''', - '''fork-filter-sibling-dependencies-c==1.0.0; sys_platform == "darwin"''', + '''a==4.4.0 ; sys_platform == 'linux'''', + '''a==4.3.0 ; sys_platform == 'darwin'''', + '''b==1.0.0 ; sys_platform == 'linux'''', + '''c==1.0.0 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -949,75 +941,75 @@ fn fork_filter_sibling_dependencies() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "4.3.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.3.0.tar.gz", hash = "sha256:7bd9f28568add1b4f4ae3c75c527376d75cbb34a720b399c36422549fdb5a397" } + sdist = { url = "http://[LOCALHOST]/files/a-4.3.0.tar.gz", hash = "sha256:ae6dc9fc44095c1d8ec669ea9ce6623fb598874d6436e989ccad9fbb8ecf121f", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.3.0-py3-none-any.whl", hash = "sha256:0cfa959f1188954c7426a84da27f310a7302c8814575ede013b6face9c71dd63" }, + { url = "http://[LOCALHOST]/files/a-4.3.0-py3-none-any.whl", hash = "sha256:801f46d2474bf22f2eed823a9a9343480a5ea089a45e949dbb1aad91a32cc14f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "4.4.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.4.0.tar.gz", hash = "sha256:3ff647ba5d00e7efd6f81922fdc037af1b3d924820304b1dce5aa3e2bb0ebc17" } + sdist = { url = "http://[LOCALHOST]/files/a-4.4.0.tar.gz", hash = "sha256:d531c73543a10f88aa8cb084ab856c7f369d442d09ad3ffdeb4a1771590b1d0f", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.4.0-py3-none-any.whl", hash = "sha256:996fd9369bfc2cd4538d5ef8ead9858574a4c692528874160515570a83986d82" }, + { url = "http://[LOCALHOST]/files/a-4.4.0-py3-none-any.whl", hash = "sha256:3df7c088229de8a0ee9765d9da6f543d5b40155fade904f0c95780fc843c8ed7", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-d", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "d", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_b-1.0.0.tar.gz", hash = "sha256:5cf545d94aae6b0be2476c2bfe9412bea2a01df9dec426235528214faedc3c30" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:e86cc7db081c964c8874d032149c4e84b3f23563c517beb7bedfa875a8f9f4ce", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_b-1.0.0-py3-none-any.whl", hash = "sha256:1e1027c7f771947bfc45d7ba1c45056af51677360f924f206f89deff41c3adba" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:ee44ff0b8963063959a61fdafa6d9742f5e03efec800a3661e4b081bb6343be1", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-d", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, + { name = "d", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_c-1.0.0.tar.gz", hash = "sha256:d1fead7e86d6a81678fa69cb5de6724d0cbfc196bdd6771a6d2029bc0a6dbafe" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:bc02a5bce314ce38dbd74b2132c25979a09f132c32808726ea3c9a793bd125a9", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_c-1.0.0-py3-none-any.whl", hash = "sha256:add817457a9772f2f81c36517b9a674017332bc634fefe0402810d87230d0609" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-py3-none-any.whl", hash = "sha256:c243070ddb01de3029aebe2536aed4b43f324ebaef3a156304c9c4d7ae2f6a63", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-d" + name = "d" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-1.0.0.tar.gz", hash = "sha256:5bafd1cfed4b7bb07af597f0817c3e3b2dd178ab26e9d00e3ec119440dbe7867" } + sdist = { url = "http://[LOCALHOST]/files/d-1.0.0.tar.gz", hash = "sha256:4f363304bad30565286697b70b1b48e348267d318562a3afb36af66a8a8cad1d", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-1.0.0-py3-none-any.whl", hash = "sha256:2c07fc89a83f28ee8b8bfb73d5fd3578555c60c3645b6502ff89ce4a4ee47ff3" }, + { url = "http://[LOCALHOST]/files/d-1.0.0-py3-none-any.whl", hash = "sha256:362166e5bd895367cc4ba5b7327949b7d417fe30cb3273a76b5db4a280dac05d", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-d" + name = "d" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-2.0.0.tar.gz", hash = "sha256:f6c80be71f98c3e0cd3d5a2a7d9be19a9651dd5657a29e5b0f4531d59a611a0b" } + sdist = { url = "http://[LOCALHOST]/files/d-2.0.0.tar.gz", hash = "sha256:710cbab9073b67674e70a5f5225f81fe5496739fb4d14e6b9ecec40109290ee9", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-2.0.0-py3-none-any.whl", hash = "sha256:b11cb3ef68f972b6b1ebf52d34310ea0e5962c7f618a3eb00db3ffc6c5c6d49f" }, + { url = "http://[LOCALHOST]/files/d-2.0.0-py3-none-any.whl", hash = "sha256:3cf4357f0fdce5ed2a98b8befcef84e8b8150244737481ebc51d3d9ec000bc3b", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -1025,18 +1017,18 @@ fn fork_filter_sibling_dependencies() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "4.3.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "4.4.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-b", marker = "sys_platform == 'linux'" }, - { name = "package-c", marker = "sys_platform == 'darwin'" }, + { name = "a", version = "4.3.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "4.4.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "b", marker = "sys_platform == 'linux'" }, + { name = "c", marker = "sys_platform == 'darwin'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "==4.3.0" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = "==4.4.0" }, - { name = "package-b", marker = "sys_platform == 'linux'", specifier = "==1.0.0" }, - { name = "package-c", marker = "sys_platform == 'darwin'", specifier = "==1.0.0" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "==4.3.0" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = "==4.4.0" }, + { name = "b", marker = "sys_platform == 'linux'", specifier = "==1.0.0" }, + { name = "c", marker = "sys_platform == 'darwin'", specifier = "==1.0.0" }, ] "# ); @@ -1048,7 +1040,7 @@ fn fork_filter_sibling_dependencies() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -1057,6 +1049,7 @@ fn fork_filter_sibling_dependencies() -> Result<()> { /// This test checks that we discard fork markers when using `--upgrade`. /// +/// /// ```text /// fork-upgrade /// ├── environment @@ -1070,9 +1063,9 @@ fn fork_filter_sibling_dependencies() -> Result<()> { /// │ └── bar-2.0.0 /// └── foo /// ├── foo-1.0.0 -/// │ ├── requires bar==1; sys_platform == "linux" +/// │ ├── requires bar==1 ; sys_platform == 'linux' /// │ │ └── satisfied by bar-1.0.0 -/// │ └── requires bar==2; sys_platform != "linux" +/// │ └── requires bar==2 ; sys_platform != 'linux' /// │ └── satisfied by bar-2.0.0 /// └── foo-2.0.0 /// └── requires bar==2 @@ -1081,10 +1074,7 @@ fn fork_filter_sibling_dependencies() -> Result<()> { #[test] fn fork_upgrade() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-upgrade-", "package-")); + let server = PackseServer::new("fork/fork-upgrade.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1093,15 +1083,17 @@ fn fork_upgrade() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-upgrade-foo''', + '''foo''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -1123,24 +1115,24 @@ fn fork_upgrade() -> Result<()> { requires-python = ">=3.12" [[package]] - name = "package-bar" + name = "bar" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_upgrade_bar-2.0.0.tar.gz", hash = "sha256:ad9667b61721151a0ace7cd482b0486eef8f0e41d1317c4051e4305d9e5b7eba" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/bar-2.0.0.tar.gz", hash = "sha256:fa9a4faf506228722784ed740a362bccd96913f4f98a4e10d45ab79d8abb270a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_upgrade_bar-2.0.0-py3-none-any.whl", hash = "sha256:be280a030a3094647684fcbf8d7d9ada41274adb3414b56e299c8df29ded92d0" }, + { url = "http://[LOCALHOST]/files/bar-2.0.0-py3-none-any.whl", hash = "sha256:563b1af3238a4ad819f2b95b74f940319a2ef30ed7991a2416fa98aa115da87d", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-foo" + name = "foo" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-bar" }, + { name = "bar" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_upgrade_foo-2.0.0.tar.gz", hash = "sha256:4f26ade9a4954b1bc419e19f130d47ac9a8a8c7ad7f446085938f29cfb6a7f30" } + sdist = { url = "http://[LOCALHOST]/files/foo-2.0.0.tar.gz", hash = "sha256:447cd218ce6ee5c4cc8ba7bf5431bd114d5336fa6214a91f28c6fc9ade89bf4c", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_upgrade_foo-2.0.0-py3-none-any.whl", hash = "sha256:af4548f32eb03890ad85d538acae65797fd339e2fef3b52538d629cfb46f191e" }, + { url = "http://[LOCALHOST]/files/foo-2.0.0-py3-none-any.whl", hash = "sha256:36953b42725b8f3b6ebde327b8fab1d1e906ce0902c6272855851ea1964bc37a", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -1148,11 +1140,11 @@ fn fork_upgrade() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-foo" }, + { name = "foo" }, ] [package.metadata] - requires-dist = [{ name = "package-foo" }] + requires-dist = [{ name = "foo" }] "# ); }); @@ -1163,7 +1155,7 @@ fn fork_upgrade() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -1176,14 +1168,15 @@ fn fork_upgrade() -> Result<()> { /// instead of having two forks around but without Python 3.13 and omitting /// `c` from the solution. /// +/// /// ```text /// fork-incomplete-markers /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a==1; python_version < "3.13" +/// │ ├── requires a==1 ; python_full_version < '3.13' /// │ │ └── satisfied by a-1.0.0 -/// │ ├── requires a==2; python_version >= "3.14" +/// │ ├── requires a==2 ; python_full_version >= '3.14' /// │ │ └── satisfied by a-2.0.0 /// │ └── requires b /// │ └── satisfied by b-1.0.0 @@ -1192,7 +1185,7 @@ fn fork_upgrade() -> Result<()> { /// │ └── a-2.0.0 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c; python_version == "3.13" +/// │ └── requires c ; python_full_version == '3.13.*' /// │ └── satisfied by c-1.0.0 /// └── c /// └── c-1.0.0 @@ -1200,10 +1193,7 @@ fn fork_upgrade() -> Result<()> { #[test] fn fork_incomplete_markers() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-incomplete-markers-", "package-")); + let server = PackseServer::new("fork/incomplete-markers.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1212,17 +1202,19 @@ fn fork_incomplete_markers() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-incomplete-markers-a==1; python_version < "3.13"''', - '''fork-incomplete-markers-a==2; python_version >= "3.14"''', - '''fork-incomplete-markers-b''', + '''a==1 ; python_full_version < '3.13'''', + '''a==2 ; python_full_version >= '3.14'''', + '''b''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -1249,48 +1241,48 @@ fn fork_incomplete_markers() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "python_full_version < '3.13'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-1.0.0.tar.gz", hash = "sha256:47c0f25a9e21c68f14173c556ebc43656d4e3fa75a2802ff5cd8df98deaf965d" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-1.0.0-py3-none-any.whl", hash = "sha256:3cdba0dffc4e4fc3b01fbf5b8529b9f85d9811917984cd587d717ae84d853893" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:f936eedc194aa91ca01a4c6c9981136ca6c75ce6df47e3951b12522881dce809", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "python_full_version >= '3.14'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-2.0.0.tar.gz", hash = "sha256:e32ea9a40f05ad71fc666a4f3441020d9c50023b75b75c5d865a89437885831f" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-2.0.0-py3-none-any.whl", hash = "sha256:4b2a4ca84cae18d8c49ad87ec457a60a8fbe1d1160c61d2aeb473cf8c2b8d9e3" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "python_full_version == '3.13.*'" }, + { name = "c", marker = "python_full_version == '3.13.*'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_b-1.0.0.tar.gz", hash = "sha256:99a13ea3286cafd8bdd4054fe0a966950574817d1973e30bcc230502b98f0be7" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:6f887656a85fbbf549dddf2da49a17c6b55ca2a31a62ca030e82b8fdfd593177", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_b-1.0.0-py3-none-any.whl", hash = "sha256:f429325c6d7cef0721bca12825f9f38ca781cd633ab305526ce9e2af6d43d68e" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:b8c3f2688065abd235cc88b32446d4c807cfa3a1f2d676874bccc7e0f63137bd", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_c-1.0.0.tar.gz", hash = "sha256:e1a20988cf66fda67faf7009b002e807d457c38f32a7802772481c00bb734fb8" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:6e14a2e7cc6be61fa5aa41c0e55beff8b708a3aea257fed948306a0741bb5c47", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_c-1.0.0-py3-none-any.whl", hash = "sha256:9d5e2b822bdae8076d9f5a5fa71307eff4d82e7ce9208dc247b1868cf3afe31a" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-py3-none-any.whl", hash = "sha256:78c0da7c5681d751d38b2e60c78d1e29d6125d91e68e5aeb22372fa66527ff95", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -1298,16 +1290,16 @@ fn fork_incomplete_markers() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_full_version < '3.13'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_full_version >= '3.14'" }, - { name = "package-b" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version < '3.13'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version >= '3.14'" }, + { name = "b" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "python_full_version < '3.13'", specifier = "==1" }, - { name = "package-a", marker = "python_full_version >= '3.14'", specifier = "==2" }, - { name = "package-b" }, + { name = "a", marker = "python_full_version < '3.13'", specifier = "==1" }, + { name = "a", marker = "python_full_version >= '3.14'", specifier = "==2" }, + { name = "b" }, ] "# ); @@ -1319,7 +1311,7 @@ fn fork_incomplete_markers() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -1332,22 +1324,23 @@ fn fork_incomplete_markers() -> Result<()> { /// darwin`, even though `linux OR darwin` doesn't actually appear verbatim as a /// marker expression for any dependency on `c`. /// +/// /// ```text /// fork-marker-accrue /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a==1.0.0; implementation_name == "cpython" +/// │ ├── requires a==1.0.0 ; implementation_name == 'cpython' /// │ │ └── satisfied by a-1.0.0 -/// │ └── requires b==1.0.0; implementation_name == "pypy" +/// │ └── requires b==1.0.0 ; implementation_name == 'pypy' /// │ └── satisfied by b-1.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ └── requires c==1.0.0; sys_platform == "linux" +/// │ └── requires c==1.0.0 ; sys_platform == 'linux' /// │ └── satisfied by c-1.0.0 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c==1.0.0; sys_platform == "darwin" +/// │ └── requires c==1.0.0 ; sys_platform == 'darwin' /// │ └── satisfied by c-1.0.0 /// └── c /// └── c-1.0.0 @@ -1355,10 +1348,7 @@ fn fork_incomplete_markers() -> Result<()> { #[test] fn fork_marker_accrue() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-accrue-", "package-")); + let server = PackseServer::new("fork/marker-accrue.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1367,16 +1357,18 @@ fn fork_marker_accrue() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-accrue-a==1.0.0; implementation_name == "cpython"''', - '''fork-marker-accrue-b==1.0.0; implementation_name == "pypy"''', + '''a==1.0.0 ; implementation_name == 'cpython'''', + '''b==1.0.0 ; implementation_name == 'pypy'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -1398,36 +1390,36 @@ fn fork_marker_accrue() -> Result<()> { requires-python = ">=3.12" [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "sys_platform == 'linux'" }, + { name = "c", marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_accrue_a-1.0.0.tar.gz", hash = "sha256:275ed50999adcfd2bbdf2ce621b7a4f850b3b314bc9e9f490d8e414cff19134d" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:8fc0cb224becb328b822daf159f2277eae1be228b916d6caa86484d2d83a5235", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_accrue_a-1.0.0-py3-none-any.whl", hash = "sha256:7d83ea2071fee0d995d31ff7c7ff4db7e4ded2d7e5faa8143fce0296cf9a7a11" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:8ed25f2453465e5f1e91e7b09e21b08234389dc5d6e3f7f27e89e801ba42d807", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "sys_platform == 'darwin'" }, + { name = "c", marker = "sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_accrue_b-1.0.0.tar.gz", hash = "sha256:1944e0bfb15477a4dcace6a94fdb885a5e699c7c11c2ec81ee5f999a965d98ab" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:71e4e9b4e8b7ebf5c34d935f8934c4b3e762ece483247af9ff7913b0d986b96e", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_accrue_b-1.0.0-py3-none-any.whl", hash = "sha256:775f67358cd784b5d0a5880bc013beb34efbace88d0329fabfeb5093b7e7bbfa" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:e21416492e59fd38a6e084b303f29fcde08805f201a4f9e4833c60cd18ebfc4f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_accrue_c-1.0.0.tar.gz", hash = "sha256:0d915ef7a76eed8e00c350dbe83fcff7aec77dba739e5a671cbfba69abca4d94" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:6e14a2e7cc6be61fa5aa41c0e55beff8b708a3aea257fed948306a0741bb5c47", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_accrue_c-1.0.0-py3-none-any.whl", hash = "sha256:4a6d7a72793d13a666302e6dd2b9ac684c202f53c854c5eef55700067924a23b" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-py3-none-any.whl", hash = "sha256:78c0da7c5681d751d38b2e60c78d1e29d6125d91e68e5aeb22372fa66527ff95", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -1435,14 +1427,14 @@ fn fork_marker_accrue() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", marker = "implementation_name == 'cpython'" }, - { name = "package-b", marker = "implementation_name == 'pypy'" }, + { name = "a", marker = "implementation_name == 'cpython'" }, + { name = "b", marker = "implementation_name == 'pypy'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "implementation_name == 'cpython'", specifier = "==1.0.0" }, - { name = "package-b", marker = "implementation_name == 'pypy'", specifier = "==1.0.0" }, + { name = "a", marker = "implementation_name == 'cpython'", specifier = "==1.0.0" }, + { name = "b", marker = "implementation_name == 'pypy'", specifier = "==1.0.0" }, ] "# ); @@ -1454,7 +1446,7 @@ fn fork_marker_accrue() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -1473,14 +1465,15 @@ fn fork_marker_accrue() -> Result<()> { /// both `1.0.0` and `2.0.0` of `a`. But of course, the correct behavior is to fail /// resolving. /// +/// /// ```text /// fork-marker-disjoint /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "linux" +/// │ └── requires a<2 ; sys_platform == 'linux' /// │ └── satisfied by a-1.0.0 /// └── a /// ├── a-1.0.0 @@ -1489,10 +1482,7 @@ fn fork_marker_accrue() -> Result<()> { #[test] fn fork_marker_disjoint() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-disjoint-", "package-")); + let server = PackseServer::new("fork/marker-disjoint.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1501,16 +1491,18 @@ fn fork_marker_disjoint() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-disjoint-a>=2; sys_platform == "linux"''', - '''fork-marker-disjoint-a<2; sys_platform == "linux"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'linux'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: false exit_code: 1 @@ -1518,7 +1510,7 @@ fn fork_marker_disjoint() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because your project depends on package-a{sys_platform == 'linux'}>=2 and package-a{sys_platform == 'linux'}<2, we can conclude that your project's requirements are unsatisfiable. + ╰─▶ Because your project depends on a{sys_platform == 'linux'}>=2 and a{sys_platform == 'linux'}<2, we can conclude that your project's requirements are unsatisfiable. " ); @@ -1532,25 +1524,26 @@ fn fork_marker_disjoint() -> Result<()> { /// `implementation_name == 'pypy'`. So in this case, `c` should be /// included. /// +/// /// ```text /// fork-marker-inherit-combined-allowed /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ ├── requires b>=2; implementation_name == "cpython" +/// │ │ ├── requires b>=2 ; implementation_name == 'cpython' /// │ │ │ └── satisfied by b-2.0.0 -/// │ │ └── requires b<2; implementation_name == "pypy" +/// │ │ └── requires b<2 ; implementation_name == 'pypy' /// │ │ └── satisfied by b-1.0.0 /// │ └── a-2.0.0 /// ├── b /// │ ├── b-1.0.0 -/// │ │ └── requires c; sys_platform == "linux" or implementation_name == "pypy" +/// │ │ └── requires c ; implementation_name == 'pypy' or sys_platform == 'linux' /// │ │ └── satisfied by c-1.0.0 /// │ └── b-2.0.0 /// └── c @@ -1559,10 +1552,7 @@ fn fork_marker_disjoint() -> Result<()> { #[test] fn fork_marker_inherit_combined_allowed() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-inherit-combined-allowed-", "package-")); + let server = PackseServer::new("fork/marker-inherit-combined-allowed.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1571,16 +1561,18 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-inherit-combined-allowed-a>=2; sys_platform == "linux"''', - '''fork-marker-inherit-combined-allowed-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -1609,69 +1601,69 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'pypy' and sys_platform == 'darwin'", "implementation_name == 'cpython' and sys_platform == 'darwin'", "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", ] dependencies = [ - { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, - { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'cpython' and sys_platform == 'darwin'" }, + { name = "b", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, + { name = "b", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "implementation_name == 'cpython' and sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_a-1.0.0.tar.gz", hash = "sha256:d6593d7102007515a301ed790fecf1b6366d8fe6c3bbc24a31497f91c44eaac6" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:7e3d8e97204e1ea461ae625819c340a5a762b70f34421d4f9721bb7b607a0a93", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_a-1.0.0-py3-none-any.whl", hash = "sha256:887cde670bb39a789806e7d1a1b562d084ab163b3a3d08e20ec706b0d4453029" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:ebe588eab684413e5969ec398e03f7386a8106c5c88a601dacb2781fe2d0c819", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_a-2.0.0.tar.gz", hash = "sha256:d822588720e6f9627a002d7c2cf97a4a8d74e530265b0b288aef2b957327e4e9" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_a-2.0.0-py3-none-any.whl", hash = "sha256:88f87116434130aacff501c430f5413a8d3c2e38f18cd89d0504b37716ecc786" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'pypy' and sys_platform == 'darwin'", ] dependencies = [ - { name = "package-c", marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, + { name = "c", marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_b-1.0.0.tar.gz", hash = "sha256:a1f33681f42aa5b47c7d05b93f013f8b9555b2ec2df49a42589dc06cab391797" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:7bbe7def4bbd23115cf653d246419c773afd3c1b3e6ba94f19acbc1742f51d08", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_b-1.0.0-py3-none-any.whl", hash = "sha256:da22041f6a3ec9d3a9e72fc4a3c7a9bb1b336b25bc48b5e15657866a12d34657" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:94d6ef21aaf5389c9ec11da5f313697b2f5a3b35f039735d2bcf0cfb7a6f88d1", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'cpython' and sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_b-2.0.0.tar.gz", hash = "sha256:0509fdaeb0ad18de20daabb084aa27c198db538636d8b1c1fdfe65ed0943f7d2" } + sdist = { url = "http://[LOCALHOST]/files/b-2.0.0.tar.gz", hash = "sha256:18fb09ba28eba255186405065e027093a6e952fa71eb565b4c46d619fdb60809", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_b-2.0.0-py3-none-any.whl", hash = "sha256:030efe965f6e36cf4a01e3fd133ac427cee33da4d2b4703c3af560005efca454" }, + { url = "http://[LOCALHOST]/files/b-2.0.0-py3-none-any.whl", hash = "sha256:04cc57f7563029528b6d23283933b244b6f52ba1543fad54687c586d6e639fc4", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_c-1.0.0.tar.gz", hash = "sha256:4a69f0cdfcd06caa73c2e16f11adfc3eeeac3c44210beb3aaf1dad4436351ceb" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:6e14a2e7cc6be61fa5aa41c0e55beff8b708a3aea257fed948306a0741bb5c47", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_c-1.0.0-py3-none-any.whl", hash = "sha256:d936683675b39b426aad63f3047792f0c2609b6a459ab17d2d0b86d592be4c6e" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-py3-none-any.whl", hash = "sha256:78c0da7c5681d751d38b2e60c78d1e29d6125d91e68e5aeb22372fa66527ff95", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -1679,14 +1671,14 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -1698,7 +1690,7 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -1713,25 +1705,26 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { /// `implementation_name == 'cpython'`. Therefore, `c` should not be /// included here. /// +/// /// ```text /// fork-marker-inherit-combined-disallowed /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ ├── requires b>=2; implementation_name == "cpython" +/// │ │ ├── requires b>=2 ; implementation_name == 'cpython' /// │ │ │ └── satisfied by b-2.0.0 -/// │ │ └── requires b<2; implementation_name == "pypy" +/// │ │ └── requires b<2 ; implementation_name == 'pypy' /// │ │ └── satisfied by b-1.0.0 /// │ └── a-2.0.0 /// ├── b /// │ ├── b-1.0.0 -/// │ │ └── requires c; sys_platform == "linux" or implementation_name == "cpython" +/// │ │ └── requires c ; implementation_name == 'cpython' or sys_platform == 'linux' /// │ │ └── satisfied by c-1.0.0 /// │ └── b-2.0.0 /// └── c @@ -1740,10 +1733,7 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { #[test] fn fork_marker_inherit_combined_disallowed() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-inherit-combined-disallowed-", "package-")); + let server = PackseServer::new("fork/marker-inherit-combined-disallowed.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1752,16 +1742,18 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-inherit-combined-disallowed-a>=2; sys_platform == "linux"''', - '''fork-marker-inherit-combined-disallowed-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -1790,57 +1782,57 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'pypy' and sys_platform == 'darwin'", "implementation_name == 'cpython' and sys_platform == 'darwin'", "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", ] dependencies = [ - { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, - { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'cpython' and sys_platform == 'darwin'" }, + { name = "b", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, + { name = "b", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "implementation_name == 'cpython' and sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_a-1.0.0.tar.gz", hash = "sha256:c5f27540b38e11066006d693119bf351ce8eda80c6ad2b283c7db1b39ec1acbf" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:7e3d8e97204e1ea461ae625819c340a5a762b70f34421d4f9721bb7b607a0a93", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_a-1.0.0-py3-none-any.whl", hash = "sha256:7607e3377c7c4f0cb0e968bfe4e3ed1d3b8b19fceb42a48143e5ed138dca1346" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:ebe588eab684413e5969ec398e03f7386a8106c5c88a601dacb2781fe2d0c819", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_a-2.0.0.tar.gz", hash = "sha256:1a98bb0369a890195060689cbad646c9c5aec9f2ead3b2340fc52cfbb252ead5" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_a-2.0.0-py3-none-any.whl", hash = "sha256:f8db9a220f480a2655cc54efe47c603037a91935c95d3da48bc408810b3bc41e" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'pypy' and sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-1.0.0.tar.gz", hash = "sha256:15005f7c07b199cb2d526cc45776bef8327a0d6e7a9cc773a318da76abafc046" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:f615a5b13329186c0948d63a275af18758e1346ad512f06366b0534e1c4e3ab3", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-1.0.0-py3-none-any.whl", hash = "sha256:1ea9b95b6aa8ae09ff6711bc175c854d4caf4c3ad9ab69b1da3d6cec4a6cb7ed" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:b9dea47846d57e4a52afe31d36ad8fc1e7c01505c768be8855222320a9028f3c", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'cpython' and sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-2.0.0.tar.gz", hash = "sha256:5ed91f43bd7013a8d4d1593a5aadc5a9e0d8629d5ee16f1357f89d24f20be2b9" } + sdist = { url = "http://[LOCALHOST]/files/b-2.0.0.tar.gz", hash = "sha256:18fb09ba28eba255186405065e027093a6e952fa71eb565b4c46d619fdb60809", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-2.0.0-py3-none-any.whl", hash = "sha256:d4f998f9d1a9f8714347565237d2f1502d9699942c3cdd86cdb4ea7c5c7e5796" }, + { url = "http://[LOCALHOST]/files/b-2.0.0-py3-none-any.whl", hash = "sha256:04cc57f7563029528b6d23283933b244b6f52ba1543fad54687c586d6e639fc4", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -1848,14 +1840,14 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -1867,7 +1859,7 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -1883,25 +1875,26 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { /// expression that provoked the *first* fork. Therefore, `c` should be /// entirely excluded from the resolution. /// +/// /// ```text /// fork-marker-inherit-combined /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ ├── requires b>=2; implementation_name == "cpython" +/// │ │ ├── requires b>=2 ; implementation_name == 'cpython' /// │ │ │ └── satisfied by b-2.0.0 -/// │ │ └── requires b<2; implementation_name == "pypy" +/// │ │ └── requires b<2 ; implementation_name == 'pypy' /// │ │ └── satisfied by b-1.0.0 /// │ └── a-2.0.0 /// ├── b /// │ ├── b-1.0.0 -/// │ │ └── requires c; sys_platform == "linux" +/// │ │ └── requires c ; sys_platform == 'linux' /// │ │ └── satisfied by c-1.0.0 /// │ └── b-2.0.0 /// └── c @@ -1910,10 +1903,7 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { #[test] fn fork_marker_inherit_combined() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-inherit-combined-", "package-")); + let server = PackseServer::new("fork/marker-inherit-combined.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -1922,16 +1912,18 @@ fn fork_marker_inherit_combined() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-inherit-combined-a>=2; sys_platform == "linux"''', - '''fork-marker-inherit-combined-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -1960,57 +1952,57 @@ fn fork_marker_inherit_combined() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'pypy' and sys_platform == 'darwin'", "implementation_name == 'cpython' and sys_platform == 'darwin'", "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", ] dependencies = [ - { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, - { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'cpython' and sys_platform == 'darwin'" }, + { name = "b", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "implementation_name == 'pypy' and sys_platform == 'darwin'" }, + { name = "b", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "implementation_name == 'cpython' and sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_a-1.0.0.tar.gz", hash = "sha256:7abc9661d5e25b05f7dd170fcda8f4abe0f6005768efd5f48b20be876e532954" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:7e3d8e97204e1ea461ae625819c340a5a762b70f34421d4f9721bb7b607a0a93", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_a-1.0.0-py3-none-any.whl", hash = "sha256:d904b3e7317e89d18a9497d93cbf322b101ec1a08aeb3ef911666382e91b12d7" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:ebe588eab684413e5969ec398e03f7386a8106c5c88a601dacb2781fe2d0c819", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_a-2.0.0.tar.gz", hash = "sha256:413d4b8f065d7c36eb99760218d2a755242b2682d3976a7adfcc1dd2e2414e44" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_a-2.0.0-py3-none-any.whl", hash = "sha256:21d5b138fbb524ce43f65b2d0a9791e73e64ceb3d88f4be42f2a07bd41520412" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'pypy' and sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-1.0.0.tar.gz", hash = "sha256:1a52000f85375ddf93f7094d36d4c613af75b4cfc0638edb5be8ad4f984a74b2" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:675a6c7a1456ba55a2bb89763b3e58b9086a120918d8a9965b616f81f77150fb", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-1.0.0-py3-none-any.whl", hash = "sha256:62bc162290737672b8d017ad0c067298b7ad0ef9ada95ae37c35d0f9a738d8a4" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:42ee7845ea1960c41a676a74c9add4f48915e78f031b0e0a7d3894a692b9b6dd", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "implementation_name == 'cpython' and sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-2.0.0.tar.gz", hash = "sha256:bdeb3b79dc439acb4f5a76c8afd7fb5f5a1e80d89030ba1e0fd3514455505271" } + sdist = { url = "http://[LOCALHOST]/files/b-2.0.0.tar.gz", hash = "sha256:18fb09ba28eba255186405065e027093a6e952fa71eb565b4c46d619fdb60809", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-2.0.0-py3-none-any.whl", hash = "sha256:cf0d309bcf6092017ecf29fec37f1d0cc80fe59fee3f0488c52e944a913a3a8b" }, + { url = "http://[LOCALHOST]/files/b-2.0.0-py3-none-any.whl", hash = "sha256:04cc57f7563029528b6d23283933b244b6f52ba1543fad54687c586d6e639fc4", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2018,14 +2010,14 @@ fn fork_marker_inherit_combined() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -2037,7 +2029,7 @@ fn fork_marker_inherit_combined() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2051,21 +2043,22 @@ fn fork_marker_inherit_combined() -> Result<()> { /// As with `fork-marker-inherit`, the `a<2` path should exclude `b==1.0.0` /// since their marker expressions are disjoint. /// +/// /// ```text /// fork-marker-inherit-isolated /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ └── requires b; sys_platform == "linux" +/// │ │ └── requires b ; sys_platform == 'linux' /// │ │ └── satisfied by b-1.0.0 /// │ └── a-2.0.0 -/// │ └── requires b; sys_platform == "linux" +/// │ └── requires b ; sys_platform == 'linux' /// │ └── satisfied by b-1.0.0 /// └── b /// └── b-1.0.0 @@ -2073,10 +2066,7 @@ fn fork_marker_inherit_combined() -> Result<()> { #[test] fn fork_marker_inherit_isolated() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-inherit-isolated-", "package-")); + let server = PackseServer::new("fork/marker-inherit-isolated.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2085,16 +2075,18 @@ fn fork_marker_inherit_isolated() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-inherit-isolated-a>=2; sys_platform == "linux"''', - '''fork-marker-inherit-isolated-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -2121,39 +2113,39 @@ fn fork_marker_inherit_isolated() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_a-1.0.0.tar.gz", hash = "sha256:026f954ea486398d169b3bffbcc3619f261de760e30665db52dd4347c4c2051d" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:36c9054329425d5b328167c29b8977798e496b738e0c773de19896aeff397ba6", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_a-1.0.0-py3-none-any.whl", hash = "sha256:df72f591a1280e23989ae5b027b2c08b39b1a059458287cd5e3ac1c5f331119d" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:0f04d2c483396a1af08a8baee4a39f08f4e4a1f9775c3b9bce5245852e864eba", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] dependencies = [ - { name = "package-b", marker = "sys_platform == 'linux'" }, + { name = "b", marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_a-2.0.0.tar.gz", hash = "sha256:048dfffa5cc67b19eb5b4db562119293e1dd49e8c93395d242677216efbb2408" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:1f9238da44c4971ded49abeb4dffd96c319bea753e2b61e4d095cc9110896d13", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_a-2.0.0-py3-none-any.whl", hash = "sha256:57a7c3e75553c34b1e269efb0c462f83eba34160f2c089b5ddab663eb2a95485" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:f455166cf613308f098ecf7d0911ca68838ca203b05c62cdb54b3043ad27c2d0", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_b-1.0.0.tar.gz", hash = "sha256:7e7b0a264412f2f9563e0b0b9a003f0b22fe47d202f804f07e48c0fea29802e4" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:b532bd9c3ccd69c4d5e915542dc50fb748c91c7a8e204c75387178d68fca113f", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_b-1.0.0-py3-none-any.whl", hash = "sha256:c8ff985d36ad17a8f0dd4b36805d02369cb4f9bdc64d794cfd42cbb1ffd98740" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:a4c65510001153cab97a29ff219ad86e0d4330653ca89d9d4c84187ccf14c621", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2161,14 +2153,14 @@ fn fork_marker_inherit_isolated() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -2180,7 +2172,7 @@ fn fork_marker_inherit_isolated() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2194,14 +2186,15 @@ fn fork_marker_inherit_isolated() -> Result<()> { /// with the initial `a<2` dependency. Therefore, it ought to be completely /// excluded from the resolution. /// +/// /// ```text /// fork-marker-inherit-transitive /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 @@ -2214,7 +2207,7 @@ fn fork_marker_inherit_isolated() -> Result<()> { /// │ └── satisfied by c-1.0.0 /// ├── c /// │ └── c-1.0.0 -/// │ └── requires d; sys_platform == "linux" +/// │ └── requires d ; sys_platform == 'linux' /// │ └── satisfied by d-1.0.0 /// └── d /// └── d-1.0.0 @@ -2222,10 +2215,7 @@ fn fork_marker_inherit_isolated() -> Result<()> { #[test] fn fork_marker_inherit_transitive() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-inherit-transitive-", "package-")); + let server = PackseServer::new("fork/marker-inherit-transitive.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2234,16 +2224,18 @@ fn fork_marker_inherit_transitive() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-inherit-transitive-a>=2; sys_platform == "linux"''', - '''fork-marker-inherit-transitive-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -2270,51 +2262,51 @@ fn fork_marker_inherit_transitive() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] dependencies = [ - { name = "package-b", marker = "sys_platform == 'darwin'" }, + { name = "b", marker = "sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_a-1.0.0.tar.gz", hash = "sha256:a6f10f68c6f39595be9e35bcb5bc47c8dff406417da2f949180c9acca5885d32" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3f8ff2b2832415dfda5a576afabc2f8b0e93e0e7a0ee9064b2f9c0a6488c1320", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_a-1.0.0-py3-none-any.whl", hash = "sha256:fc324cbddab19f021387526d0bfc0b63ba8d2994516a5f9cafabb97e93100c70" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:a0f20c0172d171015f1637827325521d0feaa0b85c49058980f660080d96170c", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_a-2.0.0.tar.gz", hash = "sha256:50c12e6503e560beabfc9c6ffd594998d34a34ec27fc395115c239f52d61e582" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_a-2.0.0-py3-none-any.whl", hash = "sha256:26a501017aced48531ec9ed5148f95ab7322c5ceef9bdbf25bba6d950621e474" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "sys_platform == 'darwin'" }, + { name = "c", marker = "sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_b-1.0.0.tar.gz", hash = "sha256:0a26e85593cef47acec83439527e58890ed1afb419b73bf6ccc344df4e1c2072" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:0e68acfea0cd703f2fa3e0a3b12f71228a0a6f5befc5df7f5f907a4cd153a90e", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_b-1.0.0-py3-none-any.whl", hash = "sha256:bc91cd833ffbc3f3dce3d74a5776a440907e8894ea8b2e106699374531f8dfc4" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:b1299c4860508b15790e950862174413d29090c6e08d069fe70297a6d4db5ee0", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_c-1.0.0.tar.gz", hash = "sha256:4ad54ca5a23cdb1810d467603551ff254a3652b55f54f17ab991ab7205ad427e" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:7f4d834ea98e687d4fb313f6b90abcf10a1b574b7273f8157eed433b7371c305", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_c-1.0.0-py3-none-any.whl", hash = "sha256:4cd03a954fb6353f68a76930b79d9ec9aae9bf80ebe23b539434170b4d7819d9" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-py3-none-any.whl", hash = "sha256:a154f7c821c8a9448936b30bcb743e04fb4a187be48bc502e8c465f83a839a0e", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2322,14 +2314,14 @@ fn fork_marker_inherit_transitive() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -2341,7 +2333,7 @@ fn fork_marker_inherit_transitive() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2359,18 +2351,19 @@ fn fork_marker_inherit_transitive() -> Result<()> { /// ignore it because it isn't possible for `sys_platform == 'linux'` and /// `sys_platform == 'darwin'` to be simultaneously true. /// +/// /// ```text /// fork-marker-inherit /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "darwin" +/// │ └── requires a<2 ; sys_platform == 'darwin' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ └── requires b; sys_platform == "linux" +/// │ │ └── requires b ; sys_platform == 'linux' /// │ │ └── satisfied by b-1.0.0 /// │ └── a-2.0.0 /// └── b @@ -2379,10 +2372,7 @@ fn fork_marker_inherit_transitive() -> Result<()> { #[test] fn fork_marker_inherit() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-inherit-", "package-")); + let server = PackseServer::new("fork/marker-inherit.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2391,16 +2381,18 @@ fn fork_marker_inherit() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-inherit-a>=2; sys_platform == "linux"''', - '''fork-marker-inherit-a<2; sys_platform == "darwin"''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -2427,27 +2419,27 @@ fn fork_marker_inherit() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-1.0.0.tar.gz", hash = "sha256:6f045f31d58183fe437f35b34462e5ec9bf0bcee541f423999426aad7ccfe1b3" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:36c9054329425d5b328167c29b8977798e496b738e0c773de19896aeff397ba6", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-1.0.0-py3-none-any.whl", hash = "sha256:63bfd654bcd092e74a8ccae0f496e3b1062a5e9e2d98741f1548f337582c849b" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:0f04d2c483396a1af08a8baee4a39f08f4e4a1f9775c3b9bce5245852e864eba", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-2.0.0.tar.gz", hash = "sha256:3222e12bb0136902b05479736e8731146ab3a01ebf0f666a6fe8c812e1181e33" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-2.0.0-py3-none-any.whl", hash = "sha256:72e2161338e159f2d6bed4cb519bc2ebf94e9daf2cbe5e0ac26d5fbe537f38af" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2455,14 +2447,14 @@ fn fork_marker_inherit() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -2474,7 +2466,7 @@ fn fork_marker_inherit() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2491,25 +2483,26 @@ fn fork_marker_inherit() -> Result<()> { /// `c` for Linux only, should still incorporate `c` as the dependency is /// not part of any fork. /// +/// /// ```text /// fork-marker-limited-inherit /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "linux" +/// │ ├── requires a>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by a-2.0.0 -/// │ ├── requires a<2; sys_platform == "darwin" +/// │ ├── requires a<2 ; sys_platform == 'darwin' /// │ │ └── satisfied by a-1.0.0 /// │ └── requires b /// │ └── satisfied by b-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ └── requires c; sys_platform == "linux" +/// │ │ └── requires c ; sys_platform == 'linux' /// │ │ └── satisfied by c-1.0.0 /// │ └── a-2.0.0 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c; sys_platform == "linux" +/// │ └── requires c ; sys_platform == 'linux' /// │ └── satisfied by c-1.0.0 /// └── c /// └── c-1.0.0 @@ -2517,10 +2510,7 @@ fn fork_marker_inherit() -> Result<()> { #[test] fn fork_marker_limited_inherit() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-limited-inherit-", "package-")); + let server = PackseServer::new("fork/marker-limited-inherit.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2529,17 +2519,19 @@ fn fork_marker_limited_inherit() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-limited-inherit-a>=2; sys_platform == "linux"''', - '''fork-marker-limited-inherit-a<2; sys_platform == "darwin"''', - '''fork-marker-limited-inherit-b''', + '''a>=2 ; sys_platform == 'linux'''', + '''a<2 ; sys_platform == 'darwin'''', + '''b''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -2566,48 +2558,48 @@ fn fork_marker_limited_inherit() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-1.0.0.tar.gz", hash = "sha256:7bb040222692677419ec7074324c46ae68148a4c4feb95f73fab51f88605f4c3" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:c59d625a854e3d8e7cca350ff23a960884bf8a558af994598950e60ecaecf1be", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-1.0.0-py3-none-any.whl", hash = "sha256:2d1c0ba50a12a630bab2870cffc57f27075cd1b6146208ec6ecbd128dc1c697e" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:83b170dfb388aa657648396e796df2890a54c7125464b7087714da4ee7aaab3b", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-2.0.0.tar.gz", hash = "sha256:23cb8783034c2c8fb7d9ce4f404241946dddde2c9e845d2ab19ee161d83d4dcb" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-2.0.0-py3-none-any.whl", hash = "sha256:a15b853867a573c4c3ba8798c7cddac85c19c6f029a1926aa80f37519bb6ce7c" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "sys_platform == 'linux'" }, + { name = "c", marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_b-1.0.0.tar.gz", hash = "sha256:401047757cda70f63f0fd187361d225f34ebae0ed9dfc6c732c54005c58206f4" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:675a6c7a1456ba55a2bb89763b3e58b9086a120918d8a9965b616f81f77150fb", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_b-1.0.0-py3-none-any.whl", hash = "sha256:6533104ddd85f6e10b501e76dd9b324eef8852edd412bfc29aad83a82a2310c3" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:42ee7845ea1960c41a676a74c9add4f48915e78f031b0e0a7d3894a692b9b6dd", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_c-1.0.0.tar.gz", hash = "sha256:3e8b40d054a5cf6d2f68be7750bd83cd305063f99025717df5def825f6236896" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:6e14a2e7cc6be61fa5aa41c0e55beff8b708a3aea257fed948306a0741bb5c47", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_c-1.0.0-py3-none-any.whl", hash = "sha256:17d86b6d2a25639cb06591901bb06353622b5c728e71562fd4c20c7a404fa5f6" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-py3-none-any.whl", hash = "sha256:78c0da7c5681d751d38b2e60c78d1e29d6125d91e68e5aeb22372fa66527ff95", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2615,16 +2607,16 @@ fn fork_marker_limited_inherit() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-b" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "b" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'linux'", specifier = ">=2" }, - { name = "package-b" }, + { name = "a", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "b" }, ] "# ); @@ -2636,7 +2628,7 @@ fn fork_marker_limited_inherit() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2649,6 +2641,7 @@ fn fork_marker_limited_inherit() -> Result<()> { /// file. So this acts as a regression test to ensure that only one version of `a` /// is selected. /// +/// /// ```text /// fork-marker-selection /// ├── environment @@ -2657,9 +2650,9 @@ fn fork_marker_limited_inherit() -> Result<()> { /// │ ├── requires a /// │ │ ├── satisfied by a-0.1.0 /// │ │ └── satisfied by a-0.2.0 -/// │ ├── requires b>=2; sys_platform == "linux" +/// │ ├── requires b>=2 ; sys_platform == 'linux' /// │ │ └── satisfied by b-2.0.0 -/// │ └── requires b<2; sys_platform == "darwin" +/// │ └── requires b<2 ; sys_platform == 'darwin' /// │ └── satisfied by b-1.0.0 /// ├── a /// │ ├── a-0.1.0 @@ -2673,10 +2666,7 @@ fn fork_marker_limited_inherit() -> Result<()> { #[test] fn fork_marker_selection() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-selection-", "package-")); + let server = PackseServer::new("fork/marker-selection.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2685,17 +2675,19 @@ fn fork_marker_selection() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-selection-a''', - '''fork-marker-selection-b>=2; sys_platform == "linux"''', - '''fork-marker-selection-b<2; sys_platform == "darwin"''', + '''a''', + '''b>=2 ; sys_platform == 'linux'''', + '''b<2 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -2722,36 +2714,36 @@ fn fork_marker_selection() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "0.1.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_a-0.1.0.tar.gz", hash = "sha256:f8a3a4de6e08270cbf03e8d1e9d860dd56e57cc57529b8f622a3e6029b68d4e0" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-0.1.0.tar.gz", hash = "sha256:7500398834f46e3567b86bca779e305a94e6039d344819c8672c589c92ba9629", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_a-0.1.0-py3-none-any.whl", hash = "sha256:6c1efed1a7f00b594202e9a811a0c2fc80cb54e62073c8d92d39c82fc745ba48" }, + { url = "http://[LOCALHOST]/files/a-0.1.0-py3-none-any.whl", hash = "sha256:40f95c8868f537e1a289e86f8d75e208fa22d1f3b46c42834f526e89630f77c0", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-1.0.0.tar.gz", hash = "sha256:13419956d96ad6b90ba779235ba6900fe084fbae8dcb288ca6749af98e537b58" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:b532bd9c3ccd69c4d5e915542dc50fb748c91c7a8e204c75387178d68fca113f", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-1.0.0-py3-none-any.whl", hash = "sha256:932c364daf4c721d3f592d8371b4d6408cf919c3560f114cba60a82fc566b397" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:a4c65510001153cab97a29ff219ad86e0d4330653ca89d9d4c84187ccf14c621", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-2.0.0.tar.gz", hash = "sha256:2b1c59091de521af15778ccb7aa6a3718dd89730b215adb2a9094652e7f34133" } + sdist = { url = "http://[LOCALHOST]/files/b-2.0.0.tar.gz", hash = "sha256:18fb09ba28eba255186405065e027093a6e952fa71eb565b4c46d619fdb60809", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-2.0.0-py3-none-any.whl", hash = "sha256:1d22dd5dc6026d5794bff9e6236968c8ffdfc7a6173926abdfd43b0591d947eb" }, + { url = "http://[LOCALHOST]/files/b-2.0.0-py3-none-any.whl", hash = "sha256:04cc57f7563029528b6d23283933b244b6f52ba1543fad54687c586d6e639fc4", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2759,16 +2751,16 @@ fn fork_marker_selection() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, - { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a" }, + { name = "b", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "b", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a" }, - { name = "package-b", marker = "sys_platform == 'darwin'", specifier = "<2" }, - { name = "package-b", marker = "sys_platform == 'linux'", specifier = ">=2" }, + { name = "a" }, + { name = "b", marker = "sys_platform == 'darwin'", specifier = "<2" }, + { name = "b", marker = "sys_platform == 'linux'", specifier = ">=2" }, ] "# ); @@ -2780,7 +2772,7 @@ fn fork_marker_selection() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2799,23 +2791,23 @@ fn fork_marker_selection() -> Result<()> { /// │ │ ├── satisfied by a-2.0.0 /// │ │ ├── satisfied by a-3.1.0 /// │ │ └── satisfied by a-4.3.0 -/// │ ├── requires b>=2.8; sys_platform == "linux" +/// │ ├── requires b>=2.8 ; sys_platform == 'linux' /// │ │ └── satisfied by b-2.8 -/// │ └── requires b<2.8; sys_platform == "darwin" +/// │ └── requires b<2.8 ; sys_platform == 'darwin' /// │ └── satisfied by b-2.7 /// ├── a /// │ ├── a-1.3.1 -/// │ │ └── requires c; implementation_name == "iron" +/// │ │ └── requires c ; implementation_name == 'iron' /// │ │ └── satisfied by c-1.10 /// │ ├── a-2.0.0 /// │ │ ├── requires b>=2.8 /// │ │ │ └── satisfied by b-2.8 -/// │ │ └── requires c; implementation_name == "cpython" +/// │ │ └── requires c ; implementation_name == 'cpython' /// │ │ └── satisfied by c-1.10 /// │ ├── a-3.1.0 /// │ │ ├── requires b>=2.8 /// │ │ │ └── satisfied by b-2.8 -/// │ │ └── requires c; implementation_name == "pypy" +/// │ │ └── requires c ; implementation_name == 'pypy' /// │ │ └── satisfied by c-1.10 /// │ └── a-4.3.0 /// │ └── requires b>=2.8 @@ -2829,10 +2821,7 @@ fn fork_marker_selection() -> Result<()> { #[test] fn fork_marker_track() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-marker-track-", "package-")); + let server = PackseServer::new("fork/marker-track.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2841,17 +2830,19 @@ fn fork_marker_track() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-marker-track-a''', - '''fork-marker-track-b>=2.8; sys_platform == "linux"''', - '''fork-marker-track-b<2.8; sys_platform == "darwin"''', + '''a''', + '''b>=2.8 ; sys_platform == 'linux'''', + '''b<2.8 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -2878,48 +2869,48 @@ fn fork_marker_track() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.3.1" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "implementation_name == 'iron'" }, + { name = "c", marker = "implementation_name == 'iron'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_a-1.3.1.tar.gz", hash = "sha256:6386d3023910b48db03db9fb28d75fcb8fa5a72a2ddc39de3fb6e8f15e9e1f8b" } + sdist = { url = "http://[LOCALHOST]/files/a-1.3.1.tar.gz", hash = "sha256:21bb6af59c842bf5ffc008914ca6e817139e07965d4093ff76cff8956c6965ff", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_a-1.3.1-py3-none-any.whl", hash = "sha256:369b83e46c9331551548ed3e3a8643aa2b821f72478a4a30c88e88937d5c5e47" }, + { url = "http://[LOCALHOST]/files/a-1.3.1-py3-none-any.whl", hash = "sha256:7720c67a8765ed540f4bdac4f8653210787d1ae518eb20ac541344ea8002f81c", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.7" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'darwin'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.7.tar.gz", hash = "sha256:8bd81fc189a302ec091eb05dac4f70510daf13fd82e10d8e0443c2d1d232748b" } + sdist = { url = "http://[LOCALHOST]/files/b-2.7.tar.gz", hash = "sha256:30fe41d5a9282b73cd50d58eceb33cec85d57c78af4a91fe3e202335f949949f", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.7-py3-none-any.whl", hash = "sha256:dd05dff9c80b7a425882b0809238784d44f021fd8b43f23c5d8ee24e1232532f" }, + { url = "http://[LOCALHOST]/files/b-2.7-py3-none-any.whl", hash = "sha256:973b02bdc3039f4fa7347e091da60de892a0a18b26effd20a1e17fdcf24a6dcd", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.8" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.8.tar.gz", hash = "sha256:4908ff48ea9ff17deaa3e56fb78938fa14c2fef9cd7f20ece4e8424d6b0785b3" } + sdist = { url = "http://[LOCALHOST]/files/b-2.8.tar.gz", hash = "sha256:673cbbd654751f7880842420431a400e62458486cb428bc7e508cfea4b9c8cd0", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.8-py3-none-any.whl", hash = "sha256:c9e3f25dd64091d3169a2ac13a22b2687eaebc482381a0d05dfbab74464ad395" }, + { url = "http://[LOCALHOST]/files/b-2.8-py3-none-any.whl", hash = "sha256:ce8467e32ba82112ebbfbdb23ffd9b537ce7502f09ab70cf65716a1a89eb1683", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "1.10" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_c-1.10.tar.gz", hash = "sha256:24ac6f9d025c0e4750c7a215150b51397df744f674fb372a23d8ffe6b243a040" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.10.tar.gz", hash = "sha256:4824f76781aef886ff01094b7c50133c4a0218f46e3c19ff0bd069f2661eee80", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_c-1.10-py3-none-any.whl", hash = "sha256:4e7104c302c1b05a2b1eae4abf4e27ba81866ec5af416e54562985e9495a22ad" }, + { url = "http://[LOCALHOST]/files/c-1.10-py3-none-any.whl", hash = "sha256:c830c4360164be9ea0027abb265a7f4f7894d8ac454942d98c09464d21a99b96", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -2927,16 +2918,16 @@ fn fork_marker_track() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, - { name = "package-b", version = "2.7", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'darwin'" }, - { name = "package-b", version = "2.8", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, + { name = "a" }, + { name = "b", version = "2.7", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'darwin'" }, + { name = "b", version = "2.8", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, ] [package.metadata] requires-dist = [ - { name = "package-a" }, - { name = "package-b", marker = "sys_platform == 'darwin'", specifier = "<2.8" }, - { name = "package-b", marker = "sys_platform == 'linux'", specifier = ">=2.8" }, + { name = "a" }, + { name = "b", marker = "sys_platform == 'darwin'", specifier = "<2.8" }, + { name = "b", marker = "sys_platform == 'linux'", specifier = ">=2.8" }, ] "# ); @@ -2948,7 +2939,7 @@ fn fork_marker_track() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -2959,6 +2950,7 @@ fn fork_marker_track() -> Result<()> { /// dependency specifications on `c` use the same constraints and thus depend on /// the same version of `c`. In this case, there is no conflict. /// +/// /// ```text /// fork-non-fork-marker-transitive /// ├── environment @@ -2970,11 +2962,11 @@ fn fork_marker_track() -> Result<()> { /// │ └── satisfied by b-1.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ └── requires c>=2.0.0; sys_platform == "linux" +/// │ └── requires c>=2.0.0 ; sys_platform == 'linux' /// │ └── satisfied by c-2.0.0 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c>=2.0.0; sys_platform == "darwin" +/// │ └── requires c>=2.0.0 ; sys_platform == 'darwin' /// │ └── satisfied by c-2.0.0 /// └── c /// ├── c-1.0.0 @@ -2983,10 +2975,7 @@ fn fork_marker_track() -> Result<()> { #[test] fn fork_non_fork_marker_transitive() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-non-fork-marker-transitive-", "package-")); + let server = PackseServer::new("fork/non-fork-marker-transitive.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -2995,16 +2984,18 @@ fn fork_non_fork_marker_transitive() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-non-fork-marker-transitive-a==1.0.0''', - '''fork-non-fork-marker-transitive-b==1.0.0''', + '''a==1.0.0''', + '''b==1.0.0''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -3026,36 +3017,36 @@ fn fork_non_fork_marker_transitive() -> Result<()> { requires-python = ">=3.12" [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "sys_platform == 'linux'" }, + { name = "c", marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_non_fork_marker_transitive_a-1.0.0.tar.gz", hash = "sha256:013eeda4d82bc86fad1a9f0b12e7dca4dda21f1588fdba1ac3345b2fa139c8c2" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:c2e92ae787edb18204782312a98f7dd9d0116ad5d0e61aa046956bdb2ef2e2e3", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_non_fork_marker_transitive_a-1.0.0-py3-none-any.whl", hash = "sha256:6150d8b54b7119a143b976472eac7c380cb130dd384ac02cffd7e6ce0cf82942" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:6de66136e60c4e5c1832fdd217af472ad50d9ebd177f4014b9b3f50904f80f5e", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", marker = "sys_platform == 'darwin'" }, + { name = "c", marker = "sys_platform == 'darwin'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_non_fork_marker_transitive_b-1.0.0.tar.gz", hash = "sha256:8257a5deb16f69d60e917f8817093640675bee4ab354a088b81b3e5969832e0c" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:8080895028b838440d4e08b9e6b1cc9c727c625e702c9c208de07fc7e06edfa0", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_non_fork_marker_transitive_b-1.0.0-py3-none-any.whl", hash = "sha256:e38a84fa43b292c50cf762656d4b2f4e57dea7fddf7be073146fb0870e7c0190" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:8cb0c9eaa95e2cf767bf5e6caf4fecfa1b2b9fc09be53c5768372704574ac244", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_non_fork_marker_transitive_c-2.0.0.tar.gz", hash = "sha256:44f2bc2e48b3b88bc4c4b64b7ef7df191c55a6fe0ea402d02dfc8132cec37c1f" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-2.0.0.tar.gz", hash = "sha256:72db9a21521acaa8ff10d0ce3bb4b68bc6b275bcb77bdb3debd95388f5120021", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_non_fork_marker_transitive_c-2.0.0-py3-none-any.whl", hash = "sha256:9afd9106b0434c27efd84358f5a5a125950bac3a808535b4b2fa1b78ecbb766f" }, + { url = "http://[LOCALHOST]/files/c-2.0.0-py3-none-any.whl", hash = "sha256:4a585f74490e3c09faafdb7df1ebb51d5e41c67b82ef08b5b5fd2f4c251b4b23", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -3063,14 +3054,14 @@ fn fork_non_fork_marker_transitive() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, - { name = "package-b" }, + { name = "a" }, + { name = "b" }, ] [package.metadata] requires-dist = [ - { name = "package-a", specifier = "==1.0.0" }, - { name = "package-b", specifier = "==1.0.0" }, + { name = "a", specifier = "==1.0.0" }, + { name = "b", specifier = "==1.0.0" }, ] "# ); @@ -3082,7 +3073,7 @@ fn fork_non_fork_marker_transitive() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -3094,14 +3085,15 @@ fn fork_non_fork_marker_transitive() -> Result<()> { /// `c` is indirect, and thus, there's no fork detected by the universal resolver. /// This in turn results in an unresolvable conflict on `c`. /// +/// /// ```text /// fork-non-local-fork-marker-direct /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a==1.0.0; sys_platform == "linux" +/// │ ├── requires a==1.0.0 ; sys_platform == 'linux' /// │ │ └── satisfied by a-1.0.0 -/// │ └── requires b==1.0.0; sys_platform == "darwin" +/// │ └── requires b==1.0.0 ; sys_platform == 'darwin' /// │ └── satisfied by b-1.0.0 /// ├── a /// │ └── a-1.0.0 @@ -3118,10 +3110,7 @@ fn fork_non_fork_marker_transitive() -> Result<()> { #[test] fn fork_non_local_fork_marker_direct() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-non-local-fork-marker-direct-", "package-")); + let server = PackseServer::new("fork/non-local-fork-marker-direct.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3130,16 +3119,18 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-non-local-fork-marker-direct-a==1.0.0; sys_platform == "linux"''', - '''fork-non-local-fork-marker-direct-b==1.0.0; sys_platform == "darwin"''', + '''a==1.0.0 ; sys_platform == 'linux'''', + '''b==1.0.0 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: false exit_code: 1 @@ -3147,8 +3138,8 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-c<2.0.0 and package-b==1.0.0 depends on package-c>=2.0.0, we can conclude that package-b==1.0.0 and package-a{sys_platform == 'linux'}==1.0.0 are incompatible. - And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable. + ╰─▶ Because a==1.0.0 depends on c<2.0.0 and b==1.0.0 depends on c>=2.0.0, we can conclude that b==1.0.0 and a{sys_platform == 'linux'}==1.0.0 are incompatible. + And because your project depends on a{sys_platform == 'linux'}==1.0.0 and b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable. " ); @@ -3166,6 +3157,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { /// specifications) in the dependency graph, the forking resolver does not "detect" /// it, and thus never forks and thus this results in "no resolution." /// +/// /// ```text /// fork-non-local-fork-marker-transitive /// ├── environment @@ -3177,11 +3169,11 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { /// │ └── satisfied by b-1.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ └── requires c<2.0.0; sys_platform == "linux" +/// │ └── requires c<2.0.0 ; sys_platform == 'linux' /// │ └── satisfied by c-1.0.0 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c>=2.0.0; sys_platform == "darwin" +/// │ └── requires c>=2.0.0 ; sys_platform == 'darwin' /// │ └── satisfied by c-2.0.0 /// └── c /// ├── c-1.0.0 @@ -3190,10 +3182,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> { #[test] fn fork_non_local_fork_marker_transitive() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-non-local-fork-marker-transitive-", "package-")); + let server = PackseServer::new("fork/non-local-fork-marker-transitive.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3202,16 +3191,18 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-non-local-fork-marker-transitive-a==1.0.0''', - '''fork-non-local-fork-marker-transitive-b==1.0.0''', + '''a==1.0.0''', + '''b==1.0.0''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: false exit_code: 1 @@ -3219,8 +3210,8 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible. - And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable. + ╰─▶ Because a==1.0.0 depends on c{sys_platform == 'linux'}<2.0.0 and b==1.0.0 depends on c{sys_platform == 'darwin'}>=2.0.0, we can conclude that a==1.0.0 and b==1.0.0 are incompatible. + And because your project depends on a==1.0.0 and b==1.0.0, we can conclude that your project's requirements are unsatisfiable. " ); @@ -3256,19 +3247,20 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { /// `1.2.0` unconditionally. (The marker expressions get normalized out /// entirely.) /// +/// /// ```text /// fork-overlapping-markers-basic /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=1.0.0; python_version < "3.13" +/// │ ├── requires a>=1.0.0 ; python_full_version < '3.13' /// │ │ ├── satisfied by a-1.0.0 /// │ │ ├── satisfied by a-1.1.0 /// │ │ └── satisfied by a-1.2.0 -/// │ ├── requires a>=1.1.0; python_version >= "3.13" +/// │ ├── requires a>=1.1.0 ; python_full_version >= '3.13' /// │ │ ├── satisfied by a-1.1.0 /// │ │ └── satisfied by a-1.2.0 -/// │ └── requires a>=1.2.0; python_version >= "3.14" +/// │ └── requires a>=1.2.0 ; python_full_version >= '3.14' /// │ └── satisfied by a-1.2.0 /// └── a /// ├── a-1.0.0 @@ -3278,10 +3270,7 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { #[test] fn fork_overlapping_markers_basic() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-overlapping-markers-basic-", "package-")); + let server = PackseServer::new("fork/overlapping-markers-basic.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3290,17 +3279,19 @@ fn fork_overlapping_markers_basic() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-overlapping-markers-basic-a>=1.0.0; python_version < "3.13"''', - '''fork-overlapping-markers-basic-a>=1.1.0; python_version >= "3.13"''', - '''fork-overlapping-markers-basic-a>=1.2.0; python_version >= "3.14"''', + '''a>=1.0.0 ; python_full_version < '3.13'''', + '''a>=1.1.0 ; python_full_version >= '3.13'''', + '''a>=1.2.0 ; python_full_version >= '3.14'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -3327,12 +3318,12 @@ fn fork_overlapping_markers_basic() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.2.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_overlapping_markers_basic_a-1.2.0.tar.gz", hash = "sha256:bc36ec5060fa24617aa2b40a77b62e9a80737aeec255fa3e005933cdfd81ad98" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.2.0.tar.gz", hash = "sha256:ea1f0436d9d88c51f66e4154f17f7c6778d0dc7674f75cda35dc4b668fb287a7", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_overlapping_markers_basic_a-1.2.0-py3-none-any.whl", hash = "sha256:3afae4a2c2a114de372c165e5b874dbb52bc41aa7ebee2b519820e89c8ccbeec" }, + { url = "http://[LOCALHOST]/files/a-1.2.0-py3-none-any.whl", hash = "sha256:ac8736ef11e0594522369998752b2780be46e71e034c07d700c7bd0f2cea9863", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -3340,14 +3331,14 @@ fn fork_overlapping_markers_basic() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, + { name = "a" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "python_full_version < '3.13'", specifier = ">=1.0.0" }, - { name = "package-a", marker = "python_full_version >= '3.13'", specifier = ">=1.1.0" }, - { name = "package-a", marker = "python_full_version >= '3.14'", specifier = ">=1.2.0" }, + { name = "a", marker = "python_full_version < '3.13'", specifier = ">=1.0.0" }, + { name = "a", marker = "python_full_version >= '3.13'", specifier = ">=1.1.0" }, + { name = "a", marker = "python_full_version >= '3.14'", specifier = ">=1.2.0" }, ] "# ); @@ -3359,7 +3350,7 @@ fn fork_overlapping_markers_basic() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -3381,39 +3372,40 @@ fn fork_overlapping_markers_basic() -> Result<()> { /// `fork-if-not-forked`, we reject cleaver 1, we select cleaver 2, we fork on /// `sys_platform`, we accept cleaver 2 since we forked on `os_name`, done. /// +/// /// ```text /// preferences-dependent-forking-bistable /// ├── environment /// │ └── python3.12 /// ├── root /// │ └── requires cleaver -/// │ ├── satisfied by cleaver-2.0.0 -/// │ └── satisfied by cleaver-1.0.0 +/// │ ├── satisfied by cleaver-1.0.0 +/// │ └── satisfied by cleaver-2.0.0 /// ├── cleaver -/// │ ├── cleaver-2.0.0 -/// │ │ ├── requires fork-sys-platform==1; sys_platform == "linux" -/// │ │ │ └── satisfied by fork-sys-platform-1.0.0 -/// │ │ ├── requires fork-sys-platform==2; sys_platform != "linux" -/// │ │ │ └── satisfied by fork-sys-platform-2.0.0 -/// │ │ ├── requires reject-cleaver2==1; os_name == "posix" -/// │ │ │ └── satisfied by reject-cleaver2-1.0.0 -/// │ │ └── requires reject-cleaver2-proxy -/// │ │ └── satisfied by reject-cleaver2-proxy-1.0.0 -/// │ └── cleaver-1.0.0 -/// │ ├── requires fork-if-not-forked!=2; sys_platform == "linux" -/// │ │ ├── satisfied by fork-if-not-forked-1.0.0 -/// │ │ └── satisfied by fork-if-not-forked-3.0.0 -/// │ ├── requires fork-if-not-forked-proxy; sys_platform != "linux" -/// │ │ └── satisfied by fork-if-not-forked-proxy-1.0.0 -/// │ ├── requires reject-cleaver1==1; sys_platform == "linux" -/// │ │ └── satisfied by reject-cleaver1-1.0.0 -/// │ └── requires reject-cleaver1-proxy -/// │ └── satisfied by reject-cleaver1-proxy-1.0.0 +/// │ ├── cleaver-1.0.0 +/// │ │ ├── requires fork-if-not-forked!=2 ; sys_platform == 'linux' +/// │ │ │ ├── satisfied by fork-if-not-forked-1.0.0 +/// │ │ │ └── satisfied by fork-if-not-forked-3.0.0 +/// │ │ ├── requires fork-if-not-forked-proxy ; sys_platform != 'linux' +/// │ │ │ └── satisfied by fork-if-not-forked-proxy-1.0.0 +/// │ │ ├── requires reject-cleaver1==1 ; sys_platform == 'linux' +/// │ │ │ └── satisfied by reject-cleaver1-1.0.0 +/// │ │ └── requires reject-cleaver1-proxy +/// │ │ └── satisfied by reject-cleaver1-proxy-1.0.0 +/// │ └── cleaver-2.0.0 +/// │ ├── requires fork-sys-platform==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by fork-sys-platform-1.0.0 +/// │ ├── requires fork-sys-platform==2 ; sys_platform != 'linux' +/// │ │ └── satisfied by fork-sys-platform-2.0.0 +/// │ ├── requires reject-cleaver2==1 ; os_name == 'posix' +/// │ │ └── satisfied by reject-cleaver2-1.0.0 +/// │ └── requires reject-cleaver2-proxy +/// │ └── satisfied by reject-cleaver2-proxy-1.0.0 /// ├── fork-if-not-forked /// │ ├── fork-if-not-forked-1.0.0 -/// │ │ ├── requires fork-os-name==1; os_name == "posix" +/// │ │ ├── requires fork-os-name==1 ; os_name == 'posix' /// │ │ │ └── satisfied by fork-os-name-1.0.0 -/// │ │ ├── requires fork-os-name==2; os_name != "posix" +/// │ │ ├── requires fork-os-name==2 ; os_name != 'posix' /// │ │ │ └── satisfied by fork-os-name-2.0.0 /// │ │ └── requires reject-cleaver1-proxy /// │ │ └── satisfied by reject-cleaver1-proxy-1.0.0 @@ -3435,23 +3427,20 @@ fn fork_overlapping_markers_basic() -> Result<()> { /// │ └── reject-cleaver1-2.0.0 /// ├── reject-cleaver1-proxy /// │ └── reject-cleaver1-proxy-1.0.0 -/// │ └── requires reject-cleaver1==2; sys_platform != "linux" +/// │ └── requires reject-cleaver1==2 ; sys_platform != 'linux' /// │ └── satisfied by reject-cleaver1-2.0.0 /// ├── reject-cleaver2 /// │ ├── reject-cleaver2-1.0.0 /// │ └── reject-cleaver2-2.0.0 /// └── reject-cleaver2-proxy /// └── reject-cleaver2-proxy-1.0.0 -/// └── requires reject-cleaver2==2; os_name != "posix" +/// └── requires reject-cleaver2==2 ; os_name != 'posix' /// └── satisfied by reject-cleaver2-2.0.0 /// ``` #[test] fn preferences_dependent_forking_bistable() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"preferences-dependent-forking-bistable-", "package-")); + let server = PackseServer::new("fork/preferences-dependent-forking-bistable.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3460,15 +3449,17 @@ fn preferences_dependent_forking_bistable() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''preferences-dependent-forking-bistable-cleaver''', + '''cleaver''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -3494,90 +3485,54 @@ fn preferences_dependent_forking_bistable() -> Result<()> { ] [[package]] - name = "package-cleaver" + name = "cleaver" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-fork-if-not-forked", version = "3.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-fork-if-not-forked-proxy", marker = "sys_platform != 'linux'" }, - { name = "package-reject-cleaver1", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-reject-cleaver1-proxy" }, + { name = "fork-if-not-forked", version = "3.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "fork-if-not-forked-proxy", marker = "sys_platform != 'linux'" }, + { name = "reject-cleaver1", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "reject-cleaver1-proxy" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_cleaver-1.0.0.tar.gz", hash = "sha256:d2279d73d7ffff655e269c3bcea9674895dd55631d542ed12fab64a4bbd9fc29" } + sdist = { url = "http://[LOCALHOST]/files/cleaver-1.0.0.tar.gz", hash = "sha256:049d1cd0cb93f315070151f69682f97eec46ff0a3da64d87d387cc11830ba541", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_cleaver-1.0.0-py3-none-any.whl", hash = "sha256:6ab14dc6b3a9658f2a222b752773beaf43ac5379fba0a88bb3012282936328b0" }, + { url = "http://[LOCALHOST]/files/cleaver-1.0.0-py3-none-any.whl", hash = "sha256:c9c97d652936b7293b36e54194d448350852ffa6fbfad051ba0671db46803d7b", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-fork-if-not-forked" + name = "fork-if-not-forked" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform != 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_fork_if_not_forked-2.0.0.tar.gz", hash = "sha256:a79abb4beae739e1413afe652bcda59e3807253e46907729dab211954be06a2c" } + sdist = { url = "http://[LOCALHOST]/files/fork_if_not_forked-2.0.0.tar.gz", hash = "sha256:0e93f72e7bcbdc71a1a3573b1f79a747e82c9c238505a847e74d314143eedc18", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_fork_if_not_forked-2.0.0-py3-none-any.whl", hash = "sha256:1c580daeffeba5529c44ae1afb90b858ea7c696157c1c72ba59de0f32f61dd26" }, + { url = "http://[LOCALHOST]/files/fork_if_not_forked-2.0.0-py3-none-any.whl", hash = "sha256:0604f383a6d7cf8fd69c252c85bdb34967743de531b2b0f4084378f00f5b4ccf", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-fork-if-not-forked" + name = "fork-if-not-forked" version = "3.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_fork_if_not_forked-3.0.0.tar.gz", hash = "sha256:4d45258ebd32cd819906e69f2e2b890044d6e99b24b8e82ebc88aba6fe607b1a" } + sdist = { url = "http://[LOCALHOST]/files/fork_if_not_forked-3.0.0.tar.gz", hash = "sha256:cdbe6609a59da78b2ea6ddb8703131385dc71fd9d13bca82e35ed5d541a7425d", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_fork_if_not_forked-3.0.0-py3-none-any.whl", hash = "sha256:b246c161c65bcf1387dbbf5458bb4841076818c2838babd1d5d274b436eda26e" }, + { url = "http://[LOCALHOST]/files/fork_if_not_forked-3.0.0-py3-none-any.whl", hash = "sha256:dcfb9267c7ae0868f4f40e29574665b22e336e666c89a542a074355f7bac643e", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-fork-if-not-forked-proxy" + name = "fork-if-not-forked-proxy" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-fork-if-not-forked", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, + { name = "fork-if-not-forked", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_fork_if_not_forked_proxy-1.0.0.tar.gz", hash = "sha256:80e40c848eed5472237156444ccaf72ddb0176cd095f2b69469fce203981a14e" } + sdist = { url = "http://[LOCALHOST]/files/fork_if_not_forked_proxy-1.0.0.tar.gz", hash = "sha256:1da9ee55735388976535cfd2ae143c8b9e21b9dd5f01ce9521a4a095a5e9e258", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_fork_if_not_forked_proxy-1.0.0-py3-none-any.whl", hash = "sha256:38cf97a4a1a1bd748744ec3859023c9e1173171525cdec8f8a48e8e5adf4422c" }, - ] - - [[package]] - name = "package-reject-cleaver1" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - resolution-markers = [ - "sys_platform == 'linux'", - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_reject_cleaver1-1.0.0.tar.gz", hash = "sha256:42426a00cb3e8c7994faf906ebcf102e0cbf7e68eabd8ada77562489ae185a3d" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_reject_cleaver1-1.0.0-py3-none-any.whl", hash = "sha256:7468472d90e0004b177841ffbc5997fca3fa3f593607a8c90ffb013aa7681768" }, - ] - - [[package]] - name = "package-reject-cleaver1" - version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - resolution-markers = [ - "sys_platform != 'linux'", - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_reject_cleaver1-2.0.0.tar.gz", hash = "sha256:1f7c625af40a3dcd86ca2765f496a3423cd0ee5162bf136bf7b73d956ae6715f" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_reject_cleaver1-2.0.0-py3-none-any.whl", hash = "sha256:695334dd35d3cb9045e111a4fda566d982558fef7a74026138941d869b97c014" }, - ] - - [[package]] - name = "package-reject-cleaver1-proxy" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-reject-cleaver1", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_reject_cleaver1_proxy-1.0.0.tar.gz", hash = "sha256:cbdc4afe283a0b8c556015b20fa5818c9be23f769cfea0b1f3c8ce138af064f9" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bistable_reject_cleaver1_proxy-1.0.0-py3-none-any.whl", hash = "sha256:74b2c5db1d5841444d89d8238b7a89b082327cf7d51fa674349544ad0f67073b" }, + { url = "http://[LOCALHOST]/files/fork_if_not_forked_proxy-1.0.0-py3-none-any.whl", hash = "sha256:704030c720c21eee6ad9453ccd4d44d983182a73c057ed89325201c5dcead7fe", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -3585,11 +3540,47 @@ fn preferences_dependent_forking_bistable() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-cleaver" }, + { name = "cleaver" }, ] [package.metadata] - requires-dist = [{ name = "package-cleaver" }] + requires-dist = [{ name = "cleaver" }] + + [[package]] + name = "reject-cleaver1" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + resolution-markers = [ + "sys_platform == 'linux'", + ] + sdist = { url = "http://[LOCALHOST]/files/reject_cleaver1-1.0.0.tar.gz", hash = "sha256:6da0074d5a0a178cb730df50264fe123ac776ebb72b8624a2e04eefe2c694246", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/reject_cleaver1-1.0.0-py3-none-any.whl", hash = "sha256:b3f3e0d98b07e6ec99719106244c07fa85658f0be9aa20f28c957aa8efcbeef8", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "reject-cleaver1" + version = "2.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + resolution-markers = [ + "sys_platform != 'linux'", + ] + sdist = { url = "http://[LOCALHOST]/files/reject_cleaver1-2.0.0.tar.gz", hash = "sha256:3b6adb7793ff0c4bf6e14d10e16156bc184046e1e5b738d921974ef6b9f9b58a", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/reject_cleaver1-2.0.0-py3-none-any.whl", hash = "sha256:db12d456e29ce239bbfed102b8b613a088adde579403d11ae91b15a08011efd2", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "reject-cleaver1-proxy" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "reject-cleaver1", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + ] + sdist = { url = "http://[LOCALHOST]/files/reject_cleaver1_proxy-1.0.0.tar.gz", hash = "sha256:43034027a9360a2497ff2558c3efc0652ec5a18b30872cf88ab87bd4a3675799", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/reject_cleaver1_proxy-1.0.0-py3-none-any.whl", hash = "sha256:8032f56f5eaddb524e079a75ab95b25220f916ac692c377a6977b58e61a3508d", upload-time = "2024-03-24T00:00:00Z" }, + ] "# ); }); @@ -3600,7 +3591,7 @@ fn preferences_dependent_forking_bistable() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -3636,6 +3627,7 @@ fn preferences_dependent_forking_bistable() -> Result<()> { /// foo 1 -> bar==2 /// ``` /// +/// /// ```text /// preferences-dependent-forking-conflicting /// ├── environment @@ -3645,8 +3637,8 @@ fn preferences_dependent_forking_bistable() -> Result<()> { /// │ │ ├── satisfied by bar-1.0.0 /// │ │ └── satisfied by bar-2.0.0 /// │ ├── requires cleaver -/// │ │ ├── satisfied by cleaver-2.0.0 -/// │ │ └── satisfied by cleaver-1.0.0 +/// │ │ ├── satisfied by cleaver-1.0.0 +/// │ │ └── satisfied by cleaver-2.0.0 /// │ └── requires foo /// │ ├── satisfied by foo-1.0.0 /// │ └── satisfied by foo-2.0.0 @@ -3654,18 +3646,18 @@ fn preferences_dependent_forking_bistable() -> Result<()> { /// │ ├── bar-1.0.0 /// │ └── bar-2.0.0 /// ├── cleaver -/// │ ├── cleaver-2.0.0 -/// │ │ ├── requires reject-cleaver-2 -/// │ │ │ └── satisfied by reject-cleaver-2-1.0.0 -/// │ │ ├── requires unrelated-dep==1; sys_platform == "linux" -/// │ │ │ └── satisfied by unrelated-dep-1.0.0 -/// │ │ └── requires unrelated-dep==2; sys_platform != "linux" -/// │ │ └── satisfied by unrelated-dep-2.0.0 -/// │ └── cleaver-1.0.0 -/// │ ├── requires bar==1; sys_platform != "linux" -/// │ │ └── satisfied by bar-1.0.0 -/// │ └── requires foo==1; sys_platform == "linux" -/// │ └── satisfied by foo-1.0.0 +/// │ ├── cleaver-1.0.0 +/// │ │ ├── requires bar==1 ; sys_platform != 'linux' +/// │ │ │ └── satisfied by bar-1.0.0 +/// │ │ └── requires foo==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by foo-1.0.0 +/// │ └── cleaver-2.0.0 +/// │ ├── requires reject-cleaver-2 +/// │ │ └── satisfied by reject-cleaver-2-1.0.0 +/// │ ├── requires unrelated-dep==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by unrelated-dep-1.0.0 +/// │ └── requires unrelated-dep==2 ; sys_platform != 'linux' +/// │ └── satisfied by unrelated-dep-2.0.0 /// ├── foo /// │ ├── foo-1.0.0 /// │ │ └── requires bar==2 @@ -3683,10 +3675,7 @@ fn preferences_dependent_forking_bistable() -> Result<()> { #[test] fn preferences_dependent_forking_conflicting() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"preferences-dependent-forking-conflicting-", "package-")); + let server = PackseServer::new("fork/preferences-dependent-forking-conflicting.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3695,17 +3684,19 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''preferences-dependent-forking-conflicting-cleaver''', - '''preferences-dependent-forking-conflicting-foo''', - '''preferences-dependent-forking-conflicting-bar''', + '''cleaver''', + '''foo''', + '''bar''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -3726,6 +3717,7 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { /// It's not polished, but it's useful to have something with a higher period /// than 2 in our test suite. /// +/// /// ```text /// preferences-dependent-forking-tristable /// ├── environment @@ -3735,25 +3727,25 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { /// │ │ ├── satisfied by bar-1.0.0 /// │ │ └── satisfied by bar-2.0.0 /// │ ├── requires cleaver -/// │ │ ├── satisfied by cleaver-2.0.0 -/// │ │ └── satisfied by cleaver-1.0.0 +/// │ │ ├── satisfied by cleaver-1.0.0 +/// │ │ └── satisfied by cleaver-2.0.0 /// │ └── requires foo /// │ ├── satisfied by foo-1.0.0 /// │ └── satisfied by foo-2.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ └── requires unrelated-dep3==1; os_name == "posix" +/// │ └── requires unrelated-dep3==1 ; os_name == 'posix' /// │ └── satisfied by unrelated-dep3-1.0.0 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires unrelated-dep3==2; os_name != "posix" +/// │ └── requires unrelated-dep3==2 ; os_name != 'posix' /// │ └── satisfied by unrelated-dep3-2.0.0 /// ├── bar /// │ ├── bar-1.0.0 -/// │ │ ├── requires c!=3; sys_platform == "linux" +/// │ │ ├── requires c!=3 ; sys_platform == 'linux' /// │ │ │ ├── satisfied by c-1.0.0 /// │ │ │ └── satisfied by c-2.0.0 -/// │ │ ├── requires d; sys_platform != "linux" +/// │ │ ├── requires d ; sys_platform != 'linux' /// │ │ │ └── satisfied by d-1.0.0 /// │ │ └── requires reject-cleaver-1 /// │ │ └── satisfied by reject-cleaver-1-1.0.0 @@ -3762,27 +3754,27 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { /// │ ├── c-1.0.0 /// │ │ ├── requires reject-cleaver-1 /// │ │ │ └── satisfied by reject-cleaver-1-1.0.0 -/// │ │ ├── requires unrelated-dep2==1; os_name == "posix" +/// │ │ ├── requires unrelated-dep2==1 ; os_name == 'posix' /// │ │ │ └── satisfied by unrelated-dep2-1.0.0 -/// │ │ └── requires unrelated-dep2==2; os_name != "posix" +/// │ │ └── requires unrelated-dep2==2 ; os_name != 'posix' /// │ │ └── satisfied by unrelated-dep2-2.0.0 /// │ ├── c-2.0.0 /// │ └── c-3.0.0 /// ├── cleaver -/// │ ├── cleaver-2.0.0 -/// │ │ ├── requires a -/// │ │ │ └── satisfied by a-1.0.0 -/// │ │ ├── requires b -/// │ │ │ └── satisfied by b-1.0.0 -/// │ │ ├── requires unrelated-dep==1; sys_platform == "linux" -/// │ │ │ └── satisfied by unrelated-dep-1.0.0 -/// │ │ └── requires unrelated-dep==2; sys_platform != "linux" -/// │ │ └── satisfied by unrelated-dep-2.0.0 -/// │ └── cleaver-1.0.0 -/// │ ├── requires bar==1; sys_platform != "linux" -/// │ │ └── satisfied by bar-1.0.0 -/// │ └── requires foo==1; sys_platform == "linux" -/// │ └── satisfied by foo-1.0.0 +/// │ ├── cleaver-1.0.0 +/// │ │ ├── requires bar==1 ; sys_platform != 'linux' +/// │ │ │ └── satisfied by bar-1.0.0 +/// │ │ └── requires foo==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by foo-1.0.0 +/// │ └── cleaver-2.0.0 +/// │ ├── requires a +/// │ │ └── satisfied by a-1.0.0 +/// │ ├── requires b +/// │ │ └── satisfied by b-1.0.0 +/// │ ├── requires unrelated-dep==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by unrelated-dep-1.0.0 +/// │ └── requires unrelated-dep==2 ; sys_platform != 'linux' +/// │ └── satisfied by unrelated-dep-2.0.0 /// ├── d /// │ └── d-1.0.0 /// │ └── requires c!=2 @@ -3790,10 +3782,10 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { /// │ └── satisfied by c-3.0.0 /// ├── foo /// │ ├── foo-1.0.0 -/// │ │ ├── requires c!=3; sys_platform == "linux" +/// │ │ ├── requires c!=3 ; sys_platform == 'linux' /// │ │ │ ├── satisfied by c-1.0.0 /// │ │ │ └── satisfied by c-2.0.0 -/// │ │ ├── requires c!=2; sys_platform != "linux" +/// │ │ ├── requires c!=2 ; sys_platform != 'linux' /// │ │ │ ├── satisfied by c-1.0.0 /// │ │ │ └── satisfied by c-3.0.0 /// │ │ └── requires reject-cleaver-1 @@ -3801,9 +3793,9 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { /// │ └── foo-2.0.0 /// ├── reject-cleaver-1 /// │ └── reject-cleaver-1-1.0.0 -/// │ ├── requires unrelated-dep2==1; sys_platform == "linux" +/// │ ├── requires unrelated-dep2==1 ; sys_platform == 'linux' /// │ │ └── satisfied by unrelated-dep2-1.0.0 -/// │ └── requires unrelated-dep2==2; sys_platform != "linux" +/// │ └── requires unrelated-dep2==2 ; sys_platform != 'linux' /// │ └── satisfied by unrelated-dep2-2.0.0 /// ├── reject-cleaver-2 /// │ └── reject-cleaver-2-1.0.0 @@ -3825,10 +3817,7 @@ fn preferences_dependent_forking_conflicting() -> Result<()> { #[test] fn preferences_dependent_forking_tristable() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"preferences-dependent-forking-tristable-", "package-")); + let server = PackseServer::new("fork/preferences-dependent-forking-tristable.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3837,17 +3826,19 @@ fn preferences_dependent_forking_tristable() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''preferences-dependent-forking-tristable-cleaver''', - '''preferences-dependent-forking-tristable-foo''', - '''preferences-dependent-forking-tristable-bar''', + '''cleaver''', + '''foo''', + '''bar''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -3873,131 +3864,94 @@ fn preferences_dependent_forking_tristable() -> Result<()> { ] [[package]] - name = "package-bar" + name = "bar" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform != 'linux'", ] dependencies = [ - { name = "package-d", marker = "sys_platform != 'linux'" }, - { name = "package-reject-cleaver-1", marker = "sys_platform != 'linux'" }, + { name = "d", marker = "sys_platform != 'linux'" }, + { name = "reject-cleaver-1", marker = "sys_platform != 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_bar-1.0.0.tar.gz", hash = "sha256:cab958c70ac0b91cf9db6d44846ee4aff97cdedaf1eaf96400657177eb8cf7d2" } + sdist = { url = "http://[LOCALHOST]/files/bar-1.0.0.tar.gz", hash = "sha256:7433981e897b2fdbefc4ccc713282a03663dbe9b7468658a702af1d01d241e27", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_bar-1.0.0-py3-none-any.whl", hash = "sha256:b81a56870b304c579d5ce5c1e4d0ae1ee60f109d50c739ead96ff4775ea1fb6b" }, + { url = "http://[LOCALHOST]/files/bar-1.0.0-py3-none-any.whl", hash = "sha256:3e2cba35e1d80892bb436521134f100c183b8f64677be0ec80ff6ee4c710aa43", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-bar" + name = "bar" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_bar-2.0.0.tar.gz", hash = "sha256:73f911717b729830afd19bdd8f8fee3562a8b03e8fc8f3a734becf9b15bcb9a4" } + sdist = { url = "http://[LOCALHOST]/files/bar-2.0.0.tar.gz", hash = "sha256:fa9a4faf506228722784ed740a362bccd96913f4f98a4e10d45ab79d8abb270a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_bar-2.0.0-py3-none-any.whl", hash = "sha256:25f44f6a64ef2a5b0e315a7ddf6470931c7c9c3acca67b477a6130abddea1523" }, + { url = "http://[LOCALHOST]/files/bar-2.0.0-py3-none-any.whl", hash = "sha256:563b1af3238a4ad819f2b95b74f940319a2ef30ed7991a2416fa98aa115da87d", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_c-2.0.0.tar.gz", hash = "sha256:ac8b3503e17933efe9be91ce969d2fd6f95d3175d5aa8f4033a1abd2baa5b4de" } + sdist = { url = "http://[LOCALHOST]/files/c-2.0.0.tar.gz", hash = "sha256:72db9a21521acaa8ff10d0ce3bb4b68bc6b275bcb77bdb3debd95388f5120021", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_c-2.0.0-py3-none-any.whl", hash = "sha256:d04193e40e9415b4239a8709518dc780dc6ed4475124c4a9f0c98574dcbf7569" }, + { url = "http://[LOCALHOST]/files/c-2.0.0-py3-none-any.whl", hash = "sha256:4a585f74490e3c09faafdb7df1ebb51d5e41c67b82ef08b5b5fd2f4c251b4b23", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-c" + name = "c" version = "3.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform != 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_c-3.0.0.tar.gz", hash = "sha256:5932c8648a79247c23756a841f5bc6ee94018cf5ec0228ae0049f9de6035262e" } + sdist = { url = "http://[LOCALHOST]/files/c-3.0.0.tar.gz", hash = "sha256:27d544487f21f6ece9d49d672fc8da1664c48a7ef864d8ff91b756183d77b831", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_c-3.0.0-py3-none-any.whl", hash = "sha256:4a0d4549221dc761782115280a678c5ffbf1f93b8c78a5a935a197298b6dc625" }, + { url = "http://[LOCALHOST]/files/c-3.0.0-py3-none-any.whl", hash = "sha256:1e031928d6a855d65842904337ad7d984de1a64473b1166d548afbfe5a397341", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-cleaver" + name = "cleaver" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-bar", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - { name = "package-foo", marker = "sys_platform == 'linux'" }, + { name = "bar", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + { name = "foo", marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_cleaver-1.0.0.tar.gz", hash = "sha256:6c1a467374165e60c80b639fc4ca4830dd66796fc1a363f78b2c5b0756838909" } + sdist = { url = "http://[LOCALHOST]/files/cleaver-1.0.0.tar.gz", hash = "sha256:9156f0d8df99763e90677ca1b4dab67bee5fa33f59d102626dd91505cf332d2a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_cleaver-1.0.0-py3-none-any.whl", hash = "sha256:1291f62c2630beb9e7a07461e7a79cf662934e4ca2fa80da0f6e7e02bb84a7d6" }, + { url = "http://[LOCALHOST]/files/cleaver-1.0.0-py3-none-any.whl", hash = "sha256:f49d93330cfe3f7096636c506aa522a497eae44d08b07f6276caf784ec87f65b", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-d" + name = "d" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", version = "3.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, + { name = "c", version = "3.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_d-1.0.0.tar.gz", hash = "sha256:0662b34dc84d1529fd1f75f69c0802a5ac768611a3a11ba331af81e0e67f96a2" } + sdist = { url = "http://[LOCALHOST]/files/d-1.0.0.tar.gz", hash = "sha256:ef58b0f5f8f9c4d7e7864a36f86ad96ca8cee734095b3e6512ae8c395380ee41", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_d-1.0.0-py3-none-any.whl", hash = "sha256:7917517d7d735efd5b2559223d95c5e776963b25c5cb174e2d64199898aa6adf" }, + { url = "http://[LOCALHOST]/files/d-1.0.0-py3-none-any.whl", hash = "sha256:890292cd440f4d868e46d5e883bd2eee4ced094d39e44a7cd88f7d9f5049f5ef", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-foo" + name = "foo" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-c", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-c", version = "3.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - { name = "package-reject-cleaver-1" }, + { name = "c", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "c", version = "3.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + { name = "reject-cleaver-1" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_foo-1.0.0.tar.gz", hash = "sha256:3ab8216595ed4d66e27f853cbbfc61142cc06c1a1205c9477581c6b1463a6245" } + sdist = { url = "http://[LOCALHOST]/files/foo-1.0.0.tar.gz", hash = "sha256:b9179a545511f4418bdde59a4907297accb585f0113176790170db5665ca3416", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_foo-1.0.0-py3-none-any.whl", hash = "sha256:3a42e5594ebefa939a4a366419cd335e9e52942798b6f5ab6e37b9b55e6474b8" }, - ] - - [[package]] - name = "package-reject-cleaver-1" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-unrelated-dep2", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-unrelated-dep2", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_reject_cleaver_1-1.0.0.tar.gz", hash = "sha256:5fc04a8d0cf1eaa98da779661bf9328b04ba771bbb9261e7784b025a40c8a133" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_reject_cleaver_1-1.0.0-py3-none-any.whl", hash = "sha256:979e44471666eba857933319c56c678ac393c72081b13f67f1f41b47f558ccf9" }, - ] - - [[package]] - name = "package-unrelated-dep2" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - resolution-markers = [ - "sys_platform == 'linux'", - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_unrelated_dep2-1.0.0.tar.gz", hash = "sha256:83fe5a7e81958bd422fb5cfde2c60537b97228762eada43a658e621e64a0780d" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_unrelated_dep2-1.0.0-py3-none-any.whl", hash = "sha256:461e8e88feafdaad02a4a79b9f1ad874333ee92131ca83030190c48b96dbcfc4" }, - ] - - [[package]] - name = "package-unrelated-dep2" - version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - resolution-markers = [ - "sys_platform != 'linux'", - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_unrelated_dep2-2.0.0.tar.gz", hash = "sha256:0b491eba7b182cb80955e8592d144f20b57bcfeaf7852c568a03fdd6b0dfd752" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_tristable_unrelated_dep2-2.0.0-py3-none-any.whl", hash = "sha256:c3aedfba6ce432d4d79bc2c304f8bc493a562e0fdc963eed9253f4eb8e8bdb8e" }, + { url = "http://[LOCALHOST]/files/foo-1.0.0-py3-none-any.whl", hash = "sha256:a6a8594d7d818843d31479b216b325e9140b6c5b180a55427b03fd85bbd261ad", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -4005,17 +3959,54 @@ fn preferences_dependent_forking_tristable() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-bar", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - { name = "package-bar", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-cleaver" }, - { name = "package-foo" }, + { name = "bar", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + { name = "bar", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "cleaver" }, + { name = "foo" }, ] [package.metadata] requires-dist = [ - { name = "package-bar" }, - { name = "package-cleaver" }, - { name = "package-foo" }, + { name = "bar" }, + { name = "cleaver" }, + { name = "foo" }, + ] + + [[package]] + name = "reject-cleaver-1" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "unrelated-dep2", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "unrelated-dep2", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + ] + sdist = { url = "http://[LOCALHOST]/files/reject_cleaver_1-1.0.0.tar.gz", hash = "sha256:a6383fa52eff39c3635fde33f9a4dd03d2fc7cffdd42fd4c148cf997e6fb5246", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/reject_cleaver_1-1.0.0-py3-none-any.whl", hash = "sha256:5b4d622444188c423a9476bb5c92358d7322f121d6e764c4b8d8bdc14e9f4017", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "unrelated-dep2" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + resolution-markers = [ + "sys_platform == 'linux'", + ] + sdist = { url = "http://[LOCALHOST]/files/unrelated_dep2-1.0.0.tar.gz", hash = "sha256:43649d9e654b0a121308187b8a4f43fa2b498e08565e2432bd1e0e3f1728acb2", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/unrelated_dep2-1.0.0-py3-none-any.whl", hash = "sha256:a485b74955fe703c7c525c52ef72da556100f2ea4b49ca97e6fd6f183469fb43", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "unrelated-dep2" + version = "2.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + resolution-markers = [ + "sys_platform != 'linux'", + ] + sdist = { url = "http://[LOCALHOST]/files/unrelated_dep2-2.0.0.tar.gz", hash = "sha256:292e78906a8e8c74931f9a5b54aba9e6957afe87cca68a2a456363569e7aa652", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/unrelated_dep2-2.0.0-py3-none-any.whl", hash = "sha256:f666a4b92a10879856a6962ef582a2ab328edd174b8def895d859b10bfea49b6", upload-time = "2024-03-24T00:00:00Z" }, ] "# ); @@ -4027,7 +4018,7 @@ fn preferences_dependent_forking_tristable() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4064,6 +4055,7 @@ fn preferences_dependent_forking_tristable() -> Result<()> { /// cleaver 1 -> bar==1; fork==2 /// ``` /// +/// /// ```text /// preferences-dependent-forking /// ├── environment @@ -4073,8 +4065,8 @@ fn preferences_dependent_forking_tristable() -> Result<()> { /// │ │ ├── satisfied by bar-1.0.0 /// │ │ └── satisfied by bar-2.0.0 /// │ ├── requires cleaver -/// │ │ ├── satisfied by cleaver-2.0.0 -/// │ │ └── satisfied by cleaver-1.0.0 +/// │ │ ├── satisfied by cleaver-1.0.0 +/// │ │ └── satisfied by cleaver-2.0.0 /// │ └── requires foo /// │ ├── satisfied by foo-1.0.0 /// │ └── satisfied by foo-2.0.0 @@ -4082,18 +4074,18 @@ fn preferences_dependent_forking_tristable() -> Result<()> { /// │ ├── bar-1.0.0 /// │ └── bar-2.0.0 /// ├── cleaver -/// │ ├── cleaver-2.0.0 -/// │ │ ├── requires reject-cleaver-2 -/// │ │ │ └── satisfied by reject-cleaver-2-1.0.0 -/// │ │ ├── requires unrelated-dep==1; sys_platform == "linux" -/// │ │ │ └── satisfied by unrelated-dep-1.0.0 -/// │ │ └── requires unrelated-dep==2; sys_platform != "linux" -/// │ │ └── satisfied by unrelated-dep-2.0.0 -/// │ └── cleaver-1.0.0 -/// │ ├── requires bar==1; sys_platform != "linux" -/// │ │ └── satisfied by bar-1.0.0 -/// │ └── requires foo==1; sys_platform == "linux" -/// │ └── satisfied by foo-1.0.0 +/// │ ├── cleaver-1.0.0 +/// │ │ ├── requires bar==1 ; sys_platform != 'linux' +/// │ │ │ └── satisfied by bar-1.0.0 +/// │ │ └── requires foo==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by foo-1.0.0 +/// │ └── cleaver-2.0.0 +/// │ ├── requires reject-cleaver-2 +/// │ │ └── satisfied by reject-cleaver-2-1.0.0 +/// │ ├── requires unrelated-dep==1 ; sys_platform == 'linux' +/// │ │ └── satisfied by unrelated-dep-1.0.0 +/// │ └── requires unrelated-dep==2 ; sys_platform != 'linux' +/// │ └── satisfied by unrelated-dep-2.0.0 /// ├── foo /// │ ├── foo-1.0.0 /// │ └── foo-2.0.0 @@ -4109,10 +4101,7 @@ fn preferences_dependent_forking_tristable() -> Result<()> { #[test] fn preferences_dependent_forking() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"preferences-dependent-forking-", "package-")); + let server = PackseServer::new("fork/preferences-dependent-forking.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4121,17 +4110,19 @@ fn preferences_dependent_forking() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''preferences-dependent-forking-cleaver''', - '''preferences-dependent-forking-foo''', - '''preferences-dependent-forking-bar''', + '''cleaver''', + '''foo''', + '''bar''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4157,49 +4148,49 @@ fn preferences_dependent_forking() -> Result<()> { ] [[package]] - name = "package-bar" + name = "bar" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform != 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bar-1.0.0.tar.gz", hash = "sha256:78ac06984ed655965d557096cd218087641602f523eb74dc241e3237c7d6129b" } + sdist = { url = "http://[LOCALHOST]/files/bar-1.0.0.tar.gz", hash = "sha256:d373f4858d602855ef53231a7f24ebff4e67e1fe30a2e810ee2b2a29b9d1a50a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bar-1.0.0-py3-none-any.whl", hash = "sha256:bf0ab2b019e425ef38d7c5632551fefd42aaa8ab96357ab2d3ce0124dfdfe892" }, + { url = "http://[LOCALHOST]/files/bar-1.0.0-py3-none-any.whl", hash = "sha256:2fbf0e0a7dd4f48a8b1c2148f73ba0c314699d0bfa0a8ec9b9bcb8105882e9fc", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-bar" + name = "bar" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'linux'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bar-2.0.0.tar.gz", hash = "sha256:70831af376e906171eb5905b0a163f92418e1398400b6ad749b04c7ba27102e1" } + sdist = { url = "http://[LOCALHOST]/files/bar-2.0.0.tar.gz", hash = "sha256:fa9a4faf506228722784ed740a362bccd96913f4f98a4e10d45ab79d8abb270a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_bar-2.0.0-py3-none-any.whl", hash = "sha256:fc188f8e81088843447d1863c1098bdeef1d051040b8caaf94b0421fa331d827" }, + { url = "http://[LOCALHOST]/files/bar-2.0.0-py3-none-any.whl", hash = "sha256:563b1af3238a4ad819f2b95b74f940319a2ef30ed7991a2416fa98aa115da87d", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-cleaver" + name = "cleaver" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } dependencies = [ - { name = "package-bar", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - { name = "package-foo", marker = "sys_platform == 'linux'" }, + { name = "bar", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + { name = "foo", marker = "sys_platform == 'linux'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_cleaver-1.0.0.tar.gz", hash = "sha256:380812f7558b9df90347bf44e47a240e4a5ff5372628c00b3c467e95858b0e47" } + sdist = { url = "http://[LOCALHOST]/files/cleaver-1.0.0.tar.gz", hash = "sha256:9156f0d8df99763e90677ca1b4dab67bee5fa33f59d102626dd91505cf332d2a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_cleaver-1.0.0-py3-none-any.whl", hash = "sha256:1873c290ddafcd254f74bc4d80000b094159212141172ea24f7565dcd3bbcfe2" }, + { url = "http://[LOCALHOST]/files/cleaver-1.0.0-py3-none-any.whl", hash = "sha256:f49d93330cfe3f7096636c506aa522a497eae44d08b07f6276caf784ec87f65b", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-foo" + name = "foo" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_foo-1.0.0.tar.gz", hash = "sha256:477ad43616170d9bfec354f737d7025c5e92fce2faa0fdbf25d4c3c077a5df54" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/foo-1.0.0.tar.gz", hash = "sha256:4d4cf959f969e0a39663aca6064428819f1e1c6be60d9a35164801ce17950ed4", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_foo-1.0.0-py3-none-any.whl", hash = "sha256:8a40eef897548e3434cab40ccdfe88648dced3bbe4f2ca40404e5dbb57d8025b" }, + { url = "http://[LOCALHOST]/files/foo-1.0.0-py3-none-any.whl", hash = "sha256:df9a39d54a6d71872deb1537d7e331de0ede3b725d630a050fee3efd3fe5145b", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -4207,17 +4198,17 @@ fn preferences_dependent_forking() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-bar", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform != 'linux'" }, - { name = "package-bar", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'linux'" }, - { name = "package-cleaver" }, - { name = "package-foo" }, + { name = "bar", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform != 'linux'" }, + { name = "bar", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'linux'" }, + { name = "cleaver" }, + { name = "foo" }, ] [package.metadata] requires-dist = [ - { name = "package-bar" }, - { name = "package-cleaver" }, - { name = "package-foo" }, + { name = "bar" }, + { name = "cleaver" }, + { name = "foo" }, ] "# ); @@ -4229,7 +4220,7 @@ fn preferences_dependent_forking() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4258,22 +4249,23 @@ fn preferences_dependent_forking() -> Result<()> { /// selected in the context of `a < 2 ; sys_platform == 'illumos'`, so `z` /// should never appear in the resolution. /// +/// /// ```text /// fork-remaining-universe-partitioning /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a>=2; sys_platform == "windows" +/// │ ├── requires a>=2 ; sys_platform == 'windows' /// │ │ └── satisfied by a-2.0.0 -/// │ └── requires a<2; sys_platform == "illumos" +/// │ └── requires a<2 ; sys_platform == 'illumos' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ ├── requires b>=2; os_name == "linux" +/// │ │ ├── requires b>=2 ; os_name == 'linux' /// │ │ │ └── satisfied by b-2.0.0 -/// │ │ ├── requires b<2; os_name == "darwin" +/// │ │ ├── requires b<2 ; os_name == 'darwin' /// │ │ │ └── satisfied by b-1.0.0 -/// │ │ └── requires z; sys_platform == "windows" +/// │ │ └── requires z ; sys_platform == 'windows' /// │ │ └── satisfied by z-1.0.0 /// │ └── a-2.0.0 /// ├── b @@ -4285,10 +4277,7 @@ fn preferences_dependent_forking() -> Result<()> { #[test] fn fork_remaining_universe_partitioning() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-remaining-universe-partitioning-", "package-")); + let server = PackseServer::new("fork/remaining-universe-partitioning.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4297,16 +4286,18 @@ fn fork_remaining_universe_partitioning() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-remaining-universe-partitioning-a>=2; sys_platform == "windows"''', - '''fork-remaining-universe-partitioning-a<2; sys_platform == "illumos"''', + '''a>=2 ; sys_platform == 'windows'''', + '''a<2 ; sys_platform == 'illumos'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4335,57 +4326,57 @@ fn fork_remaining_universe_partitioning() -> Result<()> { ] [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "os_name == 'darwin' and sys_platform == 'illumos'", "os_name == 'linux' and sys_platform == 'illumos'", "os_name != 'darwin' and os_name != 'linux' and sys_platform == 'illumos'", ] dependencies = [ - { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "os_name == 'darwin' and sys_platform == 'illumos'" }, - { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "os_name == 'linux' and sys_platform == 'illumos'" }, + { name = "b", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "os_name == 'darwin' and sys_platform == 'illumos'" }, + { name = "b", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "os_name == 'linux' and sys_platform == 'illumos'" }, ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_a-1.0.0.tar.gz", hash = "sha256:8265b2f0d92cd4d63f303ca1a81c0920b9bcb9026434388b5c41847772ea2e41" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:1073194686665f5b1459914275b353a20eafca9d9aa550f4c493aa0aceba119a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_a-1.0.0-py3-none-any.whl", hash = "sha256:2964220ab58b53d9e84ecb9f384d7284e914280779e0157ea5f1c1f4375a67b0" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:de627f2f3dc58918c496b5b61036d5a8c19c769fa5c933889bd805366fa2de1d", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-a" + name = "a" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "sys_platform == 'windows'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_a-2.0.0.tar.gz", hash = "sha256:a06312972c1436362b2af1366918160dd775f046a2a6991d7ec6f3ae17e2da4e" } + sdist = { url = "http://[LOCALHOST]/files/a-2.0.0.tar.gz", hash = "sha256:80ec95a66cff82a78a3333e3f5702e4254cf80533f21762933252eec58c9869a", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_a-2.0.0-py3-none-any.whl", hash = "sha256:4e4f4e2dd97e69715d430c227bdaa8635a46b4eac5d26b9d48edf6ea21c2ea9c" }, + { url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-any.whl", hash = "sha256:833374310e0a15880f3be9e6d082f527c9ac70129b2054d733da9b754315361f", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "os_name == 'darwin' and sys_platform == 'illumos'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_b-1.0.0.tar.gz", hash = "sha256:a41c05053e9b6c47c7d1e66a7b0ae74542b40008f906c0bca8e65ddbb124c8f1" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:b532bd9c3ccd69c4d5e915542dc50fb748c91c7a8e204c75387178d68fca113f", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_b-1.0.0-py3-none-any.whl", hash = "sha256:60c8698c2721d7abba70175c61d206f47a77729268735740294778b6fd1913e4" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:a4c65510001153cab97a29ff219ad86e0d4330653ca89d9d4c84187ccf14c621", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] - name = "package-b" + name = "b" version = "2.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } resolution-markers = [ "os_name == 'linux' and sys_platform == 'illumos'", ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_b-2.0.0.tar.gz", hash = "sha256:043c931dd9a88b360ec57b3bc6753b5df22f0e844b7589f1e42afd84c9de9d76" } + sdist = { url = "http://[LOCALHOST]/files/b-2.0.0.tar.gz", hash = "sha256:18fb09ba28eba255186405065e027093a6e952fa71eb565b4c46d619fdb60809", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_remaining_universe_partitioning_b-2.0.0-py3-none-any.whl", hash = "sha256:2bea93dc4ccddac32a3f3ab47df20c9f39a6fb7c34a2916d4df6397442202ba9" }, + { url = "http://[LOCALHOST]/files/b-2.0.0-py3-none-any.whl", hash = "sha256:04cc57f7563029528b6d23283933b244b6f52ba1543fad54687c586d6e639fc4", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -4393,14 +4384,14 @@ fn fork_remaining_universe_partitioning() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'illumos'" }, - { name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "sys_platform == 'windows'" }, + { name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'illumos'" }, + { name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "sys_platform == 'windows'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'illumos'", specifier = "<2" }, - { name = "package-a", marker = "sys_platform == 'windows'", specifier = ">=2" }, + { name = "a", marker = "sys_platform == 'illumos'", specifier = "<2" }, + { name = "a", marker = "sys_platform == 'windows'", specifier = ">=2" }, ] "# ); @@ -4412,7 +4403,7 @@ fn fork_remaining_universe_partitioning() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4425,12 +4416,13 @@ fn fork_remaining_universe_partitioning() -> Result<()> { /// In particular, this is tested via the `python_full_version` marker with /// a pre-release version. /// +/// /// ```text /// fork-requires-python-full-prerelease /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a==1.0.0; python_full_version == "3.9b1" +/// │ └── requires a==1.0.0 ; python_full_version == '3.9' /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 @@ -4438,10 +4430,7 @@ fn fork_remaining_universe_partitioning() -> Result<()> { #[test] fn fork_requires_python_full_prerelease() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-requires-python-full-prerelease-", "package-")); + let server = PackseServer::new("fork/requires-python-full-prerelease.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4450,15 +4439,17 @@ fn fork_requires_python_full_prerelease() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-requires-python-full-prerelease-a==1.0.0; python_full_version == "3.9b1"''', + '''a==1.0.0 ; python_full_version == '3.9'''', ] requires-python = ">=3.10" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4485,7 +4476,7 @@ fn fork_requires_python_full_prerelease() -> Result<()> { source = { virtual = "." } [package.metadata] - requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9'", specifier = "==1.0.0" }] + requires-dist = [{ name = "a", marker = "python_full_version == '3.9'", specifier = "==1.0.0" }] "# ); }); @@ -4496,7 +4487,7 @@ fn fork_requires_python_full_prerelease() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4509,12 +4500,13 @@ fn fork_requires_python_full_prerelease() -> Result<()> { /// In particular, this is tested via the `python_full_version` marker /// instead of the more common `python_version` marker. /// +/// /// ```text /// fork-requires-python-full /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a==1.0.0; python_full_version == "3.9" +/// │ └── requires a==1.0.0 ; python_full_version == '3.9' /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 @@ -4522,10 +4514,7 @@ fn fork_requires_python_full_prerelease() -> Result<()> { #[test] fn fork_requires_python_full() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-requires-python-full-", "package-")); + let server = PackseServer::new("fork/requires-python-full.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4534,15 +4523,17 @@ fn fork_requires_python_full() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-requires-python-full-a==1.0.0; python_full_version == "3.9"''', + '''a==1.0.0 ; python_full_version == '3.9'''', ] requires-python = ">=3.10" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4569,7 +4560,7 @@ fn fork_requires_python_full() -> Result<()> { source = { virtual = "." } [package.metadata] - requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9'", specifier = "==1.0.0" }] + requires-dist = [{ name = "a", marker = "python_full_version == '3.9'", specifier = "==1.0.0" }] "# ); }); @@ -4580,7 +4571,7 @@ fn fork_requires_python_full() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4597,12 +4588,13 @@ fn fork_requires_python_full() -> Result<()> { /// considered disjoint with `python_version == '3.10'`. Thus, the /// dependency `a` below was erroneously excluded. It should be included. /// +/// /// ```text /// fork-requires-python-patch-overlap /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a==1.0.0; python_version == "3.10" +/// │ └── requires a==1.0.0 ; python_full_version == '3.10.*' /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 @@ -4611,10 +4603,7 @@ fn fork_requires_python_full() -> Result<()> { #[test] fn fork_requires_python_patch_overlap() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-requires-python-patch-overlap-", "package-")); + let server = PackseServer::new("fork/requires-python-patch-overlap.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4623,15 +4612,17 @@ fn fork_requires_python_patch_overlap() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-requires-python-patch-overlap-a==1.0.0; python_version == "3.10"''', + '''a==1.0.0 ; python_full_version == '3.10.*'''', ] requires-python = ">=3.10.1" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4653,12 +4644,12 @@ fn fork_requires_python_patch_overlap() -> Result<()> { requires-python = ">=3.10.1" [[package]] - name = "package-a" + name = "a" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_requires_python_patch_overlap_a-1.0.0.tar.gz", hash = "sha256:98210d2d45238045c16262c38a2211b3239e1ea164dd6061292f6012e5c85d1e" } + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:0f32d06c5dab1a669df8d282b93b1d1af33e685ed4f52393d0b216436b5f52dc", upload-time = "2024-03-24T00:00:00Z" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_requires_python_patch_overlap_a-1.0.0-py3-none-any.whl", hash = "sha256:a6a97b0e0d0b176aa3942b7f58c8898a32e7050ada4349f6105922d3e2db3924" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:277ee4f1bb98d9591ba3a3a28364d1f00a54384c08d8f32100afc01ae76491a5", upload-time = "2024-03-24T00:00:00Z" }, ] [[package]] @@ -4666,11 +4657,11 @@ fn fork_requires_python_patch_overlap() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", marker = "python_full_version < '3.11'" }, + { name = "a", marker = "python_full_version < '3.11'" }, ] [package.metadata] - requires-dist = [{ name = "package-a", marker = "python_full_version == '3.10.*'", specifier = "==1.0.0" }] + requires-dist = [{ name = "a", marker = "python_full_version == '3.10.*'", specifier = "==1.0.0" }] "# ); }); @@ -4681,7 +4672,7 @@ fn fork_requires_python_patch_overlap() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4691,12 +4682,13 @@ fn fork_requires_python_patch_overlap() -> Result<()> { /// This tests that a `Requires-Python` specifier will result in the /// exclusion of dependency specifications that cannot possibly satisfy it. /// +/// /// ```text /// fork-requires-python /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a==1.0.0; python_version == "3.9" +/// │ └── requires a==1.0.0 ; python_full_version == '3.9.*' /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 @@ -4704,10 +4696,7 @@ fn fork_requires_python_patch_overlap() -> Result<()> { #[test] fn fork_requires_python() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"fork-requires-python-", "package-")); + let server = PackseServer::new("fork/requires-python.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4716,15 +4705,17 @@ fn fork_requires_python() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''fork-requires-python-a==1.0.0; python_version == "3.9"''', + '''a==1.0.0 ; python_full_version == '3.9.*'''', ] requires-python = ">=3.10" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4751,7 +4742,7 @@ fn fork_requires_python() -> Result<()> { source = { virtual = "." } [package.metadata] - requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9.*'", specifier = "==1.0.0" }] + requires-dist = [{ name = "a", marker = "python_full_version == '3.9.*'", specifier = "==1.0.0" }] "# ); }); @@ -4762,7 +4753,7 @@ fn fork_requires_python() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4785,10 +4776,7 @@ fn fork_requires_python() -> Result<()> { #[test] fn requires_python_wheels() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-python-wheels-", "package-")); + let server = PackseServer::new("tag_and_markers/requires-python-wheels.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4797,15 +4785,17 @@ fn requires_python_wheels() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''requires-python-wheels-a==1.0.0''', + '''a==1.0.0''', ] requires-python = ">=3.10" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4826,26 +4816,26 @@ fn requires_python_wheels() -> Result<()> { revision = 3 requires-python = ">=3.10" + [[package]] + name = "a" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:0f32d06c5dab1a669df8d282b93b1d1af33e685ed4f52393d0b216436b5f52dc", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-1.0.0-cp310-cp310-any.whl", hash = "sha256:34c6734e2427cc772605ac6710cf4e95c3556fd191b308b65c4d5f056fc95530", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/a-1.0.0-cp311-cp311-any.whl", hash = "sha256:a3aca28e2bd4f75c53a651a42fd54661d8aec43c1f9a4c703b3a301ad9deb5e2", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, + { name = "a" }, ] [package.metadata] - requires-dist = [{ name = "package-a", specifier = "==1.0.0" }] - - [[package]] - name = "package-a" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/requires_python_wheels_a-1.0.0.tar.gz", hash = "sha256:a47ad4fb0ebd76fcde3bf4aabf5d9393039f76822d990858e23ad17cd23dc67a" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/requires_python_wheels_a-1.0.0-cp310-cp310-any.whl", hash = "sha256:38109d4f29bd910e11cf3ea462fe06e740a346e76d1afa95fda2228845725de5" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/requires_python_wheels_a-1.0.0-cp311-cp311-any.whl", hash = "sha256:38109d4f29bd910e11cf3ea462fe06e740a346e76d1afa95fda2228845725de5" }, - ] + requires-dist = [{ name = "a", specifier = "==1.0.0" }] "# ); }); @@ -4856,7 +4846,7 @@ fn requires_python_wheels() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4870,11 +4860,11 @@ fn requires_python_wheels() -> Result<()> { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a==1.0.0; sys_platform == "win32" +/// │ └── requires a==1.0.0 ; sys_platform == 'win32' /// │ └── satisfied by a-1.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ └── requires b==1.0.0; sys_platform == "linux" +/// │ └── requires b==1.0.0 ; sys_platform == 'linux' /// │ └── satisfied by b-1.0.0 /// └── b /// └── b-1.0.0 @@ -4882,10 +4872,7 @@ fn requires_python_wheels() -> Result<()> { #[test] fn unreachable_package() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"unreachable-package-", "package-")); + let server = PackseServer::new("tag_and_markers/unreachable-package.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4894,15 +4881,17 @@ fn unreachable_package() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''unreachable-package-a==1.0.0; sys_platform == "win32"''', + '''a==1.0.0 ; sys_platform == 'win32'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -4923,25 +4912,25 @@ fn unreachable_package() -> Result<()> { revision = 3 requires-python = ">=3.12" + [[package]] + name = "a" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:4e402b215539e074a824ab44c7eaf04b6451bb1ab36d0cb045374828bb8bdcc7", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:9c02f18ae50a1da3421fa31399fe1497172375a7a6c80e8d1485ed12c3e64ee3", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", marker = "sys_platform == 'win32'" }, + { name = "a", marker = "sys_platform == 'win32'" }, ] [package.metadata] - requires-dist = [{ name = "package-a", marker = "sys_platform == 'win32'", specifier = "==1.0.0" }] - - [[package]] - name = "package-a" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_package_a-1.0.0.tar.gz", hash = "sha256:4ceb6be97b8f4526768c02f4a270cf270e225361eb42d1e87426b164be82f383" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_package_a-1.0.0-py3-none-any.whl", hash = "sha256:a61dae53684bd2dbc1bd96b375b55e0c2f445b47d4c601b061d3fe1138fd805d" }, - ] + requires-dist = [{ name = "a", marker = "sys_platform == 'win32'", specifier = "==1.0.0" }] "# ); }); @@ -4952,7 +4941,7 @@ fn unreachable_package() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -4966,11 +4955,11 @@ fn unreachable_package() -> Result<()> { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a==1.0.0; sys_platform == "win32" +/// │ ├── requires a==1.0.0 ; sys_platform == 'win32' /// │ │ └── satisfied by a-1.0.0 -/// │ ├── requires b==1.0.0; sys_platform == "linux" +/// │ ├── requires b==1.0.0 ; sys_platform == 'linux' /// │ │ └── satisfied by b-1.0.0 -/// │ └── requires c==1.0.0; sys_platform == "darwin" +/// │ └── requires c==1.0.0 ; sys_platform == 'darwin' /// │ └── satisfied by c-1.0.0 /// ├── a /// │ └── a-1.0.0 @@ -4982,10 +4971,7 @@ fn unreachable_package() -> Result<()> { #[test] fn unreachable_wheels() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"unreachable-wheels-", "package-")); + let server = PackseServer::new("tag_and_markers/unreachable-wheels.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -4994,17 +4980,19 @@ fn unreachable_wheels() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''unreachable-wheels-a==1.0.0; sys_platform == "win32"''', - '''unreachable-wheels-b==1.0.0; sys_platform == "linux"''', - '''unreachable-wheels-c==1.0.0; sys_platform == "darwin"''', + '''a==1.0.0 ; sys_platform == 'win32'''', + '''b==1.0.0 ; sys_platform == 'linux'''', + '''c==1.0.0 ; sys_platform == 'darwin'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -5025,49 +5013,49 @@ fn unreachable_wheels() -> Result<()> { revision = 3 requires-python = ">=3.12" + [[package]] + name = "a" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:3d2b4c28a4e112f3a1cef1db4dc5efa33fcbbcc38bc11ccc80321097db86c097", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:559a9c629536d99c1213064c303dcc6ee8dd2917824c0a0f6549f94d0be02abd", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "b" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:b532bd9c3ccd69c4d5e915542dc50fb748c91c7a8e204c75387178d68fca113f", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/b-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d0fc532c5d6e2f11ca522dfd3684b50b132dd1db78fba3ad510023f4d5830b7", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8f328487f947ee5119d348f2c73ab9119dd29349b840f6f26e0d351245d6084b", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "c" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/c-1.0.0.tar.gz", hash = "sha256:6e14a2e7cc6be61fa5aa41c0e55beff8b708a3aea257fed948306a0741bb5c47", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/c-1.0.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a25d2ba9ff1417f63313691a2f686f30b617a8696329023e44b7f7fc96573598", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a", marker = "sys_platform == 'win32'" }, - { name = "package-b", marker = "sys_platform == 'linux'" }, - { name = "package-c", marker = "sys_platform == 'darwin'" }, + { name = "a", marker = "sys_platform == 'win32'" }, + { name = "b", marker = "sys_platform == 'linux'" }, + { name = "c", marker = "sys_platform == 'darwin'" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "sys_platform == 'win32'", specifier = "==1.0.0" }, - { name = "package-b", marker = "sys_platform == 'linux'", specifier = "==1.0.0" }, - { name = "package-c", marker = "sys_platform == 'darwin'", specifier = "==1.0.0" }, - ] - - [[package]] - name = "package-a" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_a-1.0.0.tar.gz", hash = "sha256:7af67216432f3484632f08d42817a4425b2f7e6285d066b857b1b63479683173" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_a-1.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:fb0f200722746083492595dd95b5774f8807e632fa290b5615e66e921267cfd2" }, - ] - - [[package]] - name = "package-b" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_b-1.0.0.tar.gz", hash = "sha256:31943f973b8114889e4acd42add16396be474638db534604e6ca0a5d88e27ee5" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_b-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44108443dc9bf6bfb387875df0ee4ea793832d91417097586a0b90cc261da199" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_b-1.0.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:44108443dc9bf6bfb387875df0ee4ea793832d91417097586a0b90cc261da199" }, - ] - - [[package]] - name = "package-c" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_c-1.0.0.tar.gz", hash = "sha256:019230145b8dc90c687cb360077ffcd470243795480fd145d25b2a4af2b71858" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/unreachable_wheels_c-1.0.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:17f36f8a58561cf654235bbfecb9d4390bacc62d666ebcf4c93d94b750eb425e" }, + { name = "a", marker = "sys_platform == 'win32'", specifier = "==1.0.0" }, + { name = "b", marker = "sys_platform == 'linux'", specifier = "==1.0.0" }, + { name = "c", marker = "sys_platform == 'darwin'", specifier = "==1.0.0" }, ] "# ); @@ -5079,7 +5067,7 @@ fn unreachable_wheels() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -5093,18 +5081,18 @@ fn unreachable_wheels() -> Result<()> { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires psycopg[binary]; platform_python_implementation != "PyPy" +/// │ ├── requires psycopg[binary] ; platform_python_implementation != 'PyPy' /// │ │ ├── satisfied by psycopg-1.0.0 /// │ │ └── satisfied by psycopg-1.0.0[binary] -/// │ └── requires psycopg; platform_python_implementation == "PyPy" +/// │ └── requires psycopg ; platform_python_implementation == 'PyPy' /// │ ├── satisfied by psycopg-1.0.0 /// │ └── satisfied by psycopg-1.0.0[binary] /// ├── psycopg /// │ ├── psycopg-1.0.0 -/// │ │ └── requires tzdata; sys_platform == "win32" +/// │ │ └── requires tzdata ; sys_platform == 'win32' /// │ │ └── satisfied by tzdata-1.0.0 /// │ └── psycopg-1.0.0[binary] -/// │ └── requires psycopg-binary; implementation_name != "pypy" +/// │ └── requires psycopg-binary ; implementation_name != 'pypy' /// │ └── satisfied by psycopg-binary-1.0.0 /// ├── psycopg-binary /// │ └── psycopg-binary-1.0.0 @@ -5114,10 +5102,7 @@ fn unreachable_wheels() -> Result<()> { #[test] fn marker_variants_have_different_extras() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"marker-variants-have-different-extras-", "package-")); + let server = PackseServer::new("tag_and_markers/virtual-package-extra-priorities.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -5126,16 +5111,18 @@ fn marker_variants_have_different_extras() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''marker-variants-have-different-extras-psycopg[binary]; platform_python_implementation != "PyPy"''', - '''marker-variants-have-different-extras-psycopg; platform_python_implementation == "PyPy"''', + '''psycopg[binary] ; platform_python_implementation != 'PyPy'''', + '''psycopg ; platform_python_implementation == 'PyPy'''', ] requires-python = ">=3.12" - "### + "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -5160,54 +5147,54 @@ fn marker_variants_have_different_extras() -> Result<()> { "platform_python_implementation == 'PyPy'", ] - [[package]] - name = "package-psycopg" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-tzdata", marker = "sys_platform == 'win32'" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/marker_variants_have_different_extras_psycopg-1.0.0.tar.gz", hash = "sha256:73bfd3a8cd320336ded5c7cb2219544b8b66819c784cb88634fc2d716b42654b" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/marker_variants_have_different_extras_psycopg-1.0.0-py3-none-any.whl", hash = "sha256:29661efd1d691398d6fc0c1cca3618d795ce64ef50f4b152ff9cff471582afe1" }, - ] - - [package.optional-dependencies] - binary = [ - { name = "package-psycopg-binary", marker = "implementation_name != 'pypy' and platform_python_implementation != 'PyPy'" }, - ] - - [[package]] - name = "package-psycopg-binary" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/marker_variants_have_different_extras_psycopg_binary-1.0.0.tar.gz", hash = "sha256:988780eb0dc092cd5af36bef12d5c42404ee4dd73e6c29c842cc802a558d355e" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/marker_variants_have_different_extras_psycopg_binary-1.0.0-py3-none-any.whl", hash = "sha256:8f07333ec83cc11023198e50ec91314afbe96e3b905183ec060e60388d18eab4" }, - ] - - [[package]] - name = "package-tzdata" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/marker_variants_have_different_extras_tzdata-1.0.0.tar.gz", hash = "sha256:05613cdd40338ffb46f0c94efa618d5b64b4879040c520b2934b982772220f47" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/marker_variants_have_different_extras_tzdata-1.0.0-py3-none-any.whl", hash = "sha256:8d59a2ae73c64014e27574eae496a70d5619aca1ddcb04f5a589de97850b13a8" }, - ] - [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-psycopg" }, - { name = "package-psycopg", extra = ["binary"], marker = "platform_python_implementation != 'PyPy'" }, + { name = "psycopg" }, + { name = "psycopg", extra = ["binary"], marker = "platform_python_implementation != 'PyPy'" }, ] [package.metadata] requires-dist = [ - { name = "package-psycopg", marker = "platform_python_implementation == 'PyPy'" }, - { name = "package-psycopg", extras = ["binary"], marker = "platform_python_implementation != 'PyPy'" }, + { name = "psycopg", marker = "platform_python_implementation == 'PyPy'" }, + { name = "psycopg", extras = ["binary"], marker = "platform_python_implementation != 'PyPy'" }, + ] + + [[package]] + name = "psycopg" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, + ] + sdist = { url = "http://[LOCALHOST]/files/psycopg-1.0.0.tar.gz", hash = "sha256:c513b2021142cd4f00f9c09d92106d6cfe180f1b649f763ea6bd89b84ca9f1fb", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/psycopg-1.0.0-py3-none-any.whl", hash = "sha256:e91c6b77d7c6b4262b81b606577e444d27fcdaa22d6603742ced03b39492eda3", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [package.optional-dependencies] + binary = [ + { name = "psycopg-binary", marker = "implementation_name != 'pypy' and platform_python_implementation != 'PyPy'" }, + ] + + [[package]] + name = "psycopg-binary" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/psycopg_binary-1.0.0.tar.gz", hash = "sha256:64c3ee856436c3f1d564bf46476dfcd049a507b38eb74b14b48de437143d31c8", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/psycopg_binary-1.0.0-py3-none-any.whl", hash = "sha256:35474aba5923a77d2aac9ac1e92bb5e9dc0468b3b2611e120b3947ae956a1482", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "tzdata" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/tzdata-1.0.0.tar.gz", hash = "sha256:f2285c9ed855e3433bcd3aabb0b446753f97bcdf8dffebe4725d70af8a1f2fe5", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/tzdata-1.0.0-py3-none-any.whl", hash = "sha256:9dc909bec179ffb4d319a54ab73cb61e769c912be44743ff4411c5191432573f", upload-time = "2024-03-24T00:00:00Z" }, ] "# ); @@ -5219,7 +5206,7 @@ fn marker_variants_have_different_extras() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -5233,17 +5220,17 @@ fn marker_variants_have_different_extras() -> Result<()> { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ ├── requires a==1; python_version >= "3.8" +/// │ ├── requires a==1 ; python_full_version >= '3.8' /// │ │ └── satisfied by a-1.0.0 -/// │ └── requires b; python_version >= "3.9" +/// │ └── requires b ; python_full_version >= '3.9' /// │ ├── satisfied by b-1.0.0 /// │ └── satisfied by b-2.0.0 /// ├── a /// │ ├── a-1.0.0 -/// │ │ └── requires b==1; python_version >= "3.10" +/// │ │ └── requires b==1 ; python_full_version >= '3.10' /// │ │ └── satisfied by b-1.0.0 /// │ └── a-2.0.0 -/// │ └── requires b==1; python_version >= "3.10" +/// │ └── requires b==1 ; python_full_version >= '3.10' /// │ └── satisfied by b-1.0.0 /// └── b /// ├── b-1.0.0 @@ -5252,10 +5239,7 @@ fn marker_variants_have_different_extras() -> Result<()> { #[test] fn virtual_package_extra_priorities() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"virtual-package-extra-priorities-", "package-")); + let server = PackseServer::new("tag_and_markers/virtual-package-marker-priorities.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -5264,16 +5248,18 @@ fn virtual_package_extra_priorities() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''virtual-package-extra-priorities-a==1; python_version >= "3.8"''', - '''virtual-package-extra-priorities-b; python_version >= "3.9"''', + '''a==1 ; python_full_version >= '3.8'''', + '''b ; python_full_version >= '3.9'''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -5294,40 +5280,40 @@ fn virtual_package_extra_priorities() -> Result<()> { revision = 3 requires-python = ">=3.12" + [[package]] + name = "a" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "b" }, + ] + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:d01ad61c229cf2c4efe61755254b7419b078c7e712907c2e594b0a31ee1ac17e", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:d3c47a26ec1e08259468942a50736244908a076baf0d5f931f62407814ed96e5", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "b" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + sdist = { url = "http://[LOCALHOST]/files/b-1.0.0.tar.gz", hash = "sha256:b532bd9c3ccd69c4d5e915542dc50fb748c91c7a8e204c75387178d68fca113f", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/b-1.0.0-py3-none-any.whl", hash = "sha256:a4c65510001153cab97a29ff219ad86e0d4330653ca89d9d4c84187ccf14c621", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, - { name = "package-b" }, + { name = "a" }, + { name = "b" }, ] [package.metadata] requires-dist = [ - { name = "package-a", marker = "python_full_version >= '3.8'", specifier = "==1" }, - { name = "package-b", marker = "python_full_version >= '3.9'" }, - ] - - [[package]] - name = "package-a" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-b" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/virtual_package_extra_priorities_a-1.0.0.tar.gz", hash = "sha256:df3b975e935948c0e3ce80413fabacc7473b3b77fa6143c2d50134ca9821b982" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/virtual_package_extra_priorities_a-1.0.0-py3-none-any.whl", hash = "sha256:7d8ceba929775dd3294275637a76c1556b816705f86d9d913b92df708bbe09bc" }, - ] - - [[package]] - name = "package-b" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/virtual_package_extra_priorities_b-1.0.0.tar.gz", hash = "sha256:1871d4ccdcae672dbeda7e8c9bd16c28e3256e343470d1ae9f8bd2946ba7ab9f" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/virtual_package_extra_priorities_b-1.0.0-py3-none-any.whl", hash = "sha256:d92888ac830ae8768749436fec5133b9262667c2c743285f941b18bb13724222" }, + { name = "a", marker = "python_full_version >= '3.8'", specifier = "==1" }, + { name = "b", marker = "python_full_version >= '3.9'" }, ] "# ); @@ -5339,7 +5325,7 @@ fn virtual_package_extra_priorities() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -5353,7 +5339,7 @@ fn virtual_package_extra_priorities() -> Result<()> { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires win-only; sys_platform == "win32" +/// │ └── requires win-only ; sys_platform == 'win32' /// │ └── satisfied by win-only-1.0.0 /// └── win-only /// └── win-only-1.0.0 @@ -5361,10 +5347,7 @@ fn virtual_package_extra_priorities() -> Result<()> { #[test] fn requires_python_subset() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-python-subset-", "package-")); + let server = PackseServer::new("wheels/requires-python-subset.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -5373,20 +5356,22 @@ fn requires_python_subset() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''requires-python-subset-win-only; sys_platform == "win32"''', + '''win-only ; sys_platform == 'win32'''', ] requires-python = ">=3.12" [tool.uv] required-environments = [ - '''sys_platform == "linux"''', - '''sys_platform == "win32"''', + '''sys_platform == 'linux'''', + '''sys_platform == 'win32'''', ] "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -5416,18 +5401,18 @@ fn requires_python_subset() -> Result<()> { version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-win-only", marker = "sys_platform == 'win32'" }, + { name = "win-only", marker = "sys_platform == 'win32'" }, ] [package.metadata] - requires-dist = [{ name = "package-win-only", marker = "sys_platform == 'win32'" }] + requires-dist = [{ name = "win-only", marker = "sys_platform == 'win32'" }] [[package]] - name = "package-win-only" + name = "win-only" version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + source = { registry = "http://[LOCALHOST]/simple/" } wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/requires_python_subset_win_only-1.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:91d59021b1c4aad7449e315ae1248c5c588a7e84cb7592671a41453012302711" }, + { url = "http://[LOCALHOST]/files/win_only-1.0.0-cp312-abi3-win_amd64.whl", hash = "sha256:b53b82ec335953e0c0d5dc75c59ce658fd9b6c04330126109a7ba104e0d107e3", upload-time = "2024-03-24T00:00:00Z" }, ] "# ); @@ -5439,7 +5424,7 @@ fn requires_python_subset() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); @@ -5457,11 +5442,11 @@ fn requires_python_subset() -> Result<()> { /// │ └── satisfied by a-1.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ ├── requires b; platform_machine == "x86_64" +/// │ ├── requires b ; platform_machine == 'x86_64' /// │ │ └── satisfied by b-1.0.0 -/// │ ├── requires c; platform_machine == "aarch64" +/// │ ├── requires c ; platform_machine == 'aarch64' /// │ │ └── satisfied by c-1.0.0 -/// │ └── requires d; platform_machine == "i686" +/// │ └── requires d ; platform_machine == 'i686' /// │ └── satisfied by d-1.0.0 /// ├── b /// │ └── b-1.0.0 @@ -5473,10 +5458,7 @@ fn requires_python_subset() -> Result<()> { #[test] fn specific_architecture() -> Result<()> { let context = uv_test::test_context!("3.12"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"specific-architecture-", "package-")); + let server = PackseServer::new("wheels/specific-architecture.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -5485,15 +5467,17 @@ fn specific_architecture() -> Result<()> { name = "project" version = "0.1.0" dependencies = [ - '''specific-architecture-a''', + '''a''', ] requires-python = ">=3.12" "###, )?; + let filters = context.filters(); + let mut cmd = context.lock(); cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); + cmd.arg("--index-url").arg(server.index_url()); uv_snapshot!(filters, cmd, @" success: true exit_code: 0 @@ -5514,62 +5498,62 @@ fn specific_architecture() -> Result<()> { revision = 3 requires-python = ">=3.12" + [[package]] + name = "a" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + dependencies = [ + { name = "b", marker = "platform_machine == 'x86_64'" }, + { name = "c", marker = "platform_machine == 'aarch64'" }, + { name = "d", marker = "platform_machine == 'i686'" }, + ] + sdist = { url = "http://[LOCALHOST]/files/a-1.0.0.tar.gz", hash = "sha256:ea7bef4508a5b327b7ce64ab4ffda42d30d0818af6afab6297db8c32300e67d8", upload-time = "2024-03-24T00:00:00Z" } + wheels = [ + { url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-any.whl", hash = "sha256:ccd718f14fb28f4e947c39f67a39897f08eaf09034d0641ffb6ce41e580dac1a", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "b" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + wheels = [ + { url = "http://[LOCALHOST]/files/b-1.0.0-cp313-cp313-freebsd_13_aarch64.whl", hash = "sha256:f631c1c1a0ffc6b2ce37901dca7c5e468d80f9a5107f3e3ec2dd231424e51447", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-cp313-cp313-freebsd_13_x86_64.whl", hash = "sha256:c0a23d73fe066699bb1ed79150f23a989692c419edde2dc07b6f07ad7df04bb6", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:afc62d392afde77dc8cb1a76ca661ea28622a990e3655d96b101301526790393", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/b-1.0.0-cp313-cp313-manylinux2010_x86_64.whl", hash = "sha256:baa416bab7cc0d502bc96bc57672f2fa83dd56b7dc37b701126e16fcc8ce115a", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "c" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + wheels = [ + { url = "http://[LOCALHOST]/files/c-1.0.0-cp313-cp313-freebsd_13_aarch64.whl", hash = "sha256:e34952726fbb61bd86f63a767fa1fcc10a8e27737ef515b597617ee7fcb808ea", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-cp313-cp313-freebsd_13_x86_64.whl", hash = "sha256:c73f855a843c0842e67e01683f9b8e97391ec4de6f4e59e3bc43b7b2218f2fb9", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-cp313-cp313-macosx_10_9_arm64.whl", hash = "sha256:27e36c0dab216a5e265e247d2fb7cab31448beb97cefc22cce301885210b54df", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/c-1.0.0-cp313-cp313-manylinux2010_aarch64.whl", hash = "sha256:4d267230c94b48fb96c8eabadfd39eeb5efcb5d712f699feba087b1bb7ee18b5", upload-time = "2024-03-24T00:00:00Z" }, + ] + + [[package]] + name = "d" + version = "1.0.0" + source = { registry = "http://[LOCALHOST]/simple/" } + wheels = [ + { url = "http://[LOCALHOST]/files/d-1.0.0-cp313-cp313-freebsd_13_aarch64.whl", hash = "sha256:faa00bef937a8020e48e398b106bf2085b327128af2e0a91a1596dfa07035b0a", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/d-1.0.0-cp313-cp313-freebsd_13_x86_64.whl", hash = "sha256:0535a2b64b540cf7ab4e124fa4673054113fd4d3957ae6b77b63833729e308c9", upload-time = "2024-03-24T00:00:00Z" }, + { url = "http://[LOCALHOST]/files/d-1.0.0-cp313-cp313-manylinux2010_i686.whl", hash = "sha256:66575b89a885afc891c6c84589a50b2f5112a0d1731c924df1fd376010e659e8", upload-time = "2024-03-24T00:00:00Z" }, + ] + [[package]] name = "project" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "package-a" }, + { name = "a" }, ] [package.metadata] - requires-dist = [{ name = "package-a" }] - - [[package]] - name = "package-a" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - dependencies = [ - { name = "package-b", marker = "platform_machine == 'x86_64'" }, - { name = "package-c", marker = "platform_machine == 'aarch64'" }, - { name = "package-d", marker = "platform_machine == 'i686'" }, - ] - sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_a-1.0.0.tar.gz", hash = "sha256:d2ab13af91c3f80269717ccc9d71dae2b0cf59b0b53b7ce5e1687c3c12530518" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_a-1.0.0-py3-none-any.whl", hash = "sha256:55436c2630725abac18f06b5f2255ed32c447b8b6e8ef6abfe533259028abb82" }, - ] - - [[package]] - name = "package-b" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_b-1.0.0-cp313-cp313-freebsd_13_aarch64.whl", hash = "sha256:e494f9d058ab90fc641c4390bbd81164210daf0d1811672d187636653496f77d" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_b-1.0.0-cp313-cp313-freebsd_13_x86_64.whl", hash = "sha256:e494f9d058ab90fc641c4390bbd81164210daf0d1811672d187636653496f77d" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_b-1.0.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:e494f9d058ab90fc641c4390bbd81164210daf0d1811672d187636653496f77d" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_b-1.0.0-cp313-cp313-manylinux2010_x86_64.whl", hash = "sha256:e494f9d058ab90fc641c4390bbd81164210daf0d1811672d187636653496f77d" }, - ] - - [[package]] - name = "package-c" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_c-1.0.0-cp313-cp313-freebsd_13_aarch64.whl", hash = "sha256:64ebbefd7b78db539d4884fc7f2d9df24bd253511cb67350ac6b610291cd8405" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_c-1.0.0-cp313-cp313-freebsd_13_x86_64.whl", hash = "sha256:64ebbefd7b78db539d4884fc7f2d9df24bd253511cb67350ac6b610291cd8405" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_c-1.0.0-cp313-cp313-macosx_10_9_arm64.whl", hash = "sha256:64ebbefd7b78db539d4884fc7f2d9df24bd253511cb67350ac6b610291cd8405" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_c-1.0.0-cp313-cp313-manylinux2010_aarch64.whl", hash = "sha256:64ebbefd7b78db539d4884fc7f2d9df24bd253511cb67350ac6b610291cd8405" }, - ] - - [[package]] - name = "package-d" - version = "1.0.0" - source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } - wheels = [ - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_d-1.0.0-cp313-cp313-freebsd_13_aarch64.whl", hash = "sha256:f49cf7bda1ea7366cd0cae8b9f0d8975f7b29ae3569b2aa70fe2d5eb5176cc64" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_d-1.0.0-cp313-cp313-freebsd_13_x86_64.whl", hash = "sha256:f49cf7bda1ea7366cd0cae8b9f0d8975f7b29ae3569b2aa70fe2d5eb5176cc64" }, - { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/specific_architecture_d-1.0.0-cp313-cp313-manylinux2010_i686.whl", hash = "sha256:f49cf7bda1ea7366cd0cae8b9f0d8975f7b29ae3569b2aa70fe2d5eb5176cc64" }, - ] + requires-dist = [{ name = "a" }] "# ); }); @@ -5580,7 +5564,7 @@ fn specific_architecture() -> Result<()> { .arg("--locked") .env_remove(EnvVars::UV_EXCLUDE_NEWER) .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .assert() .success(); diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 79f534266c..aa435f19cb 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -20,9 +20,7 @@ use wiremock::{Mock, MockServer, ResponseTemplate}; use uv_fs::Simplified; use uv_static::EnvVars; -use uv_test::{ - DEFAULT_PYTHON_VERSION, TestContext, download_to_disk, packse_index_url, uv_snapshot, -}; +use uv_test::{DEFAULT_PYTHON_VERSION, TestContext, download_to_disk, uv_snapshot}; fn write_tar_gz(file: File, entries: &[(&str, &str)]) -> Result<()> { let enc = GzEncoder::new(file, flate2::Compression::default()); @@ -14397,6 +14395,8 @@ fn universal_constrained_environment() -> Result<()> { #[test] fn universal_required_environment() -> Result<()> { let context = uv_test::test_context!("3.12"); + let server = + uv_test::packse::PackseServer::new("wheels/no-sdist-no-wheels-with-matching-platform.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(&indoc::formatdoc! {r#" @@ -14404,7 +14404,7 @@ fn universal_required_environment() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.12" - dependencies = ["no-sdist-no-wheels-with-matching-platform-a"] + dependencies = ["a"] [tool.uv] required-environments = ["platform_machine == 'arm64'"] @@ -14414,7 +14414,7 @@ fn universal_required_environment() -> Result<()> { url = "{}" default = true "#, - packse_index_url() + server.index_url() })?; let filters: Vec<_> = context @@ -14431,7 +14431,7 @@ fn universal_required_environment() -> Result<()> { .arg("pyproject.toml") .arg("--universal") .arg("--exclude-newer-package") - .arg("no-sdist-no-wheels-with-matching-platform-a=false") + .arg("a=false") .env_remove(EnvVars::UV_EXCLUDE_NEWER), @" success: false exit_code: 1 @@ -14439,8 +14439,8 @@ fn universal_required_environment() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies for split (markers: platform_machine == 'arm64'): - ╰─▶ Because only no-sdist-no-wheels-with-matching-platform-a==1.0.0 is available and no-sdist-no-wheels-with-matching-platform-a==1.0.0 has no `platform_machine == 'arm64'`-compatible wheels, we can conclude that all versions of no-sdist-no-wheels-with-matching-platform-a cannot be used. - And because project depends on no-sdist-no-wheels-with-matching-platform-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 has no `platform_machine == 'arm64'`-compatible wheels, we can conclude that all versions of a cannot be used. + And because project depends on a, we can conclude that your requirements are unsatisfiable. "); Ok(()) @@ -15369,19 +15369,20 @@ fn universal_conflicting_override_urls() -> Result<()> { #[test] fn compile_lowest_extra_unpinned_warning() -> Result<()> { let context = uv_test::test_context!("3.12"); + let server = uv_test::packse::PackseServer::new("extras/all-extras-required.toml"); let requirements_in = context.temp_dir.child("requirements.in"); requirements_in.write_str(indoc::indoc! {r" - all-extras-required-a[extra_b,extra_c] - all-extras-required-b==1 - all-extras-required-c==1 + a[extra_b,extra_c] + b==1 + c==1 "})?; uv_snapshot!(context.filters(), context.pip_compile() .arg("--resolution") .arg("lowest") .arg("--index-url") - .arg(packse_index_url()) + .arg(server.index_url()) .arg(requirements_in.path()) .env_remove(EnvVars::UV_EXCLUDE_NEWER), @" success: true @@ -15389,19 +15390,19 @@ fn compile_lowest_extra_unpinned_warning() -> Result<()> { ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile --cache-dir [CACHE_DIR] --resolution lowest [TEMP_DIR]/requirements.in - all-extras-required-a==1.0.0 + a==1.0.0 # via -r requirements.in - all-extras-required-b==1.0.0 + b==1.0.0 # via # -r requirements.in - # all-extras-required-a - all-extras-required-c==1.0.0 + # a + c==1.0.0 # via # -r requirements.in - # all-extras-required-a + # a ----- stderr ----- - warning: The direct dependency `all-extras-required-a` is unpinned. Consider setting a lower bound when using `--resolution lowest` or `--resolution lowest-direct` to avoid using outdated versions. + warning: The direct dependency `a` is unpinned. Consider setting a lower bound when using `--resolution lowest` or `--resolution lowest-direct` to avoid using outdated versions. Resolved 3 packages in [TIME] " ); diff --git a/crates/uv/tests/it/pip_compile_scenarios.rs b/crates/uv/tests/it/pip_compile_scenarios.rs index 8094a337fb..8e01014e78 100644 --- a/crates/uv/tests/it/pip_compile_scenarios.rs +++ b/crates/uv/tests/it/pip_compile_scenarios.rs @@ -1,11 +1,10 @@ //! DO NOT EDIT //! -//! Generated with `./scripts/sync_scenarios.sh` -//! Scenarios from +//! Generated with `cargo dev generate-scenario-tests` +//! Scenarios from //! #![cfg(all(feature = "test-python", feature = "test-pypi", unix))] -use std::env; use std::process::Command; use anyhow::Result; @@ -14,14 +13,11 @@ use assert_fs::fixture::{FileWriteStr, PathChild}; use predicates::prelude::predicate; use uv_static::EnvVars; - -use uv_test::{ - TestContext, build_vendor_links_url, get_bin, packse_index_url, python_path_with_versions, - uv_snapshot, -}; +use uv_test::packse::PackseServer; +use uv_test::{TestContext, get_bin, python_path_with_versions, uv_snapshot}; /// Provision python binaries and return a `pip compile` command with options shared across all scenarios. -fn command(context: &TestContext, python_versions: &[&str]) -> Command { +fn command(context: &TestContext, python_versions: &[&str], server: &PackseServer) -> Command { let python_path = python_path_with_versions(&context.temp_dir, python_versions) .expect("Failed to create Python test path"); let mut command = Command::new(get_bin!()); @@ -30,9 +26,7 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { .arg("compile") .arg("requirements.in") .arg("--index-url") - .arg(packse_index_url()) - .arg("--find-links") - .arg(build_vendor_links_url()); + .arg(server.index_url()); context.add_shared_options(&mut command, true); command.env_remove(EnvVars::UV_EXCLUDE_NEWER); command.env(EnvVars::UV_PYTHON_SEARCH_PATH, python_path); @@ -57,15 +51,12 @@ fn command(context: &TestContext, python_versions: &[&str]) -> Command { fn compatible_python_incompatible_override() -> Result<()> { let context = uv_test::test_context!("3.11"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"compatible-python-incompatible-override-", "package-")); + let server = PackseServer::new("requires_python/compatible-python-incompatible-override.toml"); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("compatible-python-incompatible-override-a==1.0.0")?; + requirements_in.write_str("a==1.0.0")?; - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.9") , @" success: false @@ -75,10 +66,10 @@ fn compatible_python_incompatible_override() -> Result<()> { ----- stderr ----- warning: The requested Python version 3.9 is not available; 3.11.[X] will be used to build dependencies instead. × No solution found when resolving dependencies: - ╰─▶ Because the requested Python version (>=3.9) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. - And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because the requested Python version (>=3.9) does not satisfy Python>=3.10 and a==1.0.0 depends on Python>=3.10, we can conclude that a==1.0.0 cannot be used. + And because you require a==1.0.0, we can conclude that your requirements are unsatisfiable. - hint: The `--python-version` value (>=3.9) includes Python versions that are not supported by your dependencies (e.g., package-a==1.0.0 only supports >=3.10). Consider using a higher `--python-version` value. + hint: The `--python-version` value (>=3.9) includes Python versions that are not supported by your dependencies (e.g., a==1.0.0 only supports >=3.10). Consider using a higher `--python-version` value. " ); @@ -105,20 +96,15 @@ fn compatible_python_incompatible_override() -> Result<()> { fn incompatible_python_compatible_override_available_no_wheels() -> Result<()> { let context = uv_test::test_context!("3.9"); let python_versions = &["3.11"]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"incompatible-python-compatible-override-available-no-wheels-", - "package-", - )); + let server = PackseServer::new( + "requires_python/incompatible-python-compatible-override-available-no-wheels.toml", + ); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in - .write_str("incompatible-python-compatible-override-available-no-wheels-a==1.0.0")?; + requirements_in.write_str("a==1.0.0")?; // Since there is a compatible Python version available on the system, it should be used to build the source distributions. - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.11") , @" success: true @@ -126,7 +112,7 @@ fn incompatible_python_compatible_override_available_no_wheels() -> Result<()> { ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11 - package-a==1.0.0 + a==1.0.0 # via -r requirements.in ----- stderr ----- @@ -134,9 +120,10 @@ fn incompatible_python_compatible_override_available_no_wheels() -> Result<()> { " ); - output.assert().success().stdout(predicate::str::contains( - "incompatible-python-compatible-override-available-no-wheels-a==1.0.0", - )); + output + .assert() + .success() + .stdout(predicate::str::contains("a==1.0.0")); Ok(()) } @@ -158,20 +145,15 @@ fn incompatible_python_compatible_override_available_no_wheels() -> Result<()> { fn incompatible_python_compatible_override_no_compatible_wheels() -> Result<()> { let context = uv_test::test_context!("3.9"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"incompatible-python-compatible-override-no-compatible-wheels-", - "package-", - )); + let server = PackseServer::new( + "requires_python/incompatible-python-compatible-override-no-compatible-wheels.toml", + ); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in - .write_str("incompatible-python-compatible-override-no-compatible-wheels-a==1.0.0")?; + requirements_in.write_str("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 determine its dependencies. However, the source distribution includes static metadata, which we can use to determine dependencies without building the package. - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.11") , @" success: true @@ -179,7 +161,7 @@ fn incompatible_python_compatible_override_no_compatible_wheels() -> Result<()> ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11 - package-a==1.0.0 + a==1.0.0 # via -r requirements.in ----- stderr ----- @@ -213,19 +195,15 @@ fn incompatible_python_compatible_override_no_compatible_wheels() -> Result<()> fn incompatible_python_compatible_override_other_wheel() -> Result<()> { let context = uv_test::test_context!("3.9"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"incompatible-python-compatible-override-other-wheel-", - "package-", - )); + let server = PackseServer::new( + "requires_python/incompatible-python-compatible-override-other-wheel.toml", + ); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("incompatible-python-compatible-override-other-wheel-a")?; + requirements_in.write_str("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 source distribution to determine its dependencies. However, the source distribution includes static metadata, which we can use to determine dependencies without building the package. - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.11") , @" success: true @@ -233,7 +211,7 @@ fn incompatible_python_compatible_override_other_wheel() -> Result<()> { ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11 - package-a==1.0.0 + a==1.0.0 # via -r requirements.in ----- stderr ----- @@ -264,20 +242,15 @@ fn incompatible_python_compatible_override_other_wheel() -> Result<()> { fn incompatible_python_compatible_override_unavailable_no_wheels() -> Result<()> { let context = uv_test::test_context!("3.9"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"incompatible-python-compatible-override-unavailable-no-wheels-", - "package-", - )); + let server = PackseServer::new( + "requires_python/incompatible-python-compatible-override-unavailable-no-wheels.toml", + ); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in - .write_str("incompatible-python-compatible-override-unavailable-no-wheels-a==1.0.0")?; + requirements_in.write_str("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 dependencies. However, the source distribution includes static metadata, which we can use to determine dependencies without building the package. - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.11") , @" success: true @@ -285,7 +258,7 @@ fn incompatible_python_compatible_override_unavailable_no_wheels() -> Result<()> ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11 - package-a==1.0.0 + a==1.0.0 # via -r requirements.in ----- stderr ----- @@ -316,15 +289,12 @@ fn incompatible_python_compatible_override_unavailable_no_wheels() -> Result<()> fn incompatible_python_compatible_override() -> Result<()> { let context = uv_test::test_context!("3.9"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"incompatible-python-compatible-override-", "package-")); + let server = PackseServer::new("requires_python/incompatible-python-compatible-override.toml"); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("incompatible-python-compatible-override-a==1.0.0")?; + requirements_in.write_str("a==1.0.0")?; - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.11") , @" success: true @@ -332,7 +302,7 @@ fn incompatible_python_compatible_override() -> Result<()> { ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.11 - package-a==1.0.0 + a==1.0.0 # via -r requirements.in ----- stderr ----- @@ -341,9 +311,10 @@ fn incompatible_python_compatible_override() -> Result<()> { " ); - output.assert().success().stdout(predicate::str::contains( - "incompatible-python-compatible-override-a==1.0.0", - )); + output + .assert() + .success() + .stdout(predicate::str::contains("a==1.0.0")); Ok(()) } @@ -359,23 +330,20 @@ fn incompatible_python_compatible_override() -> Result<()> { /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 -/// └── requires python>=3.9.4 +/// └── requires python>=3.9.4 (incompatible with environment) /// ``` #[cfg(feature = "test-python-patch")] #[test] fn python_patch_override_no_patch() -> Result<()> { let context = uv_test::test_context!("3.9.21"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-patch-override-no-patch-", "package-")); + let server = PackseServer::new("requires_python/python-patch-override-no-patch.toml"); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("python-patch-override-no-patch-a==1.0.0")?; + requirements_in.write_str("a==1.0.0")?; // Since the resolver is asked to solve with 3.9, the minimum compatible Python requirement is treated as 3.9.0. - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.9") , @" success: false @@ -384,10 +352,10 @@ fn python_patch_override_no_patch() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the requested Python version (>=3.9) does not satisfy Python>=3.9.4 and package-a==1.0.0 depends on Python>=3.9.4, we can conclude that package-a==1.0.0 cannot be used. - And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because the requested Python version (>=3.9) does not satisfy Python>=3.9.4 and a==1.0.0 depends on Python>=3.9.4, we can conclude that a==1.0.0 cannot be used. + And because you require a==1.0.0, we can conclude that your requirements are unsatisfiable. - hint: The `--python-version` value (>=3.9) includes Python versions that are not supported by your dependencies (e.g., package-a==1.0.0 only supports >=3.9.4). Consider using a higher `--python-version` value. + hint: The `--python-version` value (>=3.9) includes Python versions that are not supported by your dependencies (e.g., a==1.0.0 only supports >=3.9.4). Consider using a higher `--python-version` value. " ); @@ -407,22 +375,18 @@ fn python_patch_override_no_patch() -> Result<()> { /// │ └── satisfied by a-1.0.0 /// └── a /// └── a-1.0.0 -/// └── requires python>=3.9.0 /// ``` #[cfg(feature = "test-python-patch")] #[test] fn python_patch_override_patch_compatible() -> Result<()> { let context = uv_test::test_context!("3.9.21"); let python_versions = &[]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-patch-override-patch-compatible-", "package-")); + let server = PackseServer::new("requires_python/python-patch-override-patch-compatible.toml"); let requirements_in = context.temp_dir.child("requirements.in"); - requirements_in.write_str("python-patch-override-patch-compatible-a==1.0.0")?; + requirements_in.write_str("a==1.0.0")?; - let output = uv_snapshot!(filters, command(&context, python_versions) + let output = uv_snapshot!(context.filters(), command(&context, python_versions, &server) .arg("--python-version=3.9.0") , @" success: true @@ -430,7 +394,7 @@ fn python_patch_override_patch_compatible() -> Result<()> { ----- stdout ----- # This file was autogenerated by uv via the following command: # uv pip compile requirements.in --cache-dir [CACHE_DIR] --python-version=3.9.0 - package-a==1.0.0 + a==1.0.0 # via -r requirements.in ----- stderr ----- @@ -439,9 +403,10 @@ fn python_patch_override_patch_compatible() -> Result<()> { " ); - output.assert().success().stdout(predicate::str::contains( - "python-patch-override-patch-compatible-a==1.0.0", - )); + output + .assert() + .success() + .stdout(predicate::str::contains("a==1.0.0")); Ok(()) } diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 11818a274f..56f4871805 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -27,9 +27,11 @@ use uv_fs::{PortablePath, Simplified}; use uv_static::EnvVars; #[cfg(feature = "test-git")] use uv_test::decode_token; +use uv_test::find_links::FindLinksServer; +use uv_test::packse::PackseServer; use uv_test::{ - DEFAULT_PYTHON_VERSION, TestContext, apply_filters, build_vendor_links_url, download_to_disk, - get_bin, packse_index_url, uv_snapshot, venv_bin_path, + DEFAULT_PYTHON_VERSION, TestContext, apply_filters, download_to_disk, get_bin, uv_snapshot, + venv_bin_path, }; fn write_tar_gz(file: File, entries: &[(&str, &str)]) -> Result<()> { @@ -4274,6 +4276,7 @@ fn install_git_source_respects_offline_mode() { #[test] fn build_prerelease_hint() -> Result<()> { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/transitive-package-only-prereleases-in-range.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str(indoc! {r#" @@ -4283,12 +4286,12 @@ fn build_prerelease_hint() -> Result<()> { requires-python = ">=3.12" [build-system] - requires = ["transitive-package-only-prereleases-in-range-a"] + requires = ["a"] build-backend = "setuptools.build_meta" "#})?; let mut command = context.pip_install(); - command.arg("--index-url").arg(packse_index_url()).arg("."); + command.arg("--index-url").arg(server.index_url()).arg("."); command.env_remove(EnvVars::UV_EXCLUDE_NEWER); uv_snapshot!( @@ -4303,11 +4306,11 @@ fn build_prerelease_hint() -> Result<()> { Resolved 1 package in [TIME] × Failed to build `project @ file://[TEMP_DIR]/` ├─▶ Failed to resolve requirements from `build-system.requires` - ├─▶ No solution found when resolving: `transitive-package-only-prereleases-in-range-a` - ╰─▶ Because only transitive-package-only-prereleases-in-range-b<=0.1 is available and transitive-package-only-prereleases-in-range-a==0.1.0 depends on transitive-package-only-prereleases-in-range-b>0.1, we can conclude that transitive-package-only-prereleases-in-range-a==0.1.0 cannot be used. - And because only transitive-package-only-prereleases-in-range-a==0.1.0 is available and you require transitive-package-only-prereleases-in-range-a, we can conclude that your requirements are unsatisfiable. + ├─▶ No solution found when resolving: `a` + ╰─▶ Because only b<=0.1 is available and a==0.1.0 depends on b>0.1, we can conclude that a==0.1.0 cannot be used. + And because only a==0.1.0 is available and you require a, we can conclude that your requirements are unsatisfiable. - hint: Only pre-releases of `transitive-package-only-prereleases-in-range-b` (e.g., 1.0.0a1) match these build requirements, and build environments can't enable pre-releases automatically. Add `transitive-package-only-prereleases-in-range-b>=1.0.0a1` to `build-system.requires`, `[tool.uv.extra-build-dependencies]`, or supply it via `uv build --build-constraint`. + hint: Only pre-releases of `b` (e.g., 1.0.0a1) match these build requirements, and build environments can't enable pre-releases automatically. Add `b>=1.0.0a1` to `build-system.requires`, `[tool.uv.extra-build-dependencies]`, or supply it via `uv build --build-constraint`. " ); @@ -6862,6 +6865,7 @@ fn already_installed_remote_dependencies() { #[test] fn already_installed_dependent_editable() { let context = uv_test::test_context!("3.12"); + let vendor = FindLinksServer::vendor(); let root_path = context .workspace_root .join("test/packages/dependent_locals"); @@ -6890,7 +6894,7 @@ fn already_installed_dependent_editable() { // Disable the index to guard this test against dependency confusion attacks .arg("--no-index") .arg("--find-links") - .arg(build_vendor_links_url()), @" + .arg(vendor.url()), @" success: true exit_code: 0 ----- stdout ----- @@ -6931,7 +6935,7 @@ fn already_installed_dependent_editable() { // Disable the index to guard this test against dependency confusion attacks .arg("--no-index") .arg("--find-links") - .arg(build_vendor_links_url()), @" + .arg(vendor.url()), @" success: false exit_code: 1 ----- stdout ----- @@ -6968,6 +6972,7 @@ fn already_installed_dependent_editable() { #[test] fn already_installed_local_path_dependent() { let context = uv_test::test_context!("3.12"); + let vendor = FindLinksServer::vendor(); let root_path = context .workspace_root .join("test/packages/dependent_locals"); @@ -6994,7 +6999,7 @@ fn already_installed_local_path_dependent() { // Disable the index to guard this test against dependency confusion attacks .arg("--no-index") .arg("--find-links") - .arg(build_vendor_links_url()), @" + .arg(vendor.url()), @" success: true exit_code: 0 ----- stdout ----- @@ -7050,7 +7055,7 @@ fn already_installed_local_path_dependent() { // Disable the index to guard this test against dependency confusion attacks .arg("--no-index") .arg("--find-links") - .arg(build_vendor_links_url()), @" + .arg(vendor.url()), @" success: false exit_code: 1 ----- stdout ----- @@ -7092,7 +7097,7 @@ fn already_installed_local_path_dependent() { // Disable the index to guard this test against dependency confusion attacks .arg("--no-index") .arg("--find-links") - .arg(build_vendor_links_url()), @" + .arg(vendor.url()), @" success: true exit_code: 0 ----- stdout ----- @@ -7117,7 +7122,7 @@ fn already_installed_local_path_dependent() { // Disable the index to guard this test against dependency confusion attacks .arg("--no-index") .arg("--find-links") - .arg(build_vendor_links_url()), @" + .arg(vendor.url()), @" success: true exit_code: 0 ----- stdout ----- @@ -15836,11 +15841,12 @@ fn abi_compatibility_on_nondebug_python_with_debug_wheel() { #[test] fn warn_on_bz2_wheel() { let context = uv_test::test_context!("3.14"); + let vendor = FindLinksServer::vendor(); uv_snapshot!( context.filters(), context.pip_install() - .arg("futzed_bz2 @ https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_bz2-0.1.0-py3-none-any.whl"), + .arg(format!("futzed_bz2 @ {}/futzed_bz2-0.1.0-py3-none-any.whl", vendor.url())), @" success: true exit_code: 0 @@ -15848,10 +15854,10 @@ fn warn_on_bz2_wheel() { ----- stderr ----- Resolved 1 package in [TIME] - warning: One or more file entries in 'https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_bz2-0.1.0-py3-none-any.whl' use the 'bzip2' compression method, which is not widely supported. A future version of uv will reject ZIP archives containing entries compressed with this method. Entries must be compressed with the 'stored', 'DEFLATE', or 'zstd' compression methods. + warning: One or more file entries in 'http://[LOCALHOST]/futzed_bz2-0.1.0-py3-none-any.whl' use the 'bzip2' compression method, which is not widely supported. A future version of uv will reject ZIP archives containing entries compressed with this method. Entries must be compressed with the 'stored', 'DEFLATE', or 'zstd' compression methods. Prepared 1 package in [TIME] Installed 1 package in [TIME] - + futzed-bz2==0.1.0 (from https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_bz2-0.1.0-py3-none-any.whl) + + futzed-bz2==0.1.0 (from http://[LOCALHOST]/futzed_bz2-0.1.0-py3-none-any.whl) " ); } @@ -15859,20 +15865,21 @@ fn warn_on_bz2_wheel() { #[test] fn warn_on_lzma_wheel() { let context = uv_test::test_context!("3.14"); + let vendor = FindLinksServer::vendor(); uv_snapshot!( context.filters(), context.pip_install() - .arg("futzed_lzma @ https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_lzma-0.1.0-py3-none-any.whl"), + .arg(format!("futzed_lzma @ {}/futzed_lzma-0.1.0-py3-none-any.whl", vendor.url())), @" success: false exit_code: 1 ----- stdout ----- ----- stderr ----- - × Failed to download `futzed-lzma @ https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_lzma-0.1.0-py3-none-any.whl` + × Failed to download `futzed-lzma @ http://[LOCALHOST]/futzed_lzma-0.1.0-py3-none-any.whl` ├─▶ Request failed after 3 retries in [TIME] - ├─▶ Failed to read metadata: `https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_lzma-0.1.0-py3-none-any.whl` + ├─▶ Failed to read metadata: `http://[LOCALHOST]/futzed_lzma-0.1.0-py3-none-any.whl` ├─▶ Failed to read from zip file ├─▶ an upstream reader returned an error: stream/file format not recognized ╰─▶ stream/file format not recognized diff --git a/crates/uv/tests/it/pip_install_scenarios.rs b/crates/uv/tests/it/pip_install_scenarios.rs index ff9bae1c5a..ff613abf6c 100644 --- a/crates/uv/tests/it/pip_install_scenarios.rs +++ b/crates/uv/tests/it/pip_install_scenarios.rs @@ -1,24 +1,20 @@ //! DO NOT EDIT //! -//! Generated with `./scripts/sync_scenarios.sh` -//! Scenarios from +//! Generated with `cargo dev generate-scenario-tests` +//! Scenarios from //! #![cfg(all(feature = "test-python", feature = "test-pypi", unix))] use std::process::Command; use uv_static::EnvVars; - -use uv_test::{TestContext, build_vendor_links_url, packse_index_url, uv_snapshot}; +use uv_test::packse::PackseServer; +use uv_test::{TestContext, uv_snapshot}; /// Create a `pip install` command with options shared across all scenarios. -fn command(context: &TestContext) -> Command { +fn command(context: &TestContext, server: &PackseServer) -> Command { let mut command = context.pip_install(); - command - .arg("--index-url") - .arg(packse_index_url()) - .arg("--find-links") - .arg(build_vendor_links_url()); + command.arg("--index-url").arg(server.index_url()); command.env_remove(EnvVars::UV_EXCLUDE_NEWER); command } @@ -33,17 +29,17 @@ fn command(context: &TestContext) -> Command { /// │ └── python3.12 /// ├── root /// │ ├── requires a -/// │ │ ├── satisfied by a-2.0.0 -/// │ │ └── satisfied by a-1.0.0 +/// │ │ ├── satisfied by a-1.0.0 +/// │ │ └── satisfied by a-2.0.0 /// │ └── requires b /// │ ├── satisfied by b-1.0.0 /// │ ├── satisfied by b-2.0.0 /// │ └── satisfied by b-3.0.0 /// ├── a -/// │ ├── a-2.0.0 -/// │ └── a-1.0.0 -/// │ └── requires c -/// │ └── unsatisfied: no versions for package +/// │ ├── a-1.0.0 +/// │ │ └── requires c +/// │ │ └── unsatisfied: no versions for package +/// │ └── a-2.0.0 /// └── b /// ├── b-1.0.0 /// │ └── requires a==1.0.0 @@ -58,14 +54,11 @@ fn command(context: &TestContext) -> Command { #[test] fn backtrack_to_missing_package() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("backtracking/backtrack-to-missing-package.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"backtrack-to-missing-package-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("backtrack-to-missing-package-a") - .arg("backtrack-to-missing-package-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: false exit_code: 1 @@ -73,12 +66,12 @@ fn backtrack_to_missing_package() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-c was not found in the package registry and package-a==1.0.0 depends on package-c, we can conclude that package-a==1.0.0 cannot be used. - And because all versions of package-b depend on package-a==1.0.0 and you require package-b, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because c was not found in the package registry and a==1.0.0 depends on c, we can conclude that a==1.0.0 cannot be used. + And because all versions of b depend on a==1.0.0 and you require b, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("backtrack_to_missing_package_a"); - context.assert_not_installed("backtrack_to_missing_package_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// There are two packages, `a` and `b`. The latest version of `b` requires @@ -111,14 +104,11 @@ fn backtrack_to_missing_package() { #[test] fn backtrack_with_missing_package() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("backtracking/backtrack-with-missing-package.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"backtrack-with-missing-package-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("backtrack-with-missing-package-a") - .arg("backtrack-with-missing-package-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: true exit_code: 0 @@ -128,12 +118,12 @@ fn backtrack_with_missing_package() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0 + + a==1.0.0 + + b==2.0.0 "); - context.assert_installed("backtrack_with_missing_package_a", "1.0.0"); - context.assert_installed("backtrack_with_missing_package_b", "2.0.0"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0"); } /// The user requires an exact version of package `a` but only other versions exist @@ -151,13 +141,10 @@ fn backtrack_with_missing_package() { #[test] fn requires_exact_version_does_not_exist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("does_not_exist/requires-exact-version-does-not-exist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-exact-version-does-not-exist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("requires-exact-version-does-not-exist-a==2.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==2.0.0") , @" success: false exit_code: 1 @@ -165,10 +152,10 @@ fn requires_exact_version_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-a==2.0.0 and you require package-a==2.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because there is no version of a==2.0.0 and you require a==2.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("requires_exact_version_does_not_exist_a"); + context.assert_not_installed("a"); } /// The user requires a version of `a` greater than `1.0.0` but only smaller or equal versions exist @@ -187,13 +174,10 @@ fn requires_exact_version_does_not_exist() { #[test] fn requires_greater_version_does_not_exist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("does_not_exist/requires-greater-version-does-not-exist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-greater-version-does-not-exist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("requires-greater-version-does-not-exist-a>1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.0.0") , @" success: false exit_code: 1 @@ -201,10 +185,10 @@ fn requires_greater_version_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a<=1.0.0 is available and you require package-a>1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a<=1.0.0 is available and you require a>1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("requires_greater_version_does_not_exist_a"); + context.assert_not_installed("a"); } /// The user requires a version of `a` less than `1.0.0` but only larger versions exist @@ -224,13 +208,10 @@ fn requires_greater_version_does_not_exist() { #[test] fn requires_less_version_does_not_exist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("does_not_exist/requires-less-version-does-not-exist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-less-version-does-not-exist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("requires-less-version-does-not-exist-a<2.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<2.0.0") , @" success: false exit_code: 1 @@ -238,10 +219,10 @@ fn requires_less_version_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a>=2.0.0 is available and you require package-a<2.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a>=2.0.0 is available and you require a<2.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("requires_less_version_does_not_exist_a"); + context.assert_not_installed("a"); } /// The user requires any version of package `a` which does not exist. @@ -257,13 +238,10 @@ fn requires_less_version_does_not_exist() { #[test] fn requires_package_does_not_exist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("does_not_exist/requires-package-does-not-exist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-package-does-not-exist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("requires-package-does-not-exist-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: false exit_code: 1 @@ -271,10 +249,10 @@ fn requires_package_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a was not found in the package registry and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because a was not found in the package registry and you require a, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("requires_package_does_not_exist_a"); + context.assert_not_installed("a"); } /// The user requires package `a` but `a` requires package `b` which does not exist @@ -294,13 +272,11 @@ fn requires_package_does_not_exist() { #[test] fn transitive_requires_package_does_not_exist() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("does_not_exist/transitive-requires-package-does-not-exist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-requires-package-does-not-exist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-requires-package-does-not-exist-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: false exit_code: 1 @@ -308,11 +284,11 @@ fn transitive_requires_package_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-b was not found in the package registry and package-a==1.0.0 depends on package-b, we can conclude that package-a==1.0.0 cannot be used. - And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because b was not found in the package registry and a==1.0.0 depends on b, we can conclude that a==1.0.0 cannot be used. + And because only a==1.0.0 is available and you require a, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("transitive_requires_package_does_not_exist_a"); + context.assert_not_installed("a"); } /// 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 `a` are incompatible for another reason e.g. dependency on non-existent package `d`. @@ -330,7 +306,7 @@ fn transitive_requires_package_does_not_exist() { /// │ │ ├── satisfied by a-2.3.0 /// │ │ ├── satisfied by a-2.4.0 /// │ │ └── satisfied by a-3.0.0 -/// │ ├── requires b<3.0.0,>=2.0.0 +/// │ ├── requires b>=2.0.0,<3.0.0 /// │ │ └── satisfied by b-2.0.0 /// │ └── requires c /// │ ├── satisfied by c-1.0.0 @@ -376,18 +352,14 @@ fn transitive_requires_package_does_not_exist() { #[test] fn dependency_excludes_non_contiguous_range_of_compatible_versions() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new( + "excluded/dependency-excludes-non-contiguous-range-of-compatible-versions.toml", + ); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"dependency-excludes-non-contiguous-range-of-compatible-versions-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .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") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b>=2.0.0,<3.0.0") + .arg("c") , @" success: false exit_code: 1 @@ -395,35 +367,32 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: - package-a==1.0.0 - package-a>=2.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. - And because only package-a<=3.0.0 is available, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) + ╰─▶ Because a==1.0.0 depends on b==1.0.0 and only the following versions of a are available: + a==1.0.0 + a>=2.0.0 + we can conclude that a<2.0.0 depends on b==1.0.0. + And because only a<=3.0.0 is available, we can conclude that a<2.0.0 depends on b==1.0.0. (1) - Because only the following versions of package-c are available: - package-c==1.0.0 - package-c==2.0.0 - and package-c==1.0.0 depends on package-a<2.0.0, we can conclude that package-c<2.0.0 depends on package-a<2.0.0. - And because package-c==2.0.0 depends on package-a>=3.0.0, we can conclude that all versions of package-c depend on one of: - package-a<2.0.0 - package-a>=3.0.0 + Because only the following versions of c are available: + c==1.0.0 + c==2.0.0 + and c==1.0.0 depends on a<2.0.0, we can conclude that c<2.0.0 depends on a<2.0.0. + And because c==2.0.0 depends on a>=3.0.0, we can conclude that all versions of c depend on one of: + a<2.0.0 + a>=3.0.0 - And because we know from (1) that package-a<2.0.0 depends on package-b==1.0.0, we can conclude that package-a!=3.0.0, package-b!=1.0.0, all versions of package-c are incompatible. - And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all versions of package-c depend on one of: - package-b<=1.0.0 - package-b>=3.0.0 + And because we know from (1) that a<2.0.0 depends on b==1.0.0, we can conclude that a!=3.0.0, b!=1.0.0, all versions of c are incompatible. + And because a==3.0.0 depends on b==3.0.0, we can conclude that all versions of c depend on one of: + b<=1.0.0 + b>=3.0.0 - And because you require package-b>=2.0.0,<3.0.0 and package-c, we can conclude that your requirements are unsatisfiable. + And because you require b>=2.0.0,<3.0.0 and c, we can conclude that your requirements are unsatisfiable. "); // 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. - context - .assert_not_installed("dependency_excludes_non_contiguous_range_of_compatible_versions_a"); - context - .assert_not_installed("dependency_excludes_non_contiguous_range_of_compatible_versions_b"); - context - .assert_not_installed("dependency_excludes_non_contiguous_range_of_compatible_versions_c"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); + context.assert_not_installed("c"); } /// There is a range of compatible versions for the requested package `a`, but another dependency `c` excludes that range. @@ -440,7 +409,7 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { /// │ │ ├── satisfied by a-2.2.0 /// │ │ ├── satisfied by a-2.3.0 /// │ │ └── satisfied by a-3.0.0 -/// │ ├── requires b<3.0.0,>=2.0.0 +/// │ ├── requires b>=2.0.0,<3.0.0 /// │ │ └── satisfied by b-2.0.0 /// │ └── requires c /// │ ├── satisfied by c-1.0.0 @@ -479,18 +448,13 @@ fn dependency_excludes_non_contiguous_range_of_compatible_versions() { #[test] fn dependency_excludes_range_of_compatible_versions() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("excluded/dependency-excludes-range-of-compatible-versions.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"dependency-excludes-range-of-compatible-versions-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .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") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b>=2.0.0,<3.0.0") + .arg("c") , @" success: false exit_code: 1 @@ -498,32 +462,32 @@ fn dependency_excludes_range_of_compatible_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available: - package-a==1.0.0 - package-a>=2.0.0 - we can conclude that package-a<2.0.0 depends on package-b==1.0.0. - And because only package-a<=3.0.0 is available, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. (1) + ╰─▶ Because a==1.0.0 depends on b==1.0.0 and only the following versions of a are available: + a==1.0.0 + a>=2.0.0 + we can conclude that a<2.0.0 depends on b==1.0.0. + And because only a<=3.0.0 is available, we can conclude that a<2.0.0 depends on b==1.0.0. (1) - Because only the following versions of package-c are available: - package-c==1.0.0 - package-c==2.0.0 - and package-c==1.0.0 depends on package-a<2.0.0, we can conclude that package-c<2.0.0 depends on package-a<2.0.0. - And because package-c==2.0.0 depends on package-a>=3.0.0, we can conclude that all versions of package-c depend on one of: - package-a<2.0.0 - package-a>=3.0.0 + Because only the following versions of c are available: + c==1.0.0 + c==2.0.0 + and c==1.0.0 depends on a<2.0.0, we can conclude that c<2.0.0 depends on a<2.0.0. + And because c==2.0.0 depends on a>=3.0.0, we can conclude that all versions of c depend on one of: + a<2.0.0 + a>=3.0.0 - And because we know from (1) that package-a<2.0.0 depends on package-b==1.0.0, we can conclude that package-a!=3.0.0, package-b!=1.0.0, all versions of package-c are incompatible. - And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all versions of package-c depend on one of: - package-b<=1.0.0 - package-b>=3.0.0 + And because we know from (1) that a<2.0.0 depends on b==1.0.0, we can conclude that a!=3.0.0, b!=1.0.0, all versions of c are incompatible. + And because a==3.0.0 depends on b==3.0.0, we can conclude that all versions of c depend on one of: + b<=1.0.0 + b>=3.0.0 - And because you require package-b>=2.0.0,<3.0.0 and package-c, we can conclude that your requirements are unsatisfiable. + And because you require b>=2.0.0,<3.0.0 and c, we can conclude that your requirements are unsatisfiable. "); // 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. - context.assert_not_installed("dependency_excludes_range_of_compatible_versions_a"); - context.assert_not_installed("dependency_excludes_range_of_compatible_versions_b"); - context.assert_not_installed("dependency_excludes_range_of_compatible_versions_c"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); + context.assert_not_installed("c"); } /// Only one version of the requested package `a` is compatible, but the user has banned that version. @@ -536,7 +500,7 @@ fn dependency_excludes_range_of_compatible_versions() { /// │ ├── requires a!=2.0.0 /// │ │ ├── satisfied by a-1.0.0 /// │ │ └── satisfied by a-3.0.0 -/// │ └── requires b<3.0.0,>=2.0.0 +/// │ └── requires b>=2.0.0,<3.0.0 /// │ └── satisfied by b-2.0.0 /// ├── a /// │ ├── a-1.0.0 @@ -556,14 +520,11 @@ fn dependency_excludes_range_of_compatible_versions() { #[test] fn excluded_only_compatible_version() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("excluded/excluded-only-compatible-version.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"excluded-only-compatible-version-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("excluded-only-compatible-version-a!=2.0.0") - .arg("excluded-only-compatible-version-b<3.0.0,>=2.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a!=2.0.0") + .arg("b>=2.0.0,<3.0.0") , @" success: false exit_code: 1 @@ -571,27 +532,27 @@ fn excluded_only_compatible_version() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only the following versions of package-a are available: - package-a==1.0.0 - package-a==2.0.0 - package-a==3.0.0 - and package-a==1.0.0 depends on package-b==1.0.0, we can conclude that package-a<2.0.0 depends on package-b==1.0.0. - And because package-a==3.0.0 depends on package-b==3.0.0, we can conclude that all of: - package-a<2.0.0 - package-a>2.0.0 + ╰─▶ Because only the following versions of a are available: + a==1.0.0 + a==2.0.0 + a==3.0.0 + and a==1.0.0 depends on b==1.0.0, we can conclude that a<2.0.0 depends on b==1.0.0. + And because a==3.0.0 depends on b==3.0.0, we can conclude that all of: + a<2.0.0 + a>2.0.0 depend on one of: - package-b==1.0.0 - package-b==3.0.0 + b==1.0.0 + b==3.0.0 And because you require one of: - package-a<2.0.0 - package-a>2.0.0 - and package-b>=2.0.0,<3.0.0, we can conclude that your requirements are unsatisfiable. + a<2.0.0 + a>2.0.0 + and b>=2.0.0,<3.0.0, we can conclude that your requirements are unsatisfiable. "); // 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. - context.assert_not_installed("excluded_only_compatible_version_a"); - context.assert_not_installed("excluded_only_compatible_version_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// Only one version of the requested package is available, but the user has banned that version. @@ -609,13 +570,10 @@ fn excluded_only_compatible_version() { #[test] fn excluded_only_version() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("excluded/excluded-only-version.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"excluded-only-version-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("excluded-only-version-a!=1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a!=1.0.0") , @" success: false exit_code: 1 @@ -623,14 +581,14 @@ fn excluded_only_version() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and you require one of: - package-a<1.0.0 - package-a>1.0.0 + ╰─▶ Because only a==1.0.0 is available and you require one of: + a<1.0.0 + a>1.0.0 we can conclude that your requirements are unsatisfiable. "); // Only `a==1.0.0` is available but the user excluded it. - context.assert_not_installed("excluded_only_version_a"); + context.assert_not_installed("a"); } /// Multiple optional dependencies are requested for the package via an 'all' extra. @@ -643,25 +601,25 @@ fn excluded_only_version() { /// │ └── requires a[all] /// │ ├── satisfied by a-1.0.0 /// │ ├── satisfied by a-1.0.0[all] -/// │ ├── satisfied by a-1.0.0[extra_b] -/// │ └── satisfied by a-1.0.0[extra_c] +/// │ ├── satisfied by a-1.0.0[extra-b] +/// │ └── satisfied by a-1.0.0[extra-c] /// ├── a /// │ ├── a-1.0.0 /// │ ├── a-1.0.0[all] -/// │ │ ├── requires a[extra_b] +/// │ │ ├── requires a[extra-b] /// │ │ │ ├── satisfied by a-1.0.0 /// │ │ │ ├── satisfied by a-1.0.0[all] -/// │ │ │ ├── satisfied by a-1.0.0[extra_b] -/// │ │ │ └── satisfied by a-1.0.0[extra_c] -/// │ │ └── requires a[extra_c] +/// │ │ │ ├── satisfied by a-1.0.0[extra-b] +/// │ │ │ └── satisfied by a-1.0.0[extra-c] +/// │ │ └── requires a[extra-c] /// │ │ ├── satisfied by a-1.0.0 /// │ │ ├── satisfied by a-1.0.0[all] -/// │ │ ├── satisfied by a-1.0.0[extra_b] -/// │ │ └── satisfied by a-1.0.0[extra_c] -/// │ ├── a-1.0.0[extra_b] +/// │ │ ├── satisfied by a-1.0.0[extra-b] +/// │ │ └── satisfied by a-1.0.0[extra-c] +/// │ ├── a-1.0.0[extra-b] /// │ │ └── requires b /// │ │ └── satisfied by b-1.0.0 -/// │ └── a-1.0.0[extra_c] +/// │ └── a-1.0.0[extra-c] /// │ └── requires c /// │ └── satisfied by c-1.0.0 /// ├── b @@ -672,13 +630,10 @@ fn excluded_only_version() { #[test] fn all_extras_required() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/all-extras-required.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"all-extras-required-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("all-extras-required-a[all]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[all]") , @" success: true exit_code: 0 @@ -688,14 +643,14 @@ fn all_extras_required() { Resolved 3 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] - + package-a==1.0.0 - + package-b==1.0.0 - + package-c==1.0.0 + + a==1.0.0 + + b==1.0.0 + + c==1.0.0 "); - context.assert_installed("all_extras_required_a", "1.0.0"); - context.assert_installed("all_extras_required_b", "1.0.0"); - context.assert_installed("all_extras_required_c", "1.0.0"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "1.0.0"); + context.assert_installed("c", "1.0.0"); } /// Optional dependencies are requested for the package, the extra is only available on an older version. @@ -706,30 +661,27 @@ fn all_extras_required() { /// │ └── python3.12 /// ├── root /// │ └── requires a[extra] -/// │ ├── satisfied by a-2.0.0 -/// │ ├── satisfied by a-3.0.0 /// │ ├── satisfied by a-1.0.0 -/// │ └── satisfied by a-1.0.0[extra] +/// │ ├── satisfied by a-1.0.0[extra] +/// │ ├── satisfied by a-2.0.0 +/// │ └── satisfied by a-3.0.0 /// ├── a -/// │ ├── a-2.0.0 -/// │ ├── a-3.0.0 /// │ ├── a-1.0.0 -/// │ └── a-1.0.0[extra] -/// │ └── requires b==1.0.0 -/// │ └── satisfied by b-1.0.0 +/// │ ├── a-1.0.0[extra] +/// │ │ └── requires b==1.0.0 +/// │ │ └── satisfied by b-1.0.0 +/// │ ├── a-2.0.0 +/// │ └── a-3.0.0 /// └── b /// └── b-1.0.0 /// ``` #[test] fn extra_does_not_exist_backtrack() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/extra-does-not-exist-backtrack.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"extra-does-not-exist-backtrack-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("extra-does-not-exist-backtrack-a[extra]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra]") , @" success: true exit_code: 0 @@ -739,12 +691,12 @@ fn extra_does_not_exist_backtrack() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==3.0.0 - warning: The package `package-a==3.0.0` does not have an extra named `extra` + + a==3.0.0 + warning: The package `a==3.0.0` does not have an extra named `extra` "); // The resolver should not backtrack to `a==1.0.0` because missing extras are allowed during resolution. `b` should not be installed. - context.assert_installed("extra_does_not_exist_backtrack_a", "3.0.0"); + context.assert_installed("a", "3.0.0"); } /// One of two incompatible optional dependencies are requested for the package. @@ -754,16 +706,16 @@ fn extra_does_not_exist_backtrack() { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a[extra_c] +/// │ └── requires a[extra-c] /// │ ├── satisfied by a-1.0.0 -/// │ ├── satisfied by a-1.0.0[extra_b] -/// │ └── satisfied by a-1.0.0[extra_c] +/// │ ├── satisfied by a-1.0.0[extra-b] +/// │ └── satisfied by a-1.0.0[extra-c] /// ├── a /// │ ├── a-1.0.0 -/// │ ├── a-1.0.0[extra_b] +/// │ ├── a-1.0.0[extra-b] /// │ │ └── requires b==1.0.0 /// │ │ └── satisfied by b-1.0.0 -/// │ └── a-1.0.0[extra_c] +/// │ └── a-1.0.0[extra-c] /// │ └── requires b==2.0.0 /// │ └── satisfied by b-2.0.0 /// └── b @@ -773,13 +725,10 @@ fn extra_does_not_exist_backtrack() { #[test] fn extra_incompatible_with_extra_not_requested() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/extra-incompatible-with-extra-not-requested.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"extra-incompatible-with-extra-not-requested-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("extra-incompatible-with-extra-not-requested-a[extra_c]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra-c]") , @" success: true exit_code: 0 @@ -789,13 +738,13 @@ fn extra_incompatible_with_extra_not_requested() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0 + + a==1.0.0 + + b==2.0.0 "); // Because the user does not request both extras, it is okay that one is incompatible with the other. - context.assert_installed("extra_incompatible_with_extra_not_requested_a", "1.0.0"); - context.assert_installed("extra_incompatible_with_extra_not_requested_b", "2.0.0"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0"); } /// Multiple optional dependencies are requested for the package, but they have conflicting requirements with each other. @@ -805,16 +754,16 @@ fn extra_incompatible_with_extra_not_requested() { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a[extra_b,extra_c] +/// │ └── requires a[extra-b,extra-c] /// │ ├── satisfied by a-1.0.0 -/// │ ├── satisfied by a-1.0.0[extra_b] -/// │ └── satisfied by a-1.0.0[extra_c] +/// │ ├── satisfied by a-1.0.0[extra-b] +/// │ └── satisfied by a-1.0.0[extra-c] /// ├── a /// │ ├── a-1.0.0 -/// │ ├── a-1.0.0[extra_b] +/// │ ├── a-1.0.0[extra-b] /// │ │ └── requires b==1.0.0 /// │ │ └── satisfied by b-1.0.0 -/// │ └── a-1.0.0[extra_c] +/// │ └── a-1.0.0[extra-c] /// │ └── requires b==2.0.0 /// │ └── satisfied by b-2.0.0 /// └── b @@ -824,13 +773,10 @@ fn extra_incompatible_with_extra_not_requested() { #[test] fn extra_incompatible_with_extra() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/extra-incompatible-with-extra.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"extra-incompatible-with-extra-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("extra-incompatible-with-extra-a[extra_b,extra_c]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra-b,extra-c]") , @" success: false exit_code: 1 @@ -838,13 +784,13 @@ fn extra_incompatible_with_extra() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a[extra-b]==1.0.0 is available and package-a[extra-b]==1.0.0 depends on package-b==1.0.0, we can conclude that all versions of package-a[extra-b] depend on package-b==1.0.0. - And because package-a[extra-c]==1.0.0 depends on package-b==2.0.0 and only package-a[extra-c]==1.0.0 is available, we can conclude that all versions of package-a[extra-b] and all versions of package-a[extra-c] are incompatible. - And because you require package-a[extra-b] and package-a[extra-c], we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a[extra-b]==1.0.0 is available and a[extra-b]==1.0.0 depends on b==1.0.0, we can conclude that all versions of a[extra-b] depend on b==1.0.0. + And because a[extra-c]==1.0.0 depends on b==2.0.0 and only a[extra-c]==1.0.0 is available, we can conclude that all versions of a[extra-b] and all versions of a[extra-c] are incompatible. + And because you require a[extra-b] and a[extra-c], we can conclude that your requirements are unsatisfiable. "); // Because both `extra_b` and `extra_c` are requested and they require incompatible versions of `b`, `a` cannot be installed. - context.assert_not_installed("extra_incompatible_with_extra_a"); + context.assert_not_installed("a"); } /// Optional dependencies are requested for the package, but the extra is not compatible with other requested versions. @@ -871,14 +817,11 @@ fn extra_incompatible_with_extra() { #[test] fn extra_incompatible_with_root() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/extra-incompatible-with-root.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"extra-incompatible-with-root-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("extra-incompatible-with-root-a[extra]") - .arg("extra-incompatible-with-root-b==2.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra]") + .arg("b==2.0.0") , @" success: false exit_code: 1 @@ -886,13 +829,13 @@ fn extra_incompatible_with_root() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a[extra]==1.0.0 is available and package-a[extra]==1.0.0 depends on package-b==1.0.0, we can conclude that all versions of package-a[extra] depend on package-b==1.0.0. - And because you require package-a[extra] and package-b==2.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a[extra]==1.0.0 is available and a[extra]==1.0.0 depends on b==1.0.0, we can conclude that all versions of a[extra] depend on b==1.0.0. + And because you require a[extra] and b==2.0.0, we can conclude that your requirements are unsatisfiable. "); // Because the user requested `b==2.0.0` but the requested extra requires `b==1.0.0`, the dependencies cannot be satisfied. - context.assert_not_installed("extra_incompatible_with_root_a"); - context.assert_not_installed("extra_incompatible_with_root_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// Optional dependencies are requested for the package. @@ -916,13 +859,10 @@ fn extra_incompatible_with_root() { #[test] fn extra_required() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/extra-required.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"extra-required-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("extra-required-a[extra]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra]") , @" success: true exit_code: 0 @@ -932,12 +872,12 @@ fn extra_required() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==1.0.0 + + a==1.0.0 + + b==1.0.0 "); - context.assert_installed("extra_required_a", "1.0.0"); - context.assert_installed("extra_required_b", "1.0.0"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "1.0.0"); } /// Optional dependencies are requested for the package, but the extra does not exist. @@ -955,13 +895,10 @@ fn extra_required() { #[test] fn missing_extra() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/missing-extra.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"missing-extra-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("missing-extra-a[extra]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra]") , @" success: true exit_code: 0 @@ -971,12 +908,12 @@ fn missing_extra() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 - warning: The package `package-a==1.0.0` does not have an extra named `extra` + + a==1.0.0 + warning: The package `a==1.0.0` does not have an extra named `extra` "); // Missing extras are ignored during resolution. - context.assert_installed("missing_extra_a", "1.0.0"); + context.assert_installed("a", "1.0.0"); } /// Multiple optional dependencies are requested for the package. @@ -986,16 +923,16 @@ fn missing_extra() { /// ├── environment /// │ └── python3.12 /// ├── root -/// │ └── requires a[extra_b,extra_c] +/// │ └── requires a[extra-b,extra-c] /// │ ├── satisfied by a-1.0.0 -/// │ ├── satisfied by a-1.0.0[extra_b] -/// │ └── satisfied by a-1.0.0[extra_c] +/// │ ├── satisfied by a-1.0.0[extra-b] +/// │ └── satisfied by a-1.0.0[extra-c] /// ├── a /// │ ├── a-1.0.0 -/// │ ├── a-1.0.0[extra_b] +/// │ ├── a-1.0.0[extra-b] /// │ │ └── requires b /// │ │ └── satisfied by b-1.0.0 -/// │ └── a-1.0.0[extra_c] +/// │ └── a-1.0.0[extra-c] /// │ └── requires c /// │ └── satisfied by c-1.0.0 /// ├── b @@ -1006,13 +943,10 @@ fn missing_extra() { #[test] fn multiple_extras_required() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("extras/multiple-extras-required.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"multiple-extras-required-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("multiple-extras-required-a[extra_b,extra_c]") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a[extra-b,extra-c]") , @" success: true exit_code: 0 @@ -1022,14 +956,14 @@ fn multiple_extras_required() { Resolved 3 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] - + package-a==1.0.0 - + package-b==1.0.0 - + package-c==1.0.0 + + a==1.0.0 + + b==1.0.0 + + c==1.0.0 "); - context.assert_installed("multiple_extras_required_a", "1.0.0"); - context.assert_installed("multiple_extras_required_b", "1.0.0"); - context.assert_installed("multiple_extras_required_c", "1.0.0"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "1.0.0"); + context.assert_installed("c", "1.0.0"); } /// The user requires two incompatible, existing versions of package `a` @@ -1050,14 +984,11 @@ fn multiple_extras_required() { #[test] fn direct_incompatible_versions() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("incompatible_versions/direct-incompatible-versions.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"direct-incompatible-versions-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("direct-incompatible-versions-a==1.0.0") - .arg("direct-incompatible-versions-a==2.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") + .arg("a==2.0.0") , @" success: false exit_code: 1 @@ -1065,11 +996,11 @@ fn direct_incompatible_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because you require package-a==1.0.0 and package-a==2.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because you require a==1.0.0 and a==2.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("direct_incompatible_versions_a"); - context.assert_not_installed("direct_incompatible_versions_a"); + context.assert_not_installed("a"); + context.assert_not_installed("a"); } /// The user requires `a`, which requires two incompatible, existing versions of package `b` @@ -1091,13 +1022,10 @@ fn direct_incompatible_versions() { #[test] fn transitive_incompatible_versions() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("incompatible_versions/transitive-incompatible-versions.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-incompatible-versions-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-incompatible-versions-a==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") , @" success: false exit_code: 1 @@ -1105,11 +1033,11 @@ fn transitive_incompatible_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used. - And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because a==1.0.0 depends on b==2.0.0 and b==1.0.0, we can conclude that a==1.0.0 cannot be used. + And because you require a==1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("transitive_incompatible_versions_a"); + context.assert_not_installed("a"); } /// The user requires packages `a` and `b` but `a` requires a different version of `b` @@ -1134,14 +1062,12 @@ fn transitive_incompatible_versions() { #[test] fn transitive_incompatible_with_root_version() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("incompatible_versions/transitive-incompatible-with-root-version.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-incompatible-with-root-version-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-incompatible-with-root-version-a") - .arg("transitive-incompatible-with-root-version-b==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==1.0.0") , @" success: false exit_code: 1 @@ -1149,12 +1075,12 @@ fn transitive_incompatible_with_root_version() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-b==2.0.0, we can conclude that all versions of package-a depend on package-b==2.0.0. - And because you require package-a and package-b==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 depends on b==2.0.0, we can conclude that all versions of a depend on b==2.0.0. + And because you require a and b==1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("transitive_incompatible_with_root_version_a"); - context.assert_not_installed("transitive_incompatible_with_root_version_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// The user requires package `a` and `b`; `a` and `b` require different versions of `c` @@ -1183,14 +1109,12 @@ fn transitive_incompatible_with_root_version() { #[test] fn transitive_incompatible_with_transitive() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("incompatible_versions/transitive-incompatible-with-transitive.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-incompatible-with-transitive-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-incompatible-with-transitive-a") - .arg("transitive-incompatible-with-transitive-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: false exit_code: 1 @@ -1198,13 +1122,13 @@ fn transitive_incompatible_with_transitive() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-c==1.0.0, we can conclude that all versions of package-a depend on package-c==1.0.0. - And because package-b==1.0.0 depends on package-c==2.0.0 and only package-b==1.0.0 is available, we can conclude that all versions of package-a and all versions of package-b are incompatible. - And because you require package-a and package-b, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 depends on c==1.0.0, we can conclude that all versions of a depend on c==1.0.0. + And because b==1.0.0 depends on c==2.0.0 and only b==1.0.0 is available, we can conclude that all versions of a and all versions of b are incompatible. + And because you require a and b, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("transitive_incompatible_with_transitive_a"); - context.assert_not_installed("transitive_incompatible_with_transitive_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// A local version should be included in inclusive ordered comparisons. @@ -1224,13 +1148,10 @@ fn transitive_incompatible_with_transitive() { #[test] fn local_greater_than_or_equal() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-greater-than-or-equal.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-greater-than-or-equal-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-greater-than-or-equal-a>=1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=1.2.3") , @" success: true exit_code: 0 @@ -1240,11 +1161,11 @@ fn local_greater_than_or_equal() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3+foo + + a==1.2.3+foo "); // The version '1.2.3+foo' satisfies the constraint '>=1.2.3'. - context.assert_installed("local_greater_than_or_equal_a", "1.2.3+foo"); + context.assert_installed("a", "1.2.3+foo"); } /// A local version should be excluded in exclusive ordered comparisons. @@ -1262,13 +1183,10 @@ fn local_greater_than_or_equal() { #[test] fn local_greater_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-greater-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-greater-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-greater-than-a>1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.2.3") , @" success: false exit_code: 1 @@ -1276,10 +1194,10 @@ fn local_greater_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3+foo is available and you require package-a>1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.2.3+foo is available and you require a>1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("local_greater_than_a"); + context.assert_not_installed("a"); } /// A local version should be included in inclusive ordered comparisons. @@ -1299,13 +1217,10 @@ fn local_greater_than() { #[test] fn local_less_than_or_equal() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-less-than-or-equal.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-less-than-or-equal-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-less-than-or-equal-a<=1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<=1.2.3") , @" success: true exit_code: 0 @@ -1315,11 +1230,11 @@ fn local_less_than_or_equal() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3+foo + + a==1.2.3+foo "); // The version '1.2.3+foo' satisfies the constraint '<=1.2.3'. - context.assert_installed("local_less_than_or_equal_a", "1.2.3+foo"); + context.assert_installed("a", "1.2.3+foo"); } /// A local version should be excluded in exclusive ordered comparisons. @@ -1337,13 +1252,10 @@ fn local_less_than_or_equal() { #[test] fn local_less_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-less-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-less-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-less-than-a<1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<1.2.3") , @" success: false exit_code: 1 @@ -1351,10 +1263,10 @@ fn local_less_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3+foo is available and you require package-a<1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.2.3+foo is available and you require a<1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("local_less_than_a"); + context.assert_not_installed("a"); } /// Tests that we can select an older version with a local segment when newer versions are incompatible. @@ -1365,24 +1277,21 @@ fn local_less_than() { /// │ └── python3.12 /// ├── root /// │ └── requires a>=1 -/// │ ├── satisfied by a-1.2.3 +/// │ ├── satisfied by a-1.2.1+foo /// │ ├── satisfied by a-1.2.2+foo -/// │ └── satisfied by a-1.2.1+foo +/// │ └── satisfied by a-1.2.3 /// └── a -/// ├── a-1.2.3 +/// ├── a-1.2.1+foo /// ├── a-1.2.2+foo -/// └── a-1.2.1+foo +/// └── a-1.2.3 /// ``` #[test] fn local_not_latest() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-not-latest.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-not-latest-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-not-latest-a>=1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=1") , @" success: true exit_code: 0 @@ -1392,10 +1301,10 @@ fn local_not_latest() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.1+foo + + a==1.2.1+foo "); - context.assert_installed("local_not_latest_a", "1.2.1+foo"); + context.assert_installed("a", "1.2.1+foo"); } /// If there is a 1.2.3 version with an sdist published and no compatible wheels, then the sdist will be used. @@ -1415,13 +1324,10 @@ fn local_not_latest() { #[test] fn local_not_used_with_sdist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-not-used-with-sdist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-not-used-with-sdist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-not-used-with-sdist-a==1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.2.3") , @" success: true exit_code: 0 @@ -1431,11 +1337,11 @@ fn local_not_used_with_sdist() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3+foo + + a==1.2.3+foo "); // The version '1.2.3' with an sdist satisfies the constraint '==1.2.3'. - context.assert_installed("local_not_used_with_sdist_a", "1.2.3+foo"); + context.assert_installed("a", "1.2.3+foo"); } /// A simple version constraint should not exclude published versions with local segments. @@ -1455,13 +1361,10 @@ fn local_not_used_with_sdist() { #[test] fn local_simple() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-simple.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-simple-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-simple-a==1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.2.3") , @" success: true exit_code: 0 @@ -1471,11 +1374,11 @@ fn local_simple() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3+foo + + a==1.2.3+foo "); // The version '1.2.3+foo' satisfies the constraint '==1.2.3'. - context.assert_installed("local_simple_a", "1.2.3+foo"); + context.assert_installed("a", "1.2.3+foo"); } /// A dependency depends on a conflicting local version of a direct dependency, but we can backtrack to a compatible version. @@ -1505,14 +1408,11 @@ fn local_simple() { #[test] fn local_transitive_backtrack() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-backtrack.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-backtrack-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-backtrack-a") - .arg("local-transitive-backtrack-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: true exit_code: 0 @@ -1522,13 +1422,13 @@ fn local_transitive_backtrack() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0+foo + + a==1.0.0 + + b==2.0.0+foo "); // Backtracking to '1.0.0' gives us compatible local versions of b. - context.assert_installed("local_transitive_backtrack_a", "1.0.0"); - context.assert_installed("local_transitive_backtrack_b", "2.0.0+foo"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0+foo"); } /// A dependency depends on a conflicting local version of a direct dependency. @@ -1553,14 +1453,11 @@ fn local_transitive_backtrack() { #[test] fn local_transitive_conflicting() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-conflicting.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-conflicting-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-conflicting-a") - .arg("local-transitive-conflicting-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: false exit_code: 1 @@ -1568,12 +1465,12 @@ fn local_transitive_conflicting() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-b==2.0.0+bar, we can conclude that all versions of package-a depend on package-b==2.0.0+bar. - And because you require package-a and package-b==2.0.0+foo, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 depends on b==2.0.0+bar, we can conclude that all versions of a depend on b==2.0.0+bar. + And because you require a and b==2.0.0+foo, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("local_transitive_conflicting_a"); - context.assert_not_installed("local_transitive_conflicting_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// A transitive dependency has both a non-local and local version published, but the non-local version is unusable. @@ -1599,13 +1496,10 @@ fn local_transitive_conflicting() { #[test] fn local_transitive_confounding() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-confounding.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-confounding-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-confounding-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -1615,13 +1509,13 @@ fn local_transitive_confounding() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0+foo + + a==1.0.0 + + b==2.0.0+foo "); // The version '2.0.0+foo' satisfies the constraint '==2.0.0'. - context.assert_installed("local_transitive_confounding_a", "1.0.0"); - context.assert_installed("local_transitive_confounding_b", "2.0.0+foo"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0+foo"); } /// A transitive constraint on a local version should match an inclusive ordered operator. @@ -1647,14 +1541,11 @@ fn local_transitive_confounding() { #[test] fn local_transitive_greater_than_or_equal() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-greater-than-or-equal.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-greater-than-or-equal-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-greater-than-or-equal-a") - .arg("local-transitive-greater-than-or-equal-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: true exit_code: 0 @@ -1664,13 +1555,13 @@ fn local_transitive_greater_than_or_equal() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0+foo + + a==1.0.0 + + b==2.0.0+foo "); // The version '2.0.0+foo' satisfies both >=2.0.0 and ==2.0.0+foo. - context.assert_installed("local_transitive_greater_than_or_equal_a", "1.0.0"); - context.assert_installed("local_transitive_greater_than_or_equal_b", "2.0.0+foo"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0+foo"); } /// A transitive constraint on a local version should not match an exclusive ordered operator. @@ -1695,14 +1586,11 @@ fn local_transitive_greater_than_or_equal() { #[test] fn local_transitive_greater_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-greater-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-greater-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-greater-than-a") - .arg("local-transitive-greater-than-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: false exit_code: 1 @@ -1710,12 +1598,12 @@ fn local_transitive_greater_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-b>2.0.0, we can conclude that all versions of package-a depend on package-b>2.0.0. - And because you require package-a and package-b==2.0.0+foo, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 depends on b>2.0.0, we can conclude that all versions of a depend on b>2.0.0. + And because you require a and b==2.0.0+foo, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("local_transitive_greater_than_a"); - context.assert_not_installed("local_transitive_greater_than_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// A transitive constraint on a local version should match an inclusive ordered operator. @@ -1741,14 +1629,11 @@ fn local_transitive_greater_than() { #[test] fn local_transitive_less_than_or_equal() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-less-than-or-equal.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-less-than-or-equal-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-less-than-or-equal-a") - .arg("local-transitive-less-than-or-equal-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: true exit_code: 0 @@ -1758,13 +1643,13 @@ fn local_transitive_less_than_or_equal() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0+foo + + a==1.0.0 + + b==2.0.0+foo "); // The version '2.0.0+foo' satisfies both <=2.0.0 and ==2.0.0+foo. - context.assert_installed("local_transitive_less_than_or_equal_a", "1.0.0"); - context.assert_installed("local_transitive_less_than_or_equal_b", "2.0.0+foo"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0+foo"); } /// A transitive constraint on a local version should not match an exclusive ordered operator. @@ -1789,14 +1674,11 @@ fn local_transitive_less_than_or_equal() { #[test] fn local_transitive_less_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive-less-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-less-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-less-than-a") - .arg("local-transitive-less-than-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: false exit_code: 1 @@ -1804,12 +1686,12 @@ fn local_transitive_less_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-b<2.0.0, we can conclude that all versions of package-a depend on package-b<2.0.0. - And because you require package-a and package-b==2.0.0+foo, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 depends on b<2.0.0, we can conclude that all versions of a depend on b<2.0.0. + And because you require a and b==2.0.0+foo, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("local_transitive_less_than_a"); - context.assert_not_installed("local_transitive_less_than_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// A simple version constraint should not exclude published versions with local segments. @@ -1826,23 +1708,20 @@ fn local_transitive_less_than() { /// ├── a /// │ └── a-1.0.0 /// │ └── requires b==2.0.0 -/// │ ├── satisfied by b-2.0.0+foo -/// │ └── satisfied by b-2.0.0+bar +/// │ ├── satisfied by b-2.0.0+bar +/// │ └── satisfied by b-2.0.0+foo /// └── b -/// ├── b-2.0.0+foo -/// └── b-2.0.0+bar +/// ├── b-2.0.0+bar +/// └── b-2.0.0+foo /// ``` #[test] fn local_transitive() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-transitive.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-transitive-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-transitive-a") - .arg("local-transitive-b==2.0.0+foo") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==2.0.0+foo") , @" success: true exit_code: 0 @@ -1852,13 +1731,13 @@ fn local_transitive() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==1.0.0 - + package-b==2.0.0+foo + + a==1.0.0 + + b==2.0.0+foo "); // The version '2.0.0+foo' satisfies both ==2.0.0 and ==2.0.0+foo. - context.assert_installed("local_transitive_a", "1.0.0"); - context.assert_installed("local_transitive_b", "2.0.0+foo"); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "2.0.0+foo"); } /// 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. @@ -1878,13 +1757,10 @@ fn local_transitive() { #[test] fn local_used_without_sdist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("local/local-used-without-sdist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"local-used-without-sdist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("local-used-without-sdist-a==1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.2.3") , @" success: true exit_code: 0 @@ -1894,11 +1770,11 @@ fn local_used_without_sdist() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3+foo + + a==1.2.3+foo "); // The version '1.2.3+foo' satisfies the constraint '==1.2.3'. - context.assert_installed("local_used_without_sdist_a", "1.2.3+foo"); + context.assert_installed("a", "1.2.3+foo"); } /// An equal version constraint should match a post-release version if the post-release version is available. @@ -1917,13 +1793,10 @@ fn local_used_without_sdist() { #[test] fn post_equal_available() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-equal-available.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-equal-available-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-equal-available-a==1.2.3.post0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.2.3.post0") , @" success: true exit_code: 0 @@ -1933,11 +1806,11 @@ fn post_equal_available() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3.post0 + + a==1.2.3.post0 "); // The version '1.2.3.post0' satisfies the constraint '==1.2.3.post0'. - context.assert_installed("post_equal_available_a", "1.2.3.post0"); + context.assert_installed("a", "1.2.3.post0"); } /// An equal version constraint should not match a post-release version if the post-release version is not available. @@ -1956,13 +1829,10 @@ fn post_equal_available() { #[test] fn post_equal_not_available() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-equal-not-available.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-equal-not-available-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-equal-not-available-a==1.2.3.post0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.2.3.post0") , @" success: false exit_code: 1 @@ -1970,10 +1840,10 @@ fn post_equal_not_available() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-a==1.2.3.post0 and you require package-a==1.2.3.post0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because there is no version of a==1.2.3.post0 and you require a==1.2.3.post0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_equal_not_available_a"); + context.assert_not_installed("a"); } /// A greater-than-or-equal version constraint should match a post-release version if the constraint is itself a post-release version. @@ -1993,13 +1863,10 @@ fn post_equal_not_available() { #[test] fn post_greater_than_or_equal_post() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-greater-than-or-equal-post.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-greater-than-or-equal-post-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-greater-than-or-equal-post-a>=1.2.3.post0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=1.2.3.post0") , @" success: true exit_code: 0 @@ -2009,11 +1876,11 @@ fn post_greater_than_or_equal_post() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3.post1 + + a==1.2.3.post1 "); // The version '1.2.3.post1' satisfies the constraint '>=1.2.3.post0'. - context.assert_installed("post_greater_than_or_equal_post_a", "1.2.3.post1"); + context.assert_installed("a", "1.2.3.post1"); } /// A greater-than-or-equal version constraint should match a post-release version. @@ -2031,13 +1898,10 @@ fn post_greater_than_or_equal_post() { #[test] fn post_greater_than_or_equal() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-greater-than-or-equal.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-greater-than-or-equal-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-greater-than-or-equal-a>=1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=1.2.3") , @" success: true exit_code: 0 @@ -2047,11 +1911,11 @@ fn post_greater_than_or_equal() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3.post1 + + a==1.2.3.post1 "); // The version '1.2.3.post1' satisfies the constraint '>=1.2.3'. - context.assert_installed("post_greater_than_or_equal_a", "1.2.3.post1"); + context.assert_installed("a", "1.2.3.post1"); } /// A greater-than version constraint should not match a post-release version if the post-release version is not available. @@ -2071,13 +1935,10 @@ fn post_greater_than_or_equal() { #[test] fn post_greater_than_post_not_available() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-greater-than-post-not-available.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-greater-than-post-not-available-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-greater-than-post-not-available-a>1.2.3.post2") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.2.3.post2") , @" success: false exit_code: 1 @@ -2085,10 +1946,10 @@ fn post_greater_than_post_not_available() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a<=1.2.3.post1 is available and you require package-a>=1.2.3.post3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a<=1.2.3.post1 is available and you require a>=1.2.3.post3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_greater_than_post_not_available_a"); + context.assert_not_installed("a"); } /// A greater-than version constraint should match a post-release version if the constraint is itself a post-release version. @@ -2107,13 +1968,10 @@ fn post_greater_than_post_not_available() { #[test] fn post_greater_than_post() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-greater-than-post.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-greater-than-post-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-greater-than-post-a>1.2.3.post0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.2.3.post0") , @" success: true exit_code: 0 @@ -2123,11 +1981,11 @@ fn post_greater_than_post() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.2.3.post1 + + a==1.2.3.post1 "); // The version '1.2.3.post1' satisfies the constraint '>1.2.3.post0'. - context.assert_installed("post_greater_than_post_a", "1.2.3.post1"); + context.assert_installed("a", "1.2.3.post1"); } /// A greater-than version constraint should not match a post-release version. @@ -2145,13 +2003,10 @@ fn post_greater_than_post() { #[test] fn post_greater_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-greater-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-greater-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-greater-than-a>1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.2.3") , @" success: false exit_code: 1 @@ -2159,10 +2014,10 @@ fn post_greater_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3.post1 is available and you require package-a>1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.2.3.post1 is available and you require a>1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_greater_than_a"); + context.assert_not_installed("a"); } /// A less-than-or-equal version constraint should not match a post-release version. @@ -2180,13 +2035,10 @@ fn post_greater_than() { #[test] fn post_less_than_or_equal() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-less-than-or-equal.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-less-than-or-equal-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-less-than-or-equal-a<=1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<=1.2.3") , @" success: false exit_code: 1 @@ -2194,10 +2046,10 @@ fn post_less_than_or_equal() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3.post1 is available and you require package-a<=1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.2.3.post1 is available and you require a<=1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_less_than_or_equal_a"); + context.assert_not_installed("a"); } /// A less-than version constraint should not match a post-release version. @@ -2215,13 +2067,10 @@ fn post_less_than_or_equal() { #[test] fn post_less_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-less-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-less-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-less-than-a<1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<1.2.3") , @" success: false exit_code: 1 @@ -2229,10 +2078,10 @@ fn post_less_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.2.3.post1 is available and you require package-a<1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.2.3.post1 is available and you require a<1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_less_than_a"); + context.assert_not_installed("a"); } /// A greater-than version constraint should not match a post-release version with a local version identifier. @@ -2251,13 +2100,10 @@ fn post_less_than() { #[test] fn post_local_greater_than_post() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-local-greater-than-post.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-local-greater-than-post-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-local-greater-than-post-a>1.2.3.post1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.2.3.post1") , @" success: false exit_code: 1 @@ -2265,10 +2111,10 @@ fn post_local_greater_than_post() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a<=1.2.3.post1+local is available and you require package-a>=1.2.3.post2, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a<=1.2.3.post1+local is available and you require a>=1.2.3.post2, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_local_greater_than_post_a"); + context.assert_not_installed("a"); } /// A greater-than version constraint should not match a post-release version with a local version identifier. @@ -2287,13 +2133,10 @@ fn post_local_greater_than_post() { #[test] fn post_local_greater_than() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-local-greater-than.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-local-greater-than-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-local-greater-than-a>1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>1.2.3") , @" success: false exit_code: 1 @@ -2301,10 +2144,10 @@ fn post_local_greater_than() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a<=1.2.3.post1+local is available and you require package-a>1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a<=1.2.3.post1+local is available and you require a>1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_local_greater_than_a"); + context.assert_not_installed("a"); } /// A simple version constraint should not match a post-release version. @@ -2322,13 +2165,10 @@ fn post_local_greater_than() { #[test] fn post_simple() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("post/post-simple.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"post-simple-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("post-simple-a==1.2.3") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.2.3") , @" success: false exit_code: 1 @@ -2336,10 +2176,10 @@ fn post_simple() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-a==1.2.3 and you require package-a==1.2.3, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because there is no version of a==1.2.3 and you require a==1.2.3, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("post_simple_a"); + context.assert_not_installed("a"); } /// The user requires `a` which has multiple prereleases available with different labels. @@ -2361,13 +2201,10 @@ fn post_simple() { #[test] fn package_multiple_prereleases_kinds() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-multiple-prereleases-kinds.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-multiple-prereleases-kinds-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-multiple-prereleases-kinds-a>=1.0.0a1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=1.0.0a1") , @" success: true exit_code: 0 @@ -2377,11 +2214,11 @@ fn package_multiple_prereleases_kinds() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0rc1 + + a==1.0.0rc1 "); // Release candidates should be the highest precedence prerelease kind. - context.assert_installed("package_multiple_prereleases_kinds_a", "1.0.0rc1"); + context.assert_installed("a", "1.0.0rc1"); } /// The user requires `a` which has multiple alphas available. @@ -2403,13 +2240,10 @@ fn package_multiple_prereleases_kinds() { #[test] fn package_multiple_prereleases_numbers() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-multiple-prereleases-numbers.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-multiple-prereleases-numbers-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-multiple-prereleases-numbers-a>=1.0.0a1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=1.0.0a1") , @" success: true exit_code: 0 @@ -2419,11 +2253,11 @@ fn package_multiple_prereleases_numbers() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0a3 + + a==1.0.0a3 "); // The latest alpha version should be selected. - context.assert_installed("package_multiple_prereleases_numbers_a", "1.0.0a3"); + context.assert_installed("a", "1.0.0a3"); } /// 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. @@ -2434,7 +2268,7 @@ fn package_multiple_prereleases_numbers() { /// │ └── python3.12 /// ├── root /// │ └── requires a<0.2.0 -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by a-0.1.0a1 /// └── a /// ├── a-0.1.0a1 /// ├── a-0.2.0a1 @@ -2443,13 +2277,10 @@ fn package_multiple_prereleases_numbers() { #[test] fn package_only_prereleases_boundary() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-only-prereleases-boundary.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-only-prereleases-boundary-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-only-prereleases-boundary-a<0.2.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<0.2.0") , @" success: true exit_code: 0 @@ -2459,11 +2290,11 @@ fn package_only_prereleases_boundary() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.1.0a1 + + a==0.1.0a1 "); // Since there are only prerelease versions of `a` available, a prerelease is allowed. Since the user did not explicitly request a pre-release, pre-releases at the boundary should not be selected. - context.assert_installed("package_only_prereleases_boundary_a", "0.1.0a1"); + context.assert_installed("a", "0.1.0a1"); } /// The user requires a version of package `a` which only matches prerelease versions but they did not include a prerelease specifier. @@ -2474,7 +2305,7 @@ fn package_only_prereleases_boundary() { /// │ └── python3.12 /// ├── root /// │ └── requires a>0.1.0 -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by a-1.0.0a1 /// └── a /// ├── a-0.1.0 /// └── a-1.0.0a1 @@ -2482,13 +2313,10 @@ fn package_only_prereleases_boundary() { #[test] fn package_only_prereleases_in_range() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-only-prereleases-in-range.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-only-prereleases-in-range-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-only-prereleases-in-range-a>0.1.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>0.1.0") , @" success: false exit_code: 1 @@ -2496,13 +2324,13 @@ fn package_only_prereleases_in_range() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a<=0.1.0 is available and you require package-a>0.1.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a<=0.1.0 is available and you require a>0.1.0, we can conclude that your requirements are unsatisfiable. - hint: Pre-releases are available for `package-a` in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`) + hint: Pre-releases are available for `a` in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`) "); // Since there are stable versions of `a` available, prerelease versions should not be selected without explicit opt-in. - context.assert_not_installed("package_only_prereleases_in_range_a"); + context.assert_not_installed("a"); } /// The user requires any version of package `a` which only has prerelease versions available. @@ -2513,20 +2341,17 @@ fn package_only_prereleases_in_range() { /// │ └── python3.12 /// ├── root /// │ └── requires a -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by a-1.0.0a1 /// └── a /// └── a-1.0.0a1 /// ``` #[test] fn package_only_prereleases() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-only-prereleases.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-only-prereleases-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-only-prereleases-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -2536,11 +2361,11 @@ fn package_only_prereleases() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0a1 + + a==1.0.0a1 "); // Since there are only prerelease versions of `a` available, it should be installed even though the user did not include a prerelease specifier. - context.assert_installed("package_only_prereleases_a", "1.0.0a1"); + context.assert_installed("a", "1.0.0a1"); } /// The user requires a version of `a` with a prerelease specifier and both prerelease and stable releases are available. @@ -2564,13 +2389,10 @@ fn package_only_prereleases() { #[test] fn package_prerelease_specified_mixed_available() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-prerelease-specified-mixed-available.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-prerelease-specified-mixed-available-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-prerelease-specified-mixed-available-a>=0.1.0a1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=0.1.0a1") , @" success: true exit_code: 0 @@ -2580,11 +2402,11 @@ fn package_prerelease_specified_mixed_available() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0a1 + + a==1.0.0a1 "); // Since the user provided a prerelease specifier, the latest prerelease version should be selected. - context.assert_installed("package_prerelease_specified_mixed_available_a", "1.0.0a1"); + context.assert_installed("a", "1.0.0a1"); } /// The user requires a version of `a` with a prerelease specifier and only stable releases are available. @@ -2606,16 +2428,11 @@ fn package_prerelease_specified_mixed_available() { #[test] fn package_prerelease_specified_only_final_available() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("prereleases/package-prerelease-specified-only-final-available.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"package-prerelease-specified-only-final-available-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .arg("package-prerelease-specified-only-final-available-a>=0.1.0a1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=0.1.0a1") , @" success: true exit_code: 0 @@ -2625,14 +2442,11 @@ fn package_prerelease_specified_only_final_available() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.3.0 + + a==0.3.0 "); // The latest stable version should be selected. - context.assert_installed( - "package_prerelease_specified_only_final_available_a", - "0.3.0", - ); + context.assert_installed("a", "0.3.0"); } /// The user requires a version of `a` with a prerelease specifier and only prerelease releases are available. @@ -2654,16 +2468,12 @@ fn package_prerelease_specified_only_final_available() { #[test] fn package_prerelease_specified_only_prerelease_available() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new( + "prereleases/package-prerelease-specified-only-prerelease-available.toml", + ); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"package-prerelease-specified-only-prerelease-available-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .arg("package-prerelease-specified-only-prerelease-available-a>=0.1.0a1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=0.1.0a1") , @" success: true exit_code: 0 @@ -2673,14 +2483,11 @@ fn package_prerelease_specified_only_prerelease_available() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.3.0a1 + + a==0.3.0a1 "); // The latest prerelease version should be selected. - context.assert_installed( - "package_prerelease_specified_only_prerelease_available_a", - "0.3.0a1", - ); + context.assert_installed("a", "0.3.0a1"); } /// The user requires a non-prerelease version of `a` but has enabled pre-releases. There are pre-releases on the boundary of their range. @@ -2700,14 +2507,11 @@ fn package_prerelease_specified_only_prerelease_available() { #[test] fn package_prereleases_boundary() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-prereleases-boundary.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-prereleases-boundary-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--prerelease=allow") - .arg("package-prereleases-boundary-a<0.2.0") + .arg("a<0.2.0") , @" success: true exit_code: 0 @@ -2717,11 +2521,11 @@ fn package_prereleases_boundary() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.1.0 + + a==0.1.0 "); // 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. - context.assert_installed("package_prereleases_boundary_a", "0.1.0"); + context.assert_installed("a", "0.1.0"); } /// The user requires a non-prerelease version of `a` but has enabled pre-releases. There are pre-releases on the boundary of their range. @@ -2741,14 +2545,11 @@ fn package_prereleases_boundary() { #[test] fn package_prereleases_global_boundary() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-prereleases-global-boundary.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-prereleases-global-boundary-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--prerelease=allow") - .arg("package-prereleases-global-boundary-a<0.2.0") + .arg("a<0.2.0") , @" success: true exit_code: 0 @@ -2758,11 +2559,11 @@ fn package_prereleases_global_boundary() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.1.0 + + a==0.1.0 "); // 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. - context.assert_installed("package_prereleases_global_boundary_a", "0.1.0"); + context.assert_installed("a", "0.1.0"); } /// The user requires a prerelease version of `a`. There are pre-releases on the boundary of their range. @@ -2777,22 +2578,19 @@ fn package_prereleases_global_boundary() { /// │ └── satisfied by a-0.2.0a1 /// └── a /// ├── a-0.1.0 -/// ├── a-0.2.0 /// ├── a-0.2.0a1 /// ├── a-0.2.0a2 /// ├── a-0.2.0a3 +/// ├── a-0.2.0 /// └── a-0.3.0 /// ``` #[test] fn package_prereleases_specifier_boundary() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/package-prereleases-specifier-boundary.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-prereleases-specifier-boundary-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-prereleases-specifier-boundary-a<0.2.0a2") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a<0.2.0a2") , @" success: true exit_code: 0 @@ -2802,11 +2600,11 @@ fn package_prereleases_specifier_boundary() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.2.0a1 + + a==0.2.0a1 "); // Since the user used a pre-release specifier, pre-releases at the boundary should be selected. - context.assert_installed("package_prereleases_specifier_boundary_a", "0.2.0a1"); + context.assert_installed("a", "0.2.0a1"); } /// 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. @@ -2817,7 +2615,7 @@ fn package_prereleases_specifier_boundary() { /// │ └── python3.12 /// ├── root /// │ └── requires a>0.1.0 -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by a-1.0.0a1 /// └── a /// ├── a-0.1.0 /// └── a-1.0.0a1 @@ -2825,17 +2623,13 @@ fn package_prereleases_specifier_boundary() { #[test] fn requires_package_only_prereleases_in_range_global_opt_in() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new( + "prereleases/requires-package-only-prereleases-in-range-global-opt-in.toml", + ); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"requires-package-only-prereleases-in-range-global-opt-in-", - "package-", - )); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--prerelease=allow") - .arg("requires-package-only-prereleases-in-range-global-opt-in-a>0.1.0") + .arg("a>0.1.0") , @" success: true exit_code: 0 @@ -2845,13 +2639,10 @@ fn requires_package_only_prereleases_in_range_global_opt_in() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0a1 + + a==1.0.0a1 "); - context.assert_installed( - "requires_package_only_prereleases_in_range_global_opt_in_a", - "1.0.0a1", - ); + context.assert_installed("a", "1.0.0a1"); } /// The user requires any version of package `a` has a prerelease version available and an older non-prerelease version. @@ -2862,7 +2653,8 @@ fn requires_package_only_prereleases_in_range_global_opt_in() { /// │ └── python3.12 /// ├── root /// │ └── requires a -/// │ └── satisfied by a-0.1.0 +/// │ ├── satisfied by a-0.1.0 +/// │ └── satisfied by a-1.0.0a1 /// └── a /// ├── a-0.1.0 /// └── a-1.0.0a1 @@ -2870,13 +2662,10 @@ fn requires_package_only_prereleases_in_range_global_opt_in() { #[test] fn requires_package_prerelease_and_final_any() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/requires-package-prerelease-and-final-any.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-package-prerelease-and-final-any-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("requires-package-prerelease-and-final-any-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -2886,11 +2675,11 @@ fn requires_package_prerelease_and_final_any() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.1.0 + + a==0.1.0 "); // Since the user did not provide a prerelease specifier, the older stable version should be selected. - context.assert_installed("requires_package_prerelease_and_final_any_a", "0.1.0"); + context.assert_installed("a", "0.1.0"); } /// 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. @@ -2908,7 +2697,7 @@ fn requires_package_prerelease_and_final_any() { /// ├── a /// │ └── a-0.1.0 /// │ └── requires b>0.1 -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by b-1.0.0a1 /// └── b /// ├── b-0.1.0 /// └── b-1.0.0a1 @@ -2916,17 +2705,12 @@ fn requires_package_prerelease_and_final_any() { #[test] fn transitive_package_only_prereleases_in_range_opt_in() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("prereleases/transitive-package-only-prereleases-in-range-opt-in.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"transitive-package-only-prereleases-in-range-opt-in-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .arg("transitive-package-only-prereleases-in-range-opt-in-a") - .arg("transitive-package-only-prereleases-in-range-opt-in-b>0.0.0a1") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b>0.0.0a1") , @" success: true exit_code: 0 @@ -2936,19 +2720,13 @@ fn transitive_package_only_prereleases_in_range_opt_in() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==0.1.0 - + package-b==1.0.0a1 + + a==0.1.0 + + b==1.0.0a1 "); // Since the user included a dependency on `b` with a prerelease specifier, a prerelease version can be selected. - context.assert_installed( - "transitive_package_only_prereleases_in_range_opt_in_a", - "0.1.0", - ); - context.assert_installed( - "transitive_package_only_prereleases_in_range_opt_in_b", - "1.0.0a1", - ); + context.assert_installed("a", "0.1.0"); + context.assert_installed("b", "1.0.0a1"); } /// 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. @@ -2963,7 +2741,7 @@ fn transitive_package_only_prereleases_in_range_opt_in() { /// ├── a /// │ └── a-0.1.0 /// │ └── requires b>0.1 -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by b-1.0.0a1 /// └── b /// ├── b-0.1.0 /// └── b-1.0.0a1 @@ -2971,13 +2749,10 @@ fn transitive_package_only_prereleases_in_range_opt_in() { #[test] fn transitive_package_only_prereleases_in_range() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/transitive-package-only-prereleases-in-range.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-package-only-prereleases-in-range-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-package-only-prereleases-in-range-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: false exit_code: 1 @@ -2985,14 +2760,14 @@ fn transitive_package_only_prereleases_in_range() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-b<=0.1 is available and package-a==0.1.0 depends on package-b>0.1, we can conclude that package-a==0.1.0 cannot be used. - And because only package-a==0.1.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only b<=0.1 is available and a==0.1.0 depends on b>0.1, we can conclude that a==0.1.0 cannot be used. + And because only a==0.1.0 is available and you require a, we can conclude that your requirements are unsatisfiable. - hint: Pre-releases are available for `package-b` in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`) + hint: Pre-releases are available for `b` in the requested range (e.g., 1.0.0a1), but pre-releases weren't enabled (try: `--prerelease=allow`) "); // 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. - context.assert_not_installed("transitive_package_only_prereleases_in_range_a"); + context.assert_not_installed("a"); } /// The user requires any version of package `a` which requires `b` which only has prerelease versions available. @@ -3007,20 +2782,17 @@ fn transitive_package_only_prereleases_in_range() { /// ├── a /// │ └── a-0.1.0 /// │ └── requires b -/// │ └── unsatisfied: no matching version +/// │ └── satisfied by b-1.0.0a1 /// └── b /// └── b-1.0.0a1 /// ``` #[test] fn transitive_package_only_prereleases() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/transitive-package-only-prereleases.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-package-only-prereleases-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-package-only-prereleases-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -3030,13 +2802,13 @@ fn transitive_package_only_prereleases() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==0.1.0 - + package-b==1.0.0a1 + + a==0.1.0 + + b==1.0.0a1 "); // Since there are only prerelease versions of `b` available, it should be selected even though the user did not opt-in to prereleases. - context.assert_installed("transitive_package_only_prereleases_a", "0.1.0"); - context.assert_installed("transitive_package_only_prereleases_b", "1.0.0a1"); + context.assert_installed("a", "0.1.0"); + context.assert_installed("b", "1.0.0a1"); } /// 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. @@ -3052,7 +2824,7 @@ fn transitive_package_only_prereleases() { /// │ └── satisfied by b-1.0.0 /// ├── a /// │ └── a-1.0.0 -/// │ └── requires c!=2.0.0a5,!=2.0.0a6,!=2.0.0a7,!=2.0.0b1,<2.0.0b5,>1.0.0 +/// │ └── requires c>1.0.0,!=2.0.0a5,!=2.0.0a6,!=2.0.0a7,!=2.0.0b1,<2.0.0b5 /// │ ├── satisfied by c-2.0.0a1 /// │ ├── satisfied by c-2.0.0a2 /// │ ├── satisfied by c-2.0.0a3 @@ -3064,8 +2836,26 @@ fn transitive_package_only_prereleases() { /// │ └── satisfied by c-2.0.0b4 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c<=3.0.0,>=1.0.0 -/// │ └── satisfied by c-1.0.0 +/// │ └── requires c>=1.0.0,<=3.0.0 +/// │ ├── satisfied by c-1.0.0 +/// │ ├── satisfied by c-2.0.0a1 +/// │ ├── satisfied by c-2.0.0a2 +/// │ ├── satisfied by c-2.0.0a3 +/// │ ├── satisfied by c-2.0.0a4 +/// │ ├── satisfied by c-2.0.0a5 +/// │ ├── satisfied by c-2.0.0a6 +/// │ ├── satisfied by c-2.0.0a7 +/// │ ├── satisfied by c-2.0.0a8 +/// │ ├── satisfied by c-2.0.0a9 +/// │ ├── satisfied by c-2.0.0b1 +/// │ ├── satisfied by c-2.0.0b2 +/// │ ├── satisfied by c-2.0.0b3 +/// │ ├── satisfied by c-2.0.0b4 +/// │ ├── satisfied by c-2.0.0b5 +/// │ ├── satisfied by c-2.0.0b6 +/// │ ├── satisfied by c-2.0.0b7 +/// │ ├── satisfied by c-2.0.0b8 +/// │ └── satisfied by c-2.0.0b9 /// └── c /// ├── c-1.0.0 /// ├── c-2.0.0a1 @@ -3090,17 +2880,13 @@ fn transitive_package_only_prereleases() { #[test] fn transitive_prerelease_and_stable_dependency_many_versions_holes() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new( + "prereleases/transitive-prerelease-and-stable-dependency-many-versions-holes.toml", + ); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"transitive-prerelease-and-stable-dependency-many-versions-holes-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .arg("transitive-prerelease-and-stable-dependency-many-versions-holes-a") - .arg("transitive-prerelease-and-stable-dependency-many-versions-holes-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: false exit_code: 1 @@ -3108,30 +2894,28 @@ fn transitive_prerelease_and_stable_dependency_many_versions_holes() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only the following versions of package-c are available: - package-c<=1.0.0 - package-c>=2.0.0a5,<=2.0.0a7 - package-c==2.0.0b1 - package-c>=2.0.0b5 - and package-a==1.0.0 depends on one of: - package-c>1.0.0,<2.0.0a5 - package-c>2.0.0a7,<2.0.0b1 - package-c>2.0.0b1,<2.0.0b5 - we can conclude that package-a==1.0.0 cannot be used. - And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only the following versions of c are available: + c<=1.0.0 + c>=2.0.0a5,<=2.0.0a7 + c==2.0.0b1 + c>=2.0.0b5 + and a==1.0.0 depends on one of: + c>1.0.0,<2.0.0a5 + c>2.0.0a7,<2.0.0b1 + c>2.0.0b1,<2.0.0b5 + we can conclude that a==1.0.0 cannot be used. + And because only a==1.0.0 is available and you require a, we can conclude that your requirements are unsatisfiable. - hint: `package-c` was requested with a pre-release marker (e.g., all of: - package-c>1.0.0,<2.0.0a5 - package-c>2.0.0a7,<2.0.0b1 - package-c>2.0.0b1,<2.0.0b5 + hint: `c` was requested with a pre-release marker (e.g., all of: + c>1.0.0,<2.0.0a5 + c>2.0.0a7,<2.0.0b1 + c>2.0.0b1,<2.0.0b5 ), but pre-releases weren't enabled (try: `--prerelease=allow`) "); // Since the user did not explicitly opt-in to a prerelease, it cannot be selected. - context - .assert_not_installed("transitive_prerelease_and_stable_dependency_many_versions_holes_a"); - context - .assert_not_installed("transitive_prerelease_and_stable_dependency_many_versions_holes_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// A transitive dependency has both a prerelease and a stable selector, but can only be satisfied by a prerelease. There are many prerelease versions. @@ -3159,8 +2943,26 @@ fn transitive_prerelease_and_stable_dependency_many_versions_holes() { /// │ └── satisfied by c-2.0.0b9 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c<=3.0.0,>=1.0.0 -/// │ └── satisfied by c-1.0.0 +/// │ └── requires c>=1.0.0,<=3.0.0 +/// │ ├── satisfied by c-1.0.0 +/// │ ├── satisfied by c-2.0.0a1 +/// │ ├── satisfied by c-2.0.0a2 +/// │ ├── satisfied by c-2.0.0a3 +/// │ ├── satisfied by c-2.0.0a4 +/// │ ├── satisfied by c-2.0.0a5 +/// │ ├── satisfied by c-2.0.0a6 +/// │ ├── satisfied by c-2.0.0a7 +/// │ ├── satisfied by c-2.0.0a8 +/// │ ├── satisfied by c-2.0.0a9 +/// │ ├── satisfied by c-2.0.0b1 +/// │ ├── satisfied by c-2.0.0b2 +/// │ ├── satisfied by c-2.0.0b3 +/// │ ├── satisfied by c-2.0.0b4 +/// │ ├── satisfied by c-2.0.0b5 +/// │ ├── satisfied by c-2.0.0b6 +/// │ ├── satisfied by c-2.0.0b7 +/// │ ├── satisfied by c-2.0.0b8 +/// │ └── satisfied by c-2.0.0b9 /// └── c /// ├── c-1.0.0 /// ├── c-2.0.0a1 @@ -3185,17 +2987,13 @@ fn transitive_prerelease_and_stable_dependency_many_versions_holes() { #[test] fn transitive_prerelease_and_stable_dependency_many_versions() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new( + "prereleases/transitive-prerelease-and-stable-dependency-many-versions.toml", + ); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"transitive-prerelease-and-stable-dependency-many-versions-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .arg("transitive-prerelease-and-stable-dependency-many-versions-a") - .arg("transitive-prerelease-and-stable-dependency-many-versions-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: false exit_code: 1 @@ -3203,17 +3001,17 @@ fn transitive_prerelease_and_stable_dependency_many_versions() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 depends on package-c>=2.0.0b1, we can conclude that all versions of package-a depend on package-c>=2.0.0b1. - And because only package-c<2.0.0b1 is available, we can conclude that all versions of package-a depend on package-c>3.0.0. - And because package-b==1.0.0 depends on package-c and only package-b==1.0.0 is available, we can conclude that all versions of package-a and all versions of package-b are incompatible. - And because you require package-a and package-b, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 depends on c>=2.0.0b1, we can conclude that all versions of a depend on c>=2.0.0b1. + And because only c<2.0.0b1 is available, we can conclude that all versions of a depend on c>3.0.0. + And because b==1.0.0 depends on c and only b==1.0.0 is available, we can conclude that all versions of a and all versions of b are incompatible. + And because you require a and b, we can conclude that your requirements are unsatisfiable. - hint: `package-c` was requested with a pre-release marker (e.g., package-c>=2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`) + hint: `c` was requested with a pre-release marker (e.g., c>=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. - context.assert_not_installed("transitive_prerelease_and_stable_dependency_many_versions_a"); - context.assert_not_installed("transitive_prerelease_and_stable_dependency_many_versions_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// 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. @@ -3236,8 +3034,9 @@ fn transitive_prerelease_and_stable_dependency_many_versions() { /// │ └── satisfied by c-2.0.0b1 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c<=3.0.0,>=1.0.0 -/// │ └── satisfied by c-1.0.0 +/// │ └── requires c>=1.0.0,<=3.0.0 +/// │ ├── satisfied by c-1.0.0 +/// │ └── satisfied by c-2.0.0b1 /// └── c /// ├── c-1.0.0 /// └── c-2.0.0b1 @@ -3245,18 +3044,13 @@ fn transitive_prerelease_and_stable_dependency_many_versions() { #[test] fn transitive_prerelease_and_stable_dependency_opt_in() { let context = uv_test::test_context!("3.12"); + let server = + PackseServer::new("prereleases/transitive-prerelease-and-stable-dependency-opt-in.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"transitive-prerelease-and-stable-dependency-opt-in-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .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") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") + .arg("c>=0.0.0a1") , @" success: true exit_code: 0 @@ -3266,24 +3060,15 @@ fn transitive_prerelease_and_stable_dependency_opt_in() { Resolved 3 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] - + package-a==1.0.0 - + package-b==1.0.0 - + package-c==2.0.0b1 + + a==1.0.0 + + b==1.0.0 + + c==2.0.0b1 "); // Since the user explicitly opted-in to a prerelease for `c`, it can be installed. - context.assert_installed( - "transitive_prerelease_and_stable_dependency_opt_in_a", - "1.0.0", - ); - context.assert_installed( - "transitive_prerelease_and_stable_dependency_opt_in_b", - "1.0.0", - ); - context.assert_installed( - "transitive_prerelease_and_stable_dependency_opt_in_c", - "2.0.0b1", - ); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "1.0.0"); + context.assert_installed("c", "2.0.0b1"); } /// A transitive dependency has both a prerelease and a stable selector, but can only be satisfied by a prerelease @@ -3303,8 +3088,9 @@ fn transitive_prerelease_and_stable_dependency_opt_in() { /// │ └── satisfied by c-2.0.0b1 /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c<=3.0.0,>=1.0.0 -/// │ └── satisfied by c-1.0.0 +/// │ └── requires c>=1.0.0,<=3.0.0 +/// │ ├── satisfied by c-1.0.0 +/// │ └── satisfied by c-2.0.0b1 /// └── c /// ├── c-1.0.0 /// └── c-2.0.0b1 @@ -3312,14 +3098,11 @@ fn transitive_prerelease_and_stable_dependency_opt_in() { #[test] fn transitive_prerelease_and_stable_dependency() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("prereleases/transitive-prerelease-and-stable-dependency.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-prerelease-and-stable-dependency-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-prerelease-and-stable-dependency-a") - .arg("transitive-prerelease-and-stable-dependency-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: false exit_code: 1 @@ -3327,15 +3110,15 @@ fn transitive_prerelease_and_stable_dependency() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-c==2.0.0b1 and package-a==1.0.0 depends on package-c==2.0.0b1, we can conclude that package-a==1.0.0 cannot be used. - And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because there is no version of c==2.0.0b1 and a==1.0.0 depends on c==2.0.0b1, we can conclude that a==1.0.0 cannot be used. + And because only a==1.0.0 is available and you require a, we can conclude that your requirements are unsatisfiable. - hint: `package-c` was requested with a pre-release marker (e.g., package-c==2.0.0b1), but pre-releases weren't enabled (try: `--prerelease=allow`) + hint: `c` was requested with a pre-release marker (e.g., c==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. - context.assert_not_installed("transitive_prerelease_and_stable_dependency_a"); - context.assert_not_installed("transitive_prerelease_and_stable_dependency_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } /// The user requires a package where recent versions require a Python version greater than the current version, but an older version is compatible. @@ -3362,13 +3145,10 @@ fn transitive_prerelease_and_stable_dependency() { #[test] fn python_greater_than_current_backtrack() { let context = uv_test::test_context!("3.9"); + let server = PackseServer::new("requires_python/python-greater-than-current-backtrack.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-greater-than-current-backtrack-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-greater-than-current-backtrack-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -3378,10 +3158,10 @@ fn python_greater_than_current_backtrack() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); - context.assert_installed("python_greater_than_current_backtrack_a", "1.0.0"); + context.assert_installed("a", "1.0.0"); } /// The user requires a package where recent versions require a Python version greater than the current version, but an excluded older version is compatible. @@ -3407,13 +3187,10 @@ fn python_greater_than_current_backtrack() { #[test] fn python_greater_than_current_excluded() { let context = uv_test::test_context!("3.9"); + let server = PackseServer::new("requires_python/python-greater-than-current-excluded.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-greater-than-current-excluded-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-greater-than-current-excluded-a>=2.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=2.0.0") , @" success: false exit_code: 1 @@ -3421,22 +3198,22 @@ fn python_greater_than_current_excluded() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==2.0.0 depends on Python>=3.10, we can conclude that package-a==2.0.0 cannot be used. - And because only the following versions of package-a are available: - package-a<=2.0.0 - package-a==3.0.0 - package-a==4.0.0 - we can conclude that package-a>=2.0.0,<3.0.0 cannot be used. (1) + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and a==2.0.0 depends on Python>=3.10, we can conclude that a==2.0.0 cannot be used. + And because only the following versions of a are available: + a<=2.0.0 + a==3.0.0 + a==4.0.0 + we can conclude that a>=2.0.0,<3.0.0 cannot be used. (1) - Because the current Python version (3.9.[X]) does not satisfy Python>=3.11 and package-a==3.0.0 depends on Python>=3.11, we can conclude that package-a==3.0.0 cannot be used. - And because we know from (1) that package-a>=2.0.0,<3.0.0 cannot be used, we can conclude that package-a>=2.0.0,<4.0.0 cannot be used. (2) + Because the current Python version (3.9.[X]) does not satisfy Python>=3.11 and a==3.0.0 depends on Python>=3.11, we can conclude that a==3.0.0 cannot be used. + And because we know from (1) that a>=2.0.0,<3.0.0 cannot be used, we can conclude that a>=2.0.0,<4.0.0 cannot be used. (2) - Because the current Python version (3.9.[X]) does not satisfy Python>=3.12 and package-a==4.0.0 depends on Python>=3.12, we can conclude that package-a==4.0.0 cannot be used. - And because we know from (2) that package-a>=2.0.0,<4.0.0 cannot be used, we can conclude that package-a>=2.0.0 cannot be used. - And because you require package-a>=2.0.0, we can conclude that your requirements are unsatisfiable. + Because the current Python version (3.9.[X]) does not satisfy Python>=3.12 and a==4.0.0 depends on Python>=3.12, we can conclude that a==4.0.0 cannot be used. + And because we know from (2) that a>=2.0.0,<4.0.0 cannot be used, we can conclude that a>=2.0.0 cannot be used. + And because you require a>=2.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("python_greater_than_current_excluded_a"); + context.assert_not_installed("a"); } /// The user requires a package which has many versions which all require a Python version greater than the current version @@ -3477,13 +3254,10 @@ fn python_greater_than_current_excluded() { #[test] fn python_greater_than_current_many() { let context = uv_test::test_context!("3.9"); + let server = PackseServer::new("requires_python/python-greater-than-current-many.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-greater-than-current-many-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-greater-than-current-many-a==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") , @" success: false exit_code: 1 @@ -3491,10 +3265,10 @@ fn python_greater_than_current_many() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because there is no version of package-a==1.0.0 and you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because there is no version of a==1.0.0 and you require a==1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("python_greater_than_current_many_a"); + context.assert_not_installed("a"); } /// The user requires a package which requires a Python version with a patch version greater than the current patch version @@ -3514,13 +3288,10 @@ fn python_greater_than_current_many() { #[test] fn python_greater_than_current_patch() { let context = uv_test::test_context!("3.13.0"); + let server = PackseServer::new("requires_python/python-greater-than-current-patch.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-greater-than-current-patch-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-greater-than-current-patch-a==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") , @" success: false exit_code: 1 @@ -3528,11 +3299,11 @@ fn python_greater_than_current_patch() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.13) does not satisfy Python>=3.13.2 and package-a==1.0.0 depends on Python>=3.13.2, we can conclude that package-a==1.0.0 cannot be used. - And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because the current Python version (3.13) does not satisfy Python>=3.13.2 and a==1.0.0 depends on Python>=3.13.2, we can conclude that a==1.0.0 cannot be used. + And because you require a==1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("python_greater_than_current_patch_a"); + context.assert_not_installed("a"); } /// The user requires a package which requires a Python version greater than the current version @@ -3551,13 +3322,10 @@ fn python_greater_than_current_patch() { #[test] fn python_greater_than_current() { let context = uv_test::test_context!("3.9"); + let server = PackseServer::new("requires_python/python-greater-than-current.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-greater-than-current-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-greater-than-current-a==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") , @" success: false exit_code: 1 @@ -3565,11 +3333,11 @@ fn python_greater_than_current() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and package-a==1.0.0 depends on Python>=3.10, we can conclude that package-a==1.0.0 cannot be used. - And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because the current Python version (3.9.[X]) does not satisfy Python>=3.10 and a==1.0.0 depends on Python>=3.10, we can conclude that a==1.0.0 cannot be used. + And because you require a==1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("python_greater_than_current_a"); + context.assert_not_installed("a"); } /// The user requires a package which requires a Python version less than the current version @@ -3588,13 +3356,10 @@ fn python_greater_than_current() { #[test] fn python_less_than_current() { let context = uv_test::test_context!("3.9"); + let server = PackseServer::new("requires_python/python-less-than-current.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-less-than-current-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-less-than-current-a==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") , @" success: true exit_code: 0 @@ -3604,7 +3369,7 @@ fn python_less_than_current() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); // We ignore the upper bound on Python requirements @@ -3626,13 +3391,10 @@ fn python_less_than_current() { #[test] fn python_version_does_not_exist() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("requires_python/python-version-does-not-exist.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"python-version-does-not-exist-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("python-version-does-not-exist-a==1.0.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a==1.0.0") , @" success: false exit_code: 1 @@ -3640,11 +3402,11 @@ fn python_version_does_not_exist() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because the current Python version (3.12.[X]) does not satisfy Python>=3.30 and package-a==1.0.0 depends on Python>=3.30, we can conclude that package-a==1.0.0 cannot be used. - And because you require package-a==1.0.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because the current Python version (3.12.[X]) does not satisfy Python>=3.30 and a==1.0.0 depends on Python>=3.30, we can conclude that a==1.0.0 cannot be used. + And because you require a==1.0.0, we can conclude that your requirements are unsatisfiable. "); - context.assert_not_installed("python_version_does_not_exist_a"); + context.assert_not_installed("a"); } /// Both wheels and source distributions are available, and the user has disabled binaries. @@ -3662,15 +3424,12 @@ fn python_version_does_not_exist() { #[test] fn no_binary() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-binary.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-binary-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--no-binary") - .arg("no-binary-a") - .arg("no-binary-a") + .arg("a") + .arg("a") , @" success: true exit_code: 0 @@ -3680,7 +3439,7 @@ fn no_binary() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); // The source distribution should be used for install @@ -3701,15 +3460,12 @@ fn no_binary() { #[test] fn no_build() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-build.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-build-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--only-binary") - .arg("no-build-a") - .arg("no-build-a") + .arg("a") + .arg("a") , @" success: true exit_code: 0 @@ -3719,7 +3475,7 @@ fn no_build() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); // The wheel should be used for install @@ -3740,14 +3496,11 @@ fn no_build() { #[test] fn no_sdist_no_wheels_with_matching_abi() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-sdist-no-wheels-with-matching-abi.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-sdist-no-wheels-with-matching-abi-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--python-platform=x86_64-manylinux2014") - .arg("no-sdist-no-wheels-with-matching-abi-a") + .arg("a") , @" success: false exit_code: 1 @@ -3755,13 +3508,13 @@ fn no_sdist_no_wheels_with_matching_abi() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels with a matching Python ABI tag (e.g., `cp312`), we can conclude that all versions of package-a cannot be used. - And because you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 has no wheels with a matching Python ABI tag (e.g., `cp312`), we can conclude that all versions of a cannot be used. + And because you require a, we can conclude that your requirements are unsatisfiable. - hint: You require CPython 3.12 (`cp312`), but we only found wheels for `package-a` (v1.0.0) with the following Python ABI tag: `graalpy240_310_native` + hint: You require CPython 3.12 (`cp312`), but we only found wheels for `a` (v1.0.0) with the following Python ABI tag: `graalpy240_310_native` "); - context.assert_not_installed("no_sdist_no_wheels_with_matching_abi_a"); + context.assert_not_installed("a"); } /// No wheels with matching platform tags are available, nor are any source distributions available @@ -3779,14 +3532,11 @@ fn no_sdist_no_wheels_with_matching_abi() { #[test] fn no_sdist_no_wheels_with_matching_platform() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-sdist-no-wheels-with-matching-platform.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-sdist-no-wheels-with-matching-platform-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--python-platform=x86_64-manylinux2014") - .arg("no-sdist-no-wheels-with-matching-platform-a") + .arg("a") , @" success: false exit_code: 1 @@ -3794,13 +3544,13 @@ fn no_sdist_no_wheels_with_matching_platform() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels with a matching platform tag (e.g., `manylinux_2_17_x86_64`), we can conclude that all versions of package-a cannot be used. - And because you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 has no wheels with a matching platform tag (e.g., `manylinux_2_17_x86_64`), we can conclude that all versions of a cannot be used. + And because you require a, we can conclude that your requirements are unsatisfiable. - hint: Wheels are available for `package-a` (v1.0.0) on the following platform: `macosx_10_0_ppc64` + hint: Wheels are available for `a` (v1.0.0) on the following platform: `macosx_10_0_ppc64` "); - context.assert_not_installed("no_sdist_no_wheels_with_matching_platform_a"); + context.assert_not_installed("a"); } /// No wheels with matching Python tags are available, nor are any source distributions available @@ -3818,14 +3568,11 @@ fn no_sdist_no_wheels_with_matching_platform() { #[test] fn no_sdist_no_wheels_with_matching_python() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-sdist-no-wheels-with-matching-python.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-sdist-no-wheels-with-matching-python-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--python-platform=x86_64-manylinux2014") - .arg("no-sdist-no-wheels-with-matching-python-a") + .arg("a") , @" success: false exit_code: 1 @@ -3833,13 +3580,13 @@ fn no_sdist_no_wheels_with_matching_python() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels with a matching Python implementation tag (e.g., `cp312`), we can conclude that all versions of package-a cannot be used. - And because you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 has no wheels with a matching Python implementation tag (e.g., `cp312`), we can conclude that all versions of a cannot be used. + And because you require a, we can conclude that your requirements are unsatisfiable. - hint: You require CPython 3.12 (`cp312`), but we only found wheels for `package-a` (v1.0.0) with the following Python implementation tag: `graalpy310` + hint: You require CPython 3.12 (`cp312`), but we only found wheels for `a` (v1.0.0) with the following Python implementation tag: `graalpy310` "); - context.assert_not_installed("no_sdist_no_wheels_with_matching_python_a"); + context.assert_not_installed("a"); } /// No wheels are available, only source distributions but the user has disabled builds. @@ -3857,15 +3604,12 @@ fn no_sdist_no_wheels_with_matching_python() { #[test] fn no_wheels_no_build() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-wheels-no-build.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-wheels-no-build-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--only-binary") - .arg("no-wheels-no-build-a") - .arg("no-wheels-no-build-a") + .arg("a") + .arg("a") , @" success: false exit_code: 1 @@ -3873,13 +3617,13 @@ fn no_wheels_no_build() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no usable wheels, we can conclude that all versions of package-a cannot be used. - And because you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 has no usable wheels, we can conclude that all versions of a cannot be used. + And because you require a, we can conclude that your requirements are unsatisfiable. - hint: Wheels are required for `package-a` because building from source is disabled for `package-a` (i.e., with `--no-build-package package-a`) + hint: Wheels are required for `a` because building from source is disabled for `a` (i.e., with `--no-build-package a`) "); - context.assert_not_installed("no_wheels_no_build_a"); + context.assert_not_installed("a"); } /// No wheels with matching platform tags are available, just source distributions. @@ -3897,13 +3641,10 @@ fn no_wheels_no_build() { #[test] fn no_wheels_with_matching_platform() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-wheels-with-matching-platform.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-wheels-with-matching-platform-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("no-wheels-with-matching-platform-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -3913,7 +3654,7 @@ fn no_wheels_with_matching_platform() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); } @@ -3932,13 +3673,10 @@ fn no_wheels_with_matching_platform() { #[test] fn no_wheels() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/no-wheels.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"no-wheels-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("no-wheels-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -3948,7 +3686,7 @@ fn no_wheels() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); } @@ -3967,15 +3705,12 @@ fn no_wheels() { #[test] fn only_wheels_no_binary() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/only-wheels-no-binary.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"only-wheels-no-binary-", "package-")); - - uv_snapshot!(filters, command(&context) + uv_snapshot!(context.filters(), command(&context, &server) .arg("--no-binary") - .arg("only-wheels-no-binary-a") - .arg("only-wheels-no-binary-a") + .arg("a") + .arg("a") , @" success: false exit_code: 1 @@ -3983,13 +3718,13 @@ fn only_wheels_no_binary() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no source distribution, we can conclude that all versions of package-a cannot be used. - And because you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 has no source distribution, we can conclude that all versions of a cannot be used. + And because you require a, we can conclude that your requirements are unsatisfiable. - hint: A source distribution is required for `package-a` because using pre-built wheels is disabled for `package-a` (i.e., with `--no-binary-package package-a`) + hint: A source distribution is required for `a` because using pre-built wheels is disabled for `a` (i.e., with `--no-binary-package a`) "); - context.assert_not_installed("only_wheels_no_binary_a"); + context.assert_not_installed("a"); } /// No source distributions are available, only wheels. @@ -4007,13 +3742,10 @@ fn only_wheels_no_binary() { #[test] fn only_wheels() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/only-wheels.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"only-wheels-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("only-wheels-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -4023,7 +3755,7 @@ fn only_wheels() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); } @@ -4042,13 +3774,10 @@ fn only_wheels() { #[test] fn specific_tag_and_default() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("wheels/specific-tag-and-default.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"specific-tag-and-default-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("specific-tag-and-default-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -4058,7 +3787,7 @@ fn specific_tag_and_default() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==1.0.0 + + a==1.0.0 "); } @@ -4078,13 +3807,10 @@ fn specific_tag_and_default() { #[test] fn package_only_yanked_in_range() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/package-only-yanked-in-range.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-only-yanked-in-range-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-only-yanked-in-range-a>0.1.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>0.1.0") , @" success: false exit_code: 1 @@ -4092,15 +3818,15 @@ fn package_only_yanked_in_range() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only the following versions of package-a are available: - package-a<=0.1.0 - package-a==1.0.0 - and package-a==1.0.0 was yanked (reason: Yanked for testing), we can conclude that package-a>0.1.0 cannot be used. - And because you require package-a>0.1.0, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only the following versions of a are available: + a<=0.1.0 + a==1.0.0 + and a==1.0.0 was yanked, we can conclude that a>0.1.0 cannot be used. + And because you require a>0.1.0, we can conclude that your requirements are unsatisfiable. "); // Since there are other versions of `a` available, yanked versions should not be selected without explicit opt-in. - context.assert_not_installed("package_only_yanked_in_range_a"); + context.assert_not_installed("a"); } /// The user requires any version of package `a` which only has yanked versions available. @@ -4118,13 +3844,10 @@ fn package_only_yanked_in_range() { #[test] fn package_only_yanked() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/package-only-yanked.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-only-yanked-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-only-yanked-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: false exit_code: 1 @@ -4132,12 +3855,12 @@ fn package_only_yanked() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 was yanked (reason: Yanked for testing), we can conclude that all versions of package-a cannot be used. - And because you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only a==1.0.0 is available and a==1.0.0 was yanked, we can conclude that all versions of a cannot be used. + And because you require a, we can conclude that your requirements are unsatisfiable. "); // Yanked versions should not be installed, even if they are the only one available. - context.assert_not_installed("package_only_yanked_a"); + context.assert_not_installed("a"); } /// The user requires any version of `a` and both yanked and unyanked releases are available. @@ -4159,13 +3882,10 @@ fn package_only_yanked() { #[test] fn package_yanked_specified_mixed_available() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/package-yanked-specified-mixed-available.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"package-yanked-specified-mixed-available-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("package-yanked-specified-mixed-available-a>=0.1.0") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a>=0.1.0") , @" success: true exit_code: 0 @@ -4175,11 +3895,11 @@ fn package_yanked_specified_mixed_available() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.3.0 + + a==0.3.0 "); // The latest unyanked version should be selected. - context.assert_installed("package_yanked_specified_mixed_available_a", "0.3.0"); + context.assert_installed("a", "0.3.0"); } /// The user requires any version of package `a` has a yanked version available and an older unyanked version. @@ -4198,13 +3918,10 @@ fn package_yanked_specified_mixed_available() { #[test] fn requires_package_yanked_and_unyanked_any() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/requires-package-yanked-and-unyanked-any.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"requires-package-yanked-and-unyanked-any-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("requires-package-yanked-and-unyanked-any-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: true exit_code: 0 @@ -4214,11 +3931,11 @@ fn requires_package_yanked_and_unyanked_any() { Resolved 1 package in [TIME] Prepared 1 package in [TIME] Installed 1 package in [TIME] - + package-a==0.1.0 + + a==0.1.0 "); // The unyanked version should be selected. - context.assert_installed("requires_package_yanked_and_unyanked_any_a", "0.1.0"); + context.assert_installed("a", "0.1.0"); } /// 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. @@ -4243,18 +3960,12 @@ fn requires_package_yanked_and_unyanked_any() { #[test] fn transitive_package_only_yanked_in_range_opt_in() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/transitive-package-only-yanked-in-range-opt-in.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"transitive-package-only-yanked-in-range-opt-in-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .arg("transitive-package-only-yanked-in-range-opt-in-a") - .arg("transitive-package-only-yanked-in-range-opt-in-b==1.0.0") - , @r#" + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b==1.0.0") + , @" success: true exit_code: 0 ----- stdout ----- @@ -4263,14 +3974,14 @@ fn transitive_package_only_yanked_in_range_opt_in() { Resolved 2 packages in [TIME] Prepared 2 packages in [TIME] Installed 2 packages in [TIME] - + package-a==0.1.0 - + package-b==1.0.0 - warning: `package-b==1.0.0` is yanked (reason: "Yanked for testing") - "#); + + a==0.1.0 + + b==1.0.0 + warning: `b==1.0.0` is yanked + "); // Since the user included a dependency on `b` with an exact specifier, the yanked version can be selected. - context.assert_installed("transitive_package_only_yanked_in_range_opt_in_a", "0.1.0"); - context.assert_installed("transitive_package_only_yanked_in_range_opt_in_b", "1.0.0"); + context.assert_installed("a", "0.1.0"); + context.assert_installed("b", "1.0.0"); } /// The user requires package `a` which has a dependency on a package which only matches yanked versions. @@ -4293,13 +4004,10 @@ fn transitive_package_only_yanked_in_range_opt_in() { #[test] fn transitive_package_only_yanked_in_range() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/transitive-package-only-yanked-in-range.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-package-only-yanked-in-range-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-package-only-yanked-in-range-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: false exit_code: 1 @@ -4307,16 +4015,16 @@ fn transitive_package_only_yanked_in_range() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only the following versions of package-b are available: - package-b<=0.1 - package-b==1.0.0 - and package-b==1.0.0 was yanked (reason: Yanked for testing), we can conclude that package-b>0.1 cannot be used. - And because package-a==0.1.0 depends on package-b>0.1, we can conclude that package-a==0.1.0 cannot be used. - And because only package-a==0.1.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only the following versions of b are available: + b<=0.1 + b==1.0.0 + and b==1.0.0 was yanked, we can conclude that b>0.1 cannot be used. + And because a==0.1.0 depends on b>0.1, we can conclude that a==0.1.0 cannot be used. + And because only a==0.1.0 is available and you require a, we can conclude that your requirements are unsatisfiable. "); // Yanked versions should not be installed, even if they are the only valid version in a range. - context.assert_not_installed("transitive_package_only_yanked_in_range_a"); + context.assert_not_installed("a"); } /// The user requires any version of package `a` which requires `b` which only has yanked versions available. @@ -4338,13 +4046,10 @@ fn transitive_package_only_yanked_in_range() { #[test] fn transitive_package_only_yanked() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/transitive-package-only-yanked.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-package-only-yanked-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-package-only-yanked-a") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") , @" success: false exit_code: 1 @@ -4352,13 +4057,13 @@ fn transitive_package_only_yanked() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because only package-b==1.0.0 is available and package-b==1.0.0 was yanked (reason: Yanked for testing), we can conclude that all versions of package-b cannot be used. - And because package-a==0.1.0 depends on package-b, we can conclude that package-a==0.1.0 cannot be used. - And because only package-a==0.1.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because only b==1.0.0 is available and b==1.0.0 was yanked, we can conclude that all versions of b cannot be used. + And because a==0.1.0 depends on b, we can conclude that a==0.1.0 cannot be used. + And because only a==0.1.0 is available and you require a, we can conclude that your requirements are unsatisfiable. "); // Yanked versions should not be installed, even if they are the only one available. - context.assert_not_installed("transitive_package_only_yanked_a"); + context.assert_not_installed("a"); } /// 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. @@ -4380,7 +4085,7 @@ fn transitive_package_only_yanked() { /// │ └── unsatisfied: no matching version /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c<=3.0.0,>=1.0.0 +/// │ └── requires c>=1.0.0,<=3.0.0 /// │ └── satisfied by c-1.0.0 /// └── c /// ├── c-1.0.0 @@ -4389,19 +4094,13 @@ fn transitive_package_only_yanked() { #[test] fn transitive_yanked_and_unyanked_dependency_opt_in() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/transitive-yanked-and-unyanked-dependency-opt-in.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push(( - r"transitive-yanked-and-unyanked-dependency-opt-in-", - "package-", - )); - - uv_snapshot!(filters, command(&context) - .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#" + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") + .arg("c==2.0.0") + , @" success: true exit_code: 0 ----- stdout ----- @@ -4410,25 +4109,16 @@ fn transitive_yanked_and_unyanked_dependency_opt_in() { Resolved 3 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] - + package-a==1.0.0 - + package-b==1.0.0 - + package-c==2.0.0 - warning: `package-c==2.0.0` is yanked (reason: "Yanked for testing") - "#); + + a==1.0.0 + + b==1.0.0 + + c==2.0.0 + warning: `c==2.0.0` is yanked + "); // Since the user explicitly selected the yanked version of `c`, it can be installed. - context.assert_installed( - "transitive_yanked_and_unyanked_dependency_opt_in_a", - "1.0.0", - ); - context.assert_installed( - "transitive_yanked_and_unyanked_dependency_opt_in_b", - "1.0.0", - ); - context.assert_installed( - "transitive_yanked_and_unyanked_dependency_opt_in_c", - "2.0.0", - ); + context.assert_installed("a", "1.0.0"); + context.assert_installed("b", "1.0.0"); + context.assert_installed("c", "2.0.0"); } /// A transitive dependency has both a yanked and an unyanked version, but can only be satisfied by a yanked version @@ -4448,7 +4138,7 @@ fn transitive_yanked_and_unyanked_dependency_opt_in() { /// │ └── unsatisfied: no matching version /// ├── b /// │ └── b-1.0.0 -/// │ └── requires c<=3.0.0,>=1.0.0 +/// │ └── requires c>=1.0.0,<=3.0.0 /// │ └── satisfied by c-1.0.0 /// └── c /// ├── c-1.0.0 @@ -4457,14 +4147,11 @@ fn transitive_yanked_and_unyanked_dependency_opt_in() { #[test] fn transitive_yanked_and_unyanked_dependency() { let context = uv_test::test_context!("3.12"); + let server = PackseServer::new("yanked/transitive-yanked-and-unyanked-dependency.toml"); - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"transitive-yanked-and-unyanked-dependency-", "package-")); - - uv_snapshot!(filters, command(&context) - .arg("transitive-yanked-and-unyanked-dependency-a") - .arg("transitive-yanked-and-unyanked-dependency-b") + uv_snapshot!(context.filters(), command(&context, &server) + .arg("a") + .arg("b") , @" success: false exit_code: 1 @@ -4472,11 +4159,11 @@ fn transitive_yanked_and_unyanked_dependency() { ----- stderr ----- × No solution found when resolving dependencies: - ╰─▶ Because package-c==2.0.0 was yanked (reason: Yanked for testing) and package-a==1.0.0 depends on package-c==2.0.0, we can conclude that package-a==1.0.0 cannot be used. - And because only package-a==1.0.0 is available and you require package-a, we can conclude that your requirements are unsatisfiable. + ╰─▶ Because c==2.0.0 was yanked and a==1.0.0 depends on c==2.0.0, we can conclude that a==1.0.0 cannot be used. + And because only a==1.0.0 is available and you require a, we can conclude that your requirements are unsatisfiable. "); // Since the user did not explicitly select the yanked version, it cannot be used. - context.assert_not_installed("transitive_yanked_and_unyanked_dependency_a"); - context.assert_not_installed("transitive_yanked_and_unyanked_dependency_b"); + context.assert_not_installed("a"); + context.assert_not_installed("b"); } diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index fd6054d162..f5469a238f 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -14,7 +14,7 @@ use wiremock::{Mock, MockServer, ResponseTemplate}; use uv_fs::Simplified; use uv_static::EnvVars; -use uv_test::{TestContext, download_to_disk, packse_index_url, uv_snapshot, venv_bin_path}; +use uv_test::{TestContext, download_to_disk, uv_snapshot, venv_bin_path}; #[test] fn sync() -> Result<()> { @@ -13807,6 +13807,8 @@ fn direct_url_dependency_metadata() -> Result<()> { #[test] fn sync_required_environment_hint() -> Result<()> { + let server = + uv_test::packse::PackseServer::new("wheels/no-sdist-no-wheels-with-matching-platform.toml"); let context = uv_test::test_context!("3.13"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -13815,14 +13817,14 @@ fn sync_required_environment_hint() -> Result<()> { name = "example" version = "0.1.0" requires-python = ">=3.13" - dependencies = ["no-sdist-no-wheels-with-matching-platform-a"] + dependencies = ["a"] [[tool.uv.index]] name = "packse" - url = "{}" + url = "{index_url}" default = true "#, - packse_index_url() + index_url = server.index_url() })?; uv_snapshot!(context.filters(), context.lock().env_remove(EnvVars::UV_EXCLUDE_NEWER), @" @@ -13851,9 +13853,9 @@ fn sync_required_environment_hint() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] - error: Distribution `no-sdist-no-wheels-with-matching-platform-a==1.0.0 @ registry+https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/` can't be installed because it doesn't have a source distribution or wheel for the current platform + error: Distribution `a==1.0.0 @ registry+http://[LOCALHOST]/simple/` can't be installed because it doesn't have a source distribution or wheel for the current platform - hint: You're on [PLATFORM] (`[TAG]`), but `no-sdist-no-wheels-with-matching-platform-a` (v1.0.0) only has wheels for the following platform: `macosx_10_0_ppc64`; consider adding "sys_platform == '[PLATFORM]' and platform_machine == '[MACHINE]'" to `tool.uv.required-environments` to ensure uv resolves to a version with compatible wheels + hint: You're on [PLATFORM] (`[TAG]`), but `a` (v1.0.0) only has wheels for the following platform: `macosx_10_0_ppc64`; consider adding "sys_platform == '[PLATFORM]' and platform_machine == '[MACHINE]'" to `tool.uv.required-environments` to ensure uv resolves to a version with compatible wheels "#); Ok(()) diff --git a/scripts/scenarios/generate.py b/scripts/scenarios/generate.py deleted file mode 100755 index 592f072fab..0000000000 --- a/scripts/scenarios/generate.py +++ /dev/null @@ -1,362 +0,0 @@ -#!/usr/bin/env python3 -""" -Generates and updates snapshot test cases from packse scenarios. - -Important: - - This script is the backend called by `./scripts/sync_scenarios.sh`, consider using that - if not developing scenarios. - -Requirements: - - $ uv pip install -r scripts/scenarios/pylock.toml - - 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 --no-hash - - Override the uv package index and update the tests - - $ UV_TEST_PACKSE_INDEX="http://localhost:3141" ./scripts/scenarios/generate.py - - If an editable version of packse is installed, this script will use its bundled scenarios by default. - -""" - -import argparse -import importlib.metadata -import logging -import os -import re -import subprocess -import sys -import textwrap -from enum import StrEnum, auto -from pathlib import Path -from typing import Any - -TOOL_ROOT = Path(__file__).parent -TEMPLATES = TOOL_ROOT / "templates" -PACKSE = TOOL_ROOT / "packse-scenarios" -REQUIREMENTS = TOOL_ROOT / "pylock.toml" -PROJECT_ROOT = TOOL_ROOT.parent.parent -TESTS = PROJECT_ROOT / "crates" / "uv" / "tests" / "it" -TESTS_COMMON_MOD_RS = PROJECT_ROOT / "crates" / "uv-test" / "src" / "lib.rs" - -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) - - -class TemplateKind(StrEnum): - install = auto() - compile = auto() - lock = auto() - - def template_file(self) -> Path: - return TEMPLATES / f"{self.name}.mustache" - - def test_file(self) -> Path: - match self.value: - case TemplateKind.install: - return TESTS / "pip_install_scenarios.rs" - case TemplateKind.compile: - return TESTS / "pip_compile_scenarios.rs" - case TemplateKind.lock: - return TESTS / "lock_scenarios.rs" - case _: - raise NotImplementedError() - - -def main( - scenarios: list[Path], - template_kinds: list[TemplateKind], - snapshot_update: bool = True, -): - # Fetch packse version - packse_version = importlib.metadata.version("packse") - - debug = logging.getLogger().getEffectiveLevel() <= logging.DEBUG - - # Don't update the version to `0.0.0` to preserve the `UV_TEST_PACKSE_URL` - # in local tests. - if packse_version != "0.0.0": - update_common_mod_rs(packse_version) - - 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] - 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")) - targets.extend(target.glob("**/*.toml")) - targets.extend(target.glob("**/*.yaml")) - else: - targets.append(target) - - logging.info("Loading scenario metadata...") - data = packse.inspect.variables_for_templates( - targets=targets, - no_hash=True, - ) - - data["scenarios"] = [ - scenario - for scenario in data["scenarios"] - # Drop example scenarios - if not scenario["name"].startswith("example") - ] - - # We have a mixture of long singe-line descriptions (json scenarios) we need to - # wrap and manually formatted markdown in toml and yaml scenarios we want to - # preserve. - for scenario in data["scenarios"]: - if scenario["_textwrap"]: - scenario["description"] = textwrap.wrap(scenario["description"], width=80) - else: - scenario["description"] = scenario["description"].splitlines() - # Don't drop empty lines like chevron would. - scenario["description"] = "\n/// ".join(scenario["description"]) - - # Apply the same wrapping to the expected explanation - for scenario in data["scenarios"]: - expected = scenario["expected"] - if explanation := expected["explanation"]: - if scenario["_textwrap"]: - expected["explanation"] = textwrap.wrap(explanation, width=80) - else: - expected["explanation"] = explanation.splitlines() - expected["explanation"] = "\n// ".join(expected["explanation"]) - - # Hack to track which scenarios require a specific Python patch version - for scenario in data["scenarios"]: - if "patch" in scenario["name"]: - scenario["python_patch"] = True - else: - scenario["python_patch"] = False - - # Split scenarios into `install`, `compile` and `lock` cases - install_scenarios = [] - compile_scenarios = [] - lock_scenarios = [] - - for scenario in data["scenarios"]: - resolver_options = scenario["resolver_options"] or {} - # Avoid writing the empty `required-environments = []` - resolver_options["has_required_environments"] = bool( - resolver_options.get("required_environments", []) - ) - if resolver_options.get("universal"): - lock_scenarios.append(scenario) - elif resolver_options.get("python") is not None: - compile_scenarios.append(scenario) - else: - install_scenarios.append(scenario) - - template_kinds_and_scenarios: list[tuple[TemplateKind, list[Any]]] = [ - (TemplateKind.install, install_scenarios), - (TemplateKind.compile, compile_scenarios), - (TemplateKind.lock, lock_scenarios), - ] - for template_kind, scenarios in template_kinds_and_scenarios: - if template_kind not in template_kinds: - continue - - data = {"scenarios": scenarios} - - ref = "HEAD" if packse_version == "0.0.0" else packse_version - - # Add generated metadata - data["generated_from"] = ( - f"https://github.com/astral-sh/packse/tree/{ref}/scenarios" - ) - data["generated_with"] = "./scripts/sync_scenarios.sh" - data["vendor_links"] = ( - f"https://raw.githubusercontent.com/astral-sh/packse/{ref}/vendor/links.html" - ) - - data["index_url"] = ( - os.environ.get( - "UV_TEST_PACKSE_INDEX", - f"https://astral-sh.github.io/packse/{ref}", - ) - + "/simple-html" - ) - - # Render the template - logging.info(f"Rendering template {template_kind.name}") - output = chevron_blue.render( - template=template_kind.template_file().read_text(), - data=data, - no_escape=True, - warn=True, - ) - - # Update the test files - logging.info( - f"Updating test file at `{template_kind.test_file().relative_to(PROJECT_ROOT)}`...", - ) - with open(template_kind.test_file(), "w") as test_file: - test_file.write(output) - - # Format - logging.info( - "Formatting test file...", - ) - subprocess.check_call( - ["rustfmt", template_kind.test_file()], - stderr=subprocess.STDOUT, - stdout=sys.stderr if debug else subprocess.DEVNULL, - ) - - # Update snapshots - if snapshot_update: - logging.info("Updating snapshots...") - env = os.environ.copy() - command = [ - "cargo", - "insta", - "test", - "--features", - "test-pypi,test-python,test-python-patch", - "--accept", - "--test-runner", - "nextest", - "--test", - "it", - "--", - template_kind.test_file().with_suffix("").name, - ] - logging.debug(f"Running {' '.join(command)}") - exit_code = subprocess.call( - command, - cwd=PROJECT_ROOT, - stderr=subprocess.STDOUT, - stdout=sys.stderr if debug else subprocess.DEVNULL, - env=env, - ) - if exit_code != 0: - logging.warning( - f"Snapshot update failed with exit code {exit_code} (use -v to show details)" - ) - else: - logging.info("Skipping snapshot update") - - logging.info("Done!") - - -def update_common_mod_rs(packse_version: str): - """Update the value of `PACKSE_VERSION` used in non-scenario tests. - - Example: - ```rust - pub const PACKSE_VERSION: &str = "0.3.30"; - ``` - """ - test_common = TESTS_COMMON_MOD_RS.read_text() - before_version = 'pub const PACKSE_VERSION: &str = "' - after_version = '";' - build_vendor_links_url = f"{before_version}{packse_version}{after_version}" - if build_vendor_links_url in test_common: - logging.info(f"Up-to-date: {TESTS_COMMON_MOD_RS}") - else: - logging.info(f"Updating: {TESTS_COMMON_MOD_RS}") - url_matcher = re.compile( - re.escape(before_version) + '[^"]+' + re.escape(after_version) - ) - assert len(url_matcher.findall(test_common)) == 1, ( - f"PACKSE_VERSION not found in {TESTS_COMMON_MOD_RS}" - ) - test_common = url_matcher.sub(build_vendor_links_url, test_common) - TESTS_COMMON_MOD_RS.write_text(test_common) - - -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( - "--templates", - type=TemplateKind, - choices=list(TemplateKind), - default=list(TemplateKind), - nargs="*", - help="The templates to render. By default, all templates are rendered", - ) - 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, args.templates, snapshot_update=not args.no_snapshot_update) diff --git a/scripts/scenarios/pylock.toml b/scripts/scenarios/pylock.toml deleted file mode 100644 index 7a51ed6840..0000000000 --- a/scripts/scenarios/pylock.toml +++ /dev/null @@ -1,173 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile --group scripts/scenarios/pyproject.toml:packse -o scripts/scenarios/pylock.toml --universal -p 3.12 --refresh-package packse -lock-version = "1.0" -created-by = "uv" -requires-python = ">=3.12" - -[[packages]] -name = "chevron-blue" -version = "0.3.0" -sdist = { url = "https://files.pythonhosted.org/packages/18/05/2110389caf5789648f83c4374d9aa83215a50bb028bfb5139ccb51a8e3e3/chevron_blue-0.3.0.tar.gz", upload-time = 2025-07-16T16:56:23Z, size = 7730, hashes = { sha256 = "7099e1b8fa8d8e81beea31fcc448091f845de3533da1a36eea0c19327ab8521d" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/36/94/1ffcf1651c4ca3b7ecf8d20cc4e2ce13298f264ea31fa77bc53c0b5ddf56/chevron_blue-0.3.0-py3-none-any.whl", upload-time = 2025-07-16T16:56:21Z, size = 9829, hashes = { sha256 = "7ebea4ae16a410cc0416e98a158b0fad615173f67764cfea55bd24776bb0cf29" } }] - -[[packages]] -name = "hatchling" -version = "1.27.0" -sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", upload-time = 2024-12-15T17:08:11Z, size = 54983, hashes = { sha256 = "971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", upload-time = 2024-12-15T17:08:10Z, size = 75794, hashes = { sha256 = "d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b" } }] - -[[packages]] -name = "msgspec" -version = "0.19.0" -sdist = { url = "https://files.pythonhosted.org/packages/cf/9b/95d8ce458462b8b71b8a70fa94563b2498b89933689f3a7b8911edfae3d7/msgspec-0.19.0.tar.gz", upload-time = 2024-12-27T17:40:28Z, size = 216934, hashes = { sha256 = "604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e" } } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/40/817282b42f58399762267b30deb8ac011d8db373f8da0c212c85fbe62b8f/msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2024-12-27T17:39:13Z, size = 190019, hashes = { sha256 = "d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259" } }, - { url = "https://files.pythonhosted.org/packages/92/99/bd7ed738c00f223a8119928661167a89124140792af18af513e6519b0d54/msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2024-12-27T17:39:17Z, size = 183680, hashes = { sha256 = "0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36" } }, - { url = "https://files.pythonhosted.org/packages/e5/27/322badde18eb234e36d4a14122b89edd4e2973cdbc3da61ca7edf40a1ccd/msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-12-27T17:39:19Z, size = 209334, hashes = { sha256 = "fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947" } }, - { url = "https://files.pythonhosted.org/packages/c6/65/080509c5774a1592b2779d902a70b5fe008532759927e011f068145a16cb/msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-12-27T17:39:21Z, size = 211551, hashes = { sha256 = "00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909" } }, - { url = "https://files.pythonhosted.org/packages/6f/2e/1c23c6b4ca6f4285c30a39def1054e2bee281389e4b681b5e3711bd5a8c9/msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2024-12-27T17:39:24Z, size = 215099, hashes = { sha256 = "3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a" } }, - { url = "https://files.pythonhosted.org/packages/83/fe/95f9654518879f3359d1e76bc41189113aa9102452170ab7c9a9a4ee52f6/msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2024-12-27T17:39:27Z, size = 218211, hashes = { sha256 = "2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633" } }, - { url = "https://files.pythonhosted.org/packages/79/f6/71ca7e87a1fb34dfe5efea8156c9ef59dd55613aeda2ca562f122cd22012/msgspec-0.19.0-cp310-cp310-win_amd64.whl", upload-time = 2024-12-27T17:39:29Z, size = 186174, hashes = { sha256 = "695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90" } }, - { url = "https://files.pythonhosted.org/packages/24/d4/2ec2567ac30dab072cce3e91fb17803c52f0a37aab6b0c24375d2b20a581/msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2024-12-27T17:39:32Z, size = 187939, hashes = { sha256 = "aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e" } }, - { url = "https://files.pythonhosted.org/packages/2b/c0/18226e4328897f4f19875cb62bb9259fe47e901eade9d9376ab5f251a929/msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2024-12-27T17:39:33Z, size = 182202, hashes = { sha256 = "047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551" } }, - { url = "https://files.pythonhosted.org/packages/81/25/3a4b24d468203d8af90d1d351b77ea3cffb96b29492855cf83078f16bfe4/msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-12-27T17:39:35Z, size = 209029, hashes = { sha256 = "e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7" } }, - { url = "https://files.pythonhosted.org/packages/85/2e/db7e189b57901955239f7689b5dcd6ae9458637a9c66747326726c650523/msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-12-27T17:39:36Z, size = 210682, hashes = { sha256 = "6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011" } }, - { url = "https://files.pythonhosted.org/packages/03/97/7c8895c9074a97052d7e4a1cc1230b7b6e2ca2486714eb12c3f08bb9d284/msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2024-12-27T17:39:39Z, size = 214003, hashes = { sha256 = "f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063" } }, - { url = "https://files.pythonhosted.org/packages/61/61/e892997bcaa289559b4d5869f066a8021b79f4bf8e955f831b095f47a4cd/msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2024-12-27T17:39:41Z, size = 216833, hashes = { sha256 = "45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716" } }, - { url = "https://files.pythonhosted.org/packages/ce/3d/71b2dffd3a1c743ffe13296ff701ee503feaebc3f04d0e75613b6563c374/msgspec-0.19.0-cp311-cp311-win_amd64.whl", upload-time = 2024-12-27T17:39:43Z, size = 186184, hashes = { sha256 = "70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c" } }, - { url = "https://files.pythonhosted.org/packages/b2/5f/a70c24f075e3e7af2fae5414c7048b0e11389685b7f717bb55ba282a34a7/msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2024-12-27T17:39:44Z, size = 190485, hashes = { sha256 = "f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f" } }, - { url = "https://files.pythonhosted.org/packages/89/b0/1b9763938cfae12acf14b682fcf05c92855974d921a5a985ecc197d1c672/msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2024-12-27T17:39:46Z, size = 183910, hashes = { sha256 = "43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2" } }, - { url = "https://files.pythonhosted.org/packages/87/81/0c8c93f0b92c97e326b279795f9c5b956c5a97af28ca0fbb9fd86c83737a/msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-12-27T17:39:49Z, size = 210633, hashes = { sha256 = "4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12" } }, - { url = "https://files.pythonhosted.org/packages/d0/ef/c5422ce8af73928d194a6606f8ae36e93a52fd5e8df5abd366903a5ca8da/msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-12-27T17:39:51Z, size = 213594, hashes = { sha256 = "d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc" } }, - { url = "https://files.pythonhosted.org/packages/19/2b/4137bc2ed45660444842d042be2cf5b18aa06efd2cda107cff18253b9653/msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2024-12-27T17:39:52Z, size = 214053, hashes = { sha256 = "757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c" } }, - { url = "https://files.pythonhosted.org/packages/9d/e6/8ad51bdc806aac1dc501e8fe43f759f9ed7284043d722b53323ea421c360/msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2024-12-27T17:39:55Z, size = 219081, hashes = { sha256 = "5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537" } }, - { url = "https://files.pythonhosted.org/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", upload-time = 2024-12-27T17:39:56Z, size = 187467, hashes = { sha256 = "067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0" } }, - { url = "https://files.pythonhosted.org/packages/3c/cb/2842c312bbe618d8fefc8b9cedce37f773cdc8fa453306546dba2c21fd98/msgspec-0.19.0-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2024-12-27T17:40:00Z, size = 190498, hashes = { sha256 = "f12d30dd6266557aaaf0aa0f9580a9a8fbeadfa83699c487713e355ec5f0bd86" } }, - { url = "https://files.pythonhosted.org/packages/58/95/c40b01b93465e1a5f3b6c7d91b10fb574818163740cc3acbe722d1e0e7e4/msgspec-0.19.0-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2024-12-27T17:40:04Z, size = 183950, hashes = { sha256 = "82b2c42c1b9ebc89e822e7e13bbe9d17ede0c23c187469fdd9505afd5a481314" } }, - { url = "https://files.pythonhosted.org/packages/e8/f0/5b764e066ce9aba4b70d1db8b087ea66098c7c27d59b9dd8a3532774d48f/msgspec-0.19.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-12-27T17:40:05Z, size = 210647, hashes = { sha256 = "19746b50be214a54239aab822964f2ac81e38b0055cca94808359d779338c10e" } }, - { url = "https://files.pythonhosted.org/packages/9d/87/bc14f49bc95c4cb0dd0a8c56028a67c014ee7e6818ccdce74a4862af259b/msgspec-0.19.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-12-27T17:40:10Z, size = 213563, hashes = { sha256 = "60ef4bdb0ec8e4ad62e5a1f95230c08efb1f64f32e6e8dd2ced685bcc73858b5" } }, - { url = "https://files.pythonhosted.org/packages/53/2f/2b1c2b056894fbaa975f68f81e3014bb447516a8b010f1bed3fb0e016ed7/msgspec-0.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2024-12-27T17:40:12Z, size = 213996, hashes = { sha256 = "ac7f7c377c122b649f7545810c6cd1b47586e3aa3059126ce3516ac7ccc6a6a9" } }, - { url = "https://files.pythonhosted.org/packages/aa/5a/4cd408d90d1417e8d2ce6a22b98a6853c1b4d7cb7669153e4424d60087f6/msgspec-0.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2024-12-27T17:40:14Z, size = 219087, hashes = { sha256 = "a5bc1472223a643f5ffb5bf46ccdede7f9795078194f14edd69e3aab7020d327" } }, - { url = "https://files.pythonhosted.org/packages/23/d8/f15b40611c2d5753d1abb0ca0da0c75348daf1252220e5dda2867bd81062/msgspec-0.19.0-cp313-cp313-win_amd64.whl", upload-time = 2024-12-27T17:40:16Z, size = 187432, hashes = { sha256 = "317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f" } }, - { url = "https://files.pythonhosted.org/packages/ea/d0/323f867eaec1f2236ba30adf613777b1c97a7e8698e2e881656b21871fa4/msgspec-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2024-12-27T17:40:18Z, size = 189926, hashes = { sha256 = "15c1e86fff77184c20a2932cd9742bf33fe23125fa3fcf332df9ad2f7d483044" } }, - { url = "https://files.pythonhosted.org/packages/a8/37/c3e1b39bdae90a7258d77959f5f5e36ad44b40e2be91cff83eea33c54d43/msgspec-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2024-12-27T17:40:20Z, size = 183873, hashes = { sha256 = "3b5541b2b3294e5ffabe31a09d604e23a88533ace36ac288fa32a420aa38d229" } }, - { url = "https://files.pythonhosted.org/packages/cb/a2/48f2c15c7644668e51f4dce99d5f709bd55314e47acb02e90682f5880f35/msgspec-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-12-27T17:40:21Z, size = 209272, hashes = { sha256 = "0f5c043ace7962ef188746e83b99faaa9e3e699ab857ca3f367b309c8e2c6b12" } }, - { url = "https://files.pythonhosted.org/packages/25/3c/aa339cf08b990c3f07e67b229a3a8aa31bf129ed974b35e5daa0df7d9d56/msgspec-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-12-27T17:40:22Z, size = 211396, hashes = { sha256 = "ca06aa08e39bf57e39a258e1996474f84d0dd8130d486c00bec26d797b8c5446" } }, - { url = "https://files.pythonhosted.org/packages/c7/00/c7fb9d524327c558b2803973cc3f988c5100a1708879970a9e377bdf6f4f/msgspec-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2024-12-27T17:40:24Z, size = 215002, hashes = { sha256 = "e695dad6897896e9384cf5e2687d9ae9feaef50e802f93602d35458e20d1fb19" } }, - { url = "https://files.pythonhosted.org/packages/3f/bf/d9f9fff026c1248cde84a5ce62b3742e8a63a3c4e811f99f00c8babf7615/msgspec-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2024-12-27T17:40:25Z, size = 218132, hashes = { sha256 = "3be5c02e1fee57b54130316a08fe40cca53af92999a302a6054cd451700ea7db" } }, - { url = "https://files.pythonhosted.org/packages/00/03/b92011210f79794958167a3a3ea64a71135d9a2034cfb7597b545a42606d/msgspec-0.19.0-cp39-cp39-win_amd64.whl", upload-time = 2024-12-27T17:40:27Z, size = 186301, hashes = { sha256 = "0684573a821be3c749912acf5848cce78af4298345cb2d7a8b8948a0a5a27cfe" } }, -] - -[[packages]] -name = "packaging" -version = "25.0" -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", upload-time = 2025-04-19T11:48:59Z, size = 165727, hashes = { sha256 = "d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", upload-time = 2025-04-19T11:48:57Z, size = 66469, hashes = { sha256 = "29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" } }] - -[[packages]] -name = "packse" -version = "0.3.59" -sdist = { url = "https://files.pythonhosted.org/packages/16/90/51404d8933506bd9554f607f5054f4715e0a5e1d34e4c6542580553e8b75/packse-0.3.59.tar.gz", upload-time = 2026-02-18T17:44:17Z, size = 5880109, hashes = { sha256 = "718bcca5dd1e9321f5c2918d5975ffd772f0c3b150cf29b03979677a003d73d2" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/8d/95/b4a997b57a1f46f0f7575c1c021ee15e8fc1470b6ce74010239079d1d9aa/packse-0.3.59-py3-none-any.whl", upload-time = 2026-02-18T17:44:16Z, size = 34107, hashes = { sha256 = "10a3689ce0c00805cd7f1589862ab2d8f9fd4ab38c117342c351bfb13daa5c69" } }] - -[[packages]] -name = "pathspec" -version = "0.12.1" -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", upload-time = 2023-12-10T22:30:45Z, size = 51043, hashes = { sha256 = "a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", upload-time = 2023-12-10T22:30:43Z, size = 31191, hashes = { sha256 = "a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08" } }] - -[[packages]] -name = "pluggy" -version = "1.6.0" -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", upload-time = 2025-05-15T12:30:07Z, size = 69412, hashes = { sha256 = "7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", upload-time = 2025-05-15T12:30:06Z, size = 20538, hashes = { sha256 = "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" } }] - -[[packages]] -name = "pyyaml" -version = "6.0.2" -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", upload-time = 2024-08-06T20:33:50Z, size = 130631, hashes = { sha256 = "d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e" } } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2024-08-06T20:31:40Z, size = 184199, hashes = { sha256 = "0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086" } }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2024-08-06T20:31:42Z, size = 171758, hashes = { sha256 = "29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf" } }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-08-06T20:31:44Z, size = 718463, hashes = { sha256 = "8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237" } }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2024-08-06T20:31:50Z, size = 719280, hashes = { sha256 = "7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b" } }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-08-06T20:31:52Z, size = 751239, hashes = { sha256 = "ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed" } }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", upload-time = 2024-08-06T20:31:53Z, size = 695802, hashes = { sha256 = "936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180" } }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", upload-time = 2024-08-06T20:31:55Z, size = 720527, hashes = { sha256 = "23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68" } }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", upload-time = 2024-08-06T20:31:56Z, size = 144052, hashes = { sha256 = "2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99" } }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", upload-time = 2024-08-06T20:31:58Z, size = 161774, hashes = { sha256 = "a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e" } }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2024-08-06T20:32:03Z, size = 184612, hashes = { sha256 = "cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774" } }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2024-08-06T20:32:04Z, size = 172040, hashes = { sha256 = "1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee" } }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-08-06T20:32:06Z, size = 736829, hashes = { sha256 = "5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c" } }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2024-08-06T20:32:08Z, size = 764167, hashes = { sha256 = "5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317" } }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-08-06T20:32:14Z, size = 762952, hashes = { sha256 = "3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85" } }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", upload-time = 2024-08-06T20:32:16Z, size = 735301, hashes = { sha256 = "ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" } }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", upload-time = 2024-08-06T20:32:18Z, size = 756638, hashes = { sha256 = "797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e" } }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", upload-time = 2024-08-06T20:32:19Z, size = 143850, hashes = { sha256 = "11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5" } }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", upload-time = 2024-08-06T20:32:21Z, size = 161980, hashes = { sha256 = "e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44" } }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", upload-time = 2024-08-06T20:32:25Z, size = 183873, hashes = { sha256 = "c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab" } }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2024-08-06T20:32:26Z, size = 173302, hashes = { sha256 = "ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725" } }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-08-06T20:32:28Z, size = 739154, hashes = { sha256 = "1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5" } }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2024-08-06T20:32:30Z, size = 766223, hashes = { sha256 = "9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425" } }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-08-06T20:32:31Z, size = 767542, hashes = { sha256 = "80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476" } }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", upload-time = 2024-08-06T20:32:37Z, size = 731164, hashes = { sha256 = "0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48" } }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", upload-time = 2024-08-06T20:32:38Z, size = 756611, hashes = { sha256 = "8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b" } }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", upload-time = 2024-08-06T20:32:40Z, size = 140591, hashes = { sha256 = "ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4" } }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", upload-time = 2024-08-06T20:32:41Z, size = 156338, hashes = { sha256 = "7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8" } }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2024-08-06T20:32:43Z, size = 181309, hashes = { sha256 = "efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba" } }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2024-08-06T20:32:44Z, size = 171679, hashes = { sha256 = "50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1" } }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-08-06T20:32:46Z, size = 733428, hashes = { sha256 = "0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133" } }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2024-08-06T20:32:51Z, size = 763361, hashes = { sha256 = "17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484" } }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-08-06T20:32:53Z, size = 759523, hashes = { sha256 = "70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5" } }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", upload-time = 2024-08-06T20:32:54Z, size = 726660, hashes = { sha256 = "41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc" } }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", upload-time = 2024-08-06T20:32:56Z, size = 751597, hashes = { sha256 = "68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652" } }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", upload-time = 2024-08-06T20:33:03Z, size = 140527, hashes = { sha256 = "bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183" } }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", upload-time = 2024-08-06T20:33:04Z, size = 156446, hashes = { sha256 = "8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563" } }, - { url = "https://files.pythonhosted.org/packages/74/d9/323a59d506f12f498c2097488d80d16f4cf965cee1791eab58b56b19f47a/PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", upload-time = 2024-08-06T20:33:06Z, size = 183218, hashes = { sha256 = "24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a" } }, - { url = "https://files.pythonhosted.org/packages/74/cc/20c34d00f04d785f2028737e2e2a8254e1425102e730fee1d6396f832577/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-08-06T20:33:07Z, size = 728067, hashes = { sha256 = "d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5" } }, - { url = "https://files.pythonhosted.org/packages/20/52/551c69ca1501d21c0de51ddafa8c23a0191ef296ff098e98358f69080577/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2024-08-06T20:33:12Z, size = 757812, hashes = { sha256 = "d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d" } }, - { url = "https://files.pythonhosted.org/packages/fd/7f/2c3697bba5d4aa5cc2afe81826d73dfae5f049458e44732c7a0938baa673/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-08-06T20:33:14Z, size = 746531, hashes = { sha256 = "9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083" } }, - { url = "https://files.pythonhosted.org/packages/8c/ab/6226d3df99900e580091bb44258fde77a8433511a86883bd4681ea19a858/PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", upload-time = 2024-08-06T20:33:16Z, size = 800820, hashes = { sha256 = "82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706" } }, - { url = "https://files.pythonhosted.org/packages/a0/99/a9eb0f3e710c06c5d922026f6736e920d431812ace24aae38228d0d64b04/PyYAML-6.0.2-cp38-cp38-win32.whl", upload-time = 2024-08-06T20:33:22Z, size = 145514, hashes = { sha256 = "43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a" } }, - { url = "https://files.pythonhosted.org/packages/75/8a/ee831ad5fafa4431099aa4e078d4c8efd43cd5e48fbc774641d233b683a9/PyYAML-6.0.2-cp38-cp38-win_amd64.whl", upload-time = 2024-08-06T20:33:23Z, size = 162702, hashes = { sha256 = "01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff" } }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2024-08-06T20:33:25Z, size = 184777, hashes = { sha256 = "688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d" } }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2024-08-06T20:33:27Z, size = 172318, hashes = { sha256 = "a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f" } }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-08-06T20:33:28Z, size = 720891, hashes = { sha256 = "d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290" } }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2024-08-06T20:33:34Z, size = 722614, hashes = { sha256 = "f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12" } }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-08-06T20:33:35Z, size = 737360, hashes = { sha256 = "3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19" } }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", upload-time = 2024-08-06T20:33:37Z, size = 699006, hashes = { sha256 = "0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e" } }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", upload-time = 2024-08-06T20:33:39Z, size = 723577, hashes = { sha256 = "a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725" } }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", upload-time = 2024-08-06T20:33:46Z, size = 144593, hashes = { sha256 = "6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631" } }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", upload-time = 2024-08-06T20:33:49Z, size = 162312, hashes = { sha256 = "39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8" } }, -] - -[[packages]] -name = "trove-classifiers" -version = "2025.9.11.17" -sdist = { url = "https://files.pythonhosted.org/packages/ca/9a/778622bc06632529817c3c524c82749a112603ae2bbcf72ee3eb33a2c4f1/trove_classifiers-2025.9.11.17.tar.gz", upload-time = 2025-09-11T17:07:50Z, size = 16975, hashes = { sha256 = "931ca9841a5e9c9408bc2ae67b50d28acf85bef56219b56860876dd1f2d024dd" } } -wheels = [{ url = "https://files.pythonhosted.org/packages/e1/85/a4ff8758c66f1fc32aa5e9a145908394bf9cf1c79ffd1113cfdeb77e74e4/trove_classifiers-2025.9.11.17-py3-none-any.whl", upload-time = 2025-09-11T17:07:49Z, size = 14158, hashes = { sha256 = "5d392f2d244deb1866556457d6f3516792124a23d1c3a463a2e8668a5d1c15dd" } }] - -[[packages]] -name = "uv" -version = "0.8.17" -sdist = { url = "https://files.pythonhosted.org/packages/6f/4c/c270c6b8ed3e8c7fe38ea0b99df9eff09c332421b93d55a158371f75220e/uv-0.8.17.tar.gz", upload-time = 2025-09-10T21:51:25Z, size = 3615060, hashes = { sha256 = "2afd4525a53c8ab3a11a5a15093c503d27da67e76257a649b05e4f0bc2ebb5ae" } } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/7d/bbaa45c88b2c91e02714a8a5c9e787c47e4898bddfdd268569163492ba45/uv-0.8.17-py3-none-linux_armv6l.whl", upload-time = 2025-09-10T21:50:18Z, size = 20242144, hashes = { sha256 = "c51c9633ca93ef63c07df2443941e6264efd2819cc9faabfd9fe11899c6a0d6a" } }, - { url = "https://files.pythonhosted.org/packages/65/34/609b72034df0c62bcfb0c0ad4b11e2b55e537c0f0817588b5337d3dcca71/uv-0.8.17-py3-none-macosx_10_12_x86_64.whl", upload-time = 2025-09-10T21:50:22Z, size = 19363081, hashes = { sha256 = "c28fba6d7bb5c34ade2c8da5000faebe8425a287f42a043ca01ceb24ebc81590" } }, - { url = "https://files.pythonhosted.org/packages/b6/bc/9417df48f0c18a9d54c2444096e03f2f56a3534c5b869f50ac620729cbc8/uv-0.8.17-py3-none-macosx_11_0_arm64.whl", upload-time = 2025-09-10T21:50:25Z, size = 17943513, hashes = { sha256 = "b009f1ec9e28de00f76814ad66e35aaae82c98a0f24015de51943dcd1c2a1895" } }, - { url = "https://files.pythonhosted.org/packages/63/1c/14fd54c852fd592a2b5da4b7960f3bf4a15c7e51eb20eaddabe8c8cca32d/uv-0.8.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", upload-time = 2025-09-10T21:50:29Z, size = 19507222, hashes = { sha256 = "84d56ae50ca71aec032577adf9737974554a82a94e52cee57722745656c1d383" } }, - { url = "https://files.pythonhosted.org/packages/be/47/f6a68cc310feca37c965bcbd57eb999e023d35eaeda9c9759867bf3ed232/uv-0.8.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-10T21:50:32Z, size = 19865652, hashes = { sha256 = "85c2140f8553b9a4387a7395dc30cd151ef94046785fe8b198f13f2c380fb39b" } }, - { url = "https://files.pythonhosted.org/packages/ab/6a/fdeb2d4a2635a6927c6d549b07177bcaf6ce15bdef58e8253e75c1b70f54/uv-0.8.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-09-10T21:50:37Z, size = 20831760, hashes = { sha256 = "2076119783e4a6d3c9e25638956cb123f0eabf4d7d407d9661cdf7f84818dcb9" } }, - { url = "https://files.pythonhosted.org/packages/d0/4c/bd58b8a76015aa9ac49d6b4e1211ae1ca98a0aade0c49e1a5f645fb5cd38/uv-0.8.17-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", upload-time = 2025-09-10T21:50:41Z, size = 22209056, hashes = { sha256 = "707a55660d302924fdbcb509e63dfec8842e19d35b69bcc17af76c25db15ad6f" } }, - { url = "https://files.pythonhosted.org/packages/7e/2e/28f59c00a2ed6532502fb1e27da9394e505fb7b41cc0274475104b43561b/uv-0.8.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-10T21:50:45Z, size = 21871684, hashes = { sha256 = "1824b76911a14aaa9eee65ad9e180e6a4d2d7c86826232c2f28ae86aee56ed0e" } }, - { url = "https://files.pythonhosted.org/packages/5a/1d/a8a4fc08de1f767316467e7a1989bb125734b7ed9cd98ce8969386a70653/uv-0.8.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-10T21:50:50Z, size = 21145154, hashes = { sha256 = "bb9b515cc813fb1b08f1e7592f76e437e2fb44945e53cde4fee11dee3b16d0c3" } }, - { url = "https://files.pythonhosted.org/packages/8f/35/cb47d2d07a383c07b0e5043c6fe5555f0fd79683c6d7f9760222987c8be9/uv-0.8.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-10T21:50:54Z, size = 21106619, hashes = { sha256 = "b6d30d02fb65193309fc12a20f9e1a9fab67f469d3e487a254ca1145fd06788f" } }, - { url = "https://files.pythonhosted.org/packages/6e/93/c310f0153b9dfe79bdd7f7eaef6380a8545c8939dbfc4e6bdee8f3ee7050/uv-0.8.17-py3-none-manylinux_2_28_aarch64.whl", upload-time = 2025-09-10T21:50:57Z, size = 19777591, hashes = { sha256 = "3941cecd9a6a46d3d4505753912c9cf3e8ae5eea30b9d0813f3656210f8c5d01" } }, - { url = "https://files.pythonhosted.org/packages/6c/4f/971d3c84c2f09cf8df4536c33644e6b97e10a259d8630a0c1696c1fa6e94/uv-0.8.17-py3-none-manylinux_2_31_riscv64.whl", upload-time = 2025-09-10T21:51:01Z, size = 20845039, hashes = { sha256 = "cd0ad366cfe4cbe9212bd660b5b9f3a827ff35a7601cefdac2d153bfc8079eb7" } }, - { url = "https://files.pythonhosted.org/packages/4a/29/8ad9038e75cb91f54b81cc933dd14fcfa92fa6f8706117d43d4251a8a662/uv-0.8.17-py3-none-musllinux_1_1_armv7l.whl", upload-time = 2025-09-10T21:51:04Z, size = 19820370, hashes = { sha256 = "505854bc75c497b95d2c65590291dc820999a4a7d9dfab4f44a9434a6cff7b5f" } }, - { url = "https://files.pythonhosted.org/packages/f2/c9/fc8482d1e7dfe187c6e03dcefbac0db41a5dd72aa7b017c0f80f91a04444/uv-0.8.17-py3-none-musllinux_1_1_i686.whl", upload-time = 2025-09-10T21:51:08Z, size = 20289951, hashes = { sha256 = "dc479f661da449df37d68b36fdffa641e89fb53ad38c16a5c9f98f3211785b63" } }, - { url = "https://files.pythonhosted.org/packages/2d/84/ad878ed045f02aa973be46636c802d494f8270caf5ea8bd04b7bbc68aa23/uv-0.8.17-py3-none-musllinux_1_1_x86_64.whl", upload-time = 2025-09-10T21:51:12Z, size = 21234644, hashes = { sha256 = "a1d11cd805be6d137ffef4a8227905f87f459031c645ac5031c30a3bcd08abd6" } }, - { url = "https://files.pythonhosted.org/packages/f8/03/3fa2641513922988e641050b3adbc87de527f44c2cc8328510703616be6a/uv-0.8.17-py3-none-win32.whl", upload-time = 2025-09-10T21:51:16Z, size = 19216757, hashes = { sha256 = "d13a616eb0b2b33c7aa09746cc85860101d595655b58653f0b499af19f33467c" } }, - { url = "https://files.pythonhosted.org/packages/1a/c4/0082f437bac162ab95e5a3a389a184c122d45eb5593960aab92fdf80374b/uv-0.8.17-py3-none-win_amd64.whl", upload-time = 2025-09-10T21:51:19Z, size = 21125811, hashes = { sha256 = "cf85b84b81b41d57a9b6eeded8473ec06ace8ee959ad0bb57e102b5ad023bd34" } }, - { url = "https://files.pythonhosted.org/packages/50/a2/29f57b118b3492c9d5ab1a99ba4906e7d7f8b658881d31bc2c4408d64d07/uv-0.8.17-py3-none-win_arm64.whl", upload-time = 2025-09-10T21:51:22Z, size = 19564631, hashes = { sha256 = "64d649a8c4c3732b05dc712544963b004cf733d95fdc5d26f43c5493553ff0a7" } }, -] diff --git a/scripts/scenarios/pyproject.toml b/scripts/scenarios/pyproject.toml deleted file mode 100644 index 869a09e06f..0000000000 --- a/scripts/scenarios/pyproject.toml +++ /dev/null @@ -1,5 +0,0 @@ -[dependency-groups] -packse = [ - "chevron-blue", - "packse>=0.3.59" -] diff --git a/scripts/scenarios/templates/compile.mustache b/scripts/scenarios/templates/compile.mustache deleted file mode 100644 index 066357a715..0000000000 --- a/scripts/scenarios/templates/compile.mustache +++ /dev/null @@ -1,106 +0,0 @@ -//! DO NOT EDIT -//! -//! Generated with `{{generated_with}}` -//! Scenarios from <{{generated_from}}> -//! -#![cfg(all(feature = "test-python", feature = "test-pypi", unix))] - -use std::env; -use std::process::Command; - -use anyhow::Result; -use assert_cmd::assert::OutputAssertExt; -use assert_fs::fixture::{FileWriteStr, PathChild}; -use predicates::prelude::predicate; - -use uv_static::EnvVars; - -use uv_test::{ - TestContext, build_vendor_links_url, get_bin, packse_index_url, python_path_with_versions, - uv_snapshot, -}; - -/// Provision python binaries and return a `pip compile` command with options shared across all scenarios. -fn command(context: &TestContext, python_versions: &[&str]) -> Command { - let python_path = python_path_with_versions(&context.temp_dir, python_versions) - .expect("Failed to create Python test path"); - let mut command = Command::new(get_bin!()); - command - .arg("pip") - .arg("compile") - .arg("requirements.in") - .arg("--index-url") - .arg(packse_index_url()) - .arg("--find-links") - .arg(build_vendor_links_url()); - context.add_shared_options(&mut command, true); - command.env_remove(EnvVars::UV_EXCLUDE_NEWER); - command.env(EnvVars::UV_PYTHON_SEARCH_PATH, python_path); - - command -} - -{{#scenarios}} - -/// {{description}} -/// -/// ```text -/// {{name}} -{{#tree}} -/// {{.}} -{{/tree}} -/// ``` -{{#python_patch}} -#[cfg(feature = "test-python-patch")] -{{/python_patch}} -#[test] -fn {{module_name}}() -> Result<()> { - let context = uv_test::test_context!("{{environment.python}}"); - let python_versions = &[{{#environment.additional_python}}"{{.}}", {{/environment.additional_python}}]; - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"{{name}}-", "package-")); - - let requirements_in = context.temp_dir.child("requirements.in"); - {{#root.requires}} - requirements_in.write_str("{{requirement}}")?; - {{/root.requires}} - - {{#expected.explanation}} - // {{expected.explanation}} - {{/expected.explanation}} - let output = uv_snapshot!(filters, command(&context, python_versions) - {{#resolver_options.prereleases}} - .arg("--prerelease=allow") - {{/resolver_options.prereleases}} - {{#resolver_options.no_build}} - .arg("--only-binary") - .arg("{{.}}") - {{/resolver_options.no_build}} - {{#resolver_options.no_binary}} - .arg("--no-binary") - .arg("{{.}}") - {{/resolver_options.no_binary}} - {{#resolver_options.python}} - .arg("--python-version={{.}}") - {{/resolver_options.python}}, @r###" - "### - ); - - output - .assert() - {{#expected.satisfiable}} - .success() - {{#expected.packages}} - .stdout(predicate::str::contains("{{name}}=={{version}}")) - {{/expected.packages}} - {{/expected.satisfiable}} - {{^expected.satisfiable}} - .failure() - {{/expected.satisfiable}} - ; - - Ok(()) -} -{{/scenarios}} diff --git a/scripts/scenarios/templates/install.mustache b/scripts/scenarios/templates/install.mustache deleted file mode 100644 index a98bfb90aa..0000000000 --- a/scripts/scenarios/templates/install.mustache +++ /dev/null @@ -1,81 +0,0 @@ -//! DO NOT EDIT -//! -//! Generated with `{{generated_with}}` -//! Scenarios from <{{generated_from}}> -//! -#![cfg(all(feature = "test-python", feature = "test-pypi", unix))] - -use std::process::Command; - -use uv_static::EnvVars; - -use uv_test::{TestContext, build_vendor_links_url, packse_index_url, uv_snapshot}; - -/// Create a `pip install` command with options shared across all scenarios. -fn command(context: &TestContext) -> Command { - let mut command = context.pip_install(); - command - .arg("--index-url") - .arg(packse_index_url()) - .arg("--find-links") - .arg(build_vendor_links_url()); - command.env_remove(EnvVars::UV_EXCLUDE_NEWER); - command -} - -{{#scenarios}} - -/// {{description}} -/// -/// ```text -/// {{name}} -{{#tree}} -/// {{.}} -{{/tree}} -/// ``` -{{#python_patch}} -#[cfg(feature = "test-python-patch")] -{{/python_patch}} -#[test] -fn {{module_name}}() { - let context = uv_test::test_context!("{{environment.python}}"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"{{name}}-", "package-")); - - uv_snapshot!(filters, command(&context) - {{#resolver_options.prereleases}} - .arg("--prerelease=allow") - {{/resolver_options.prereleases}} - {{#resolver_options.no_build}} - .arg("--only-binary") - .arg("{{.}}") - {{/resolver_options.no_build}} - {{#resolver_options.no_binary}} - .arg("--no-binary") - .arg("{{.}}") - {{/resolver_options.no_binary}} - {{#resolver_options.python_platform}} - .arg("--python-platform={{.}}") - {{/resolver_options.python_platform}} - {{#root.requires}} - .arg("{{requirement}}") - {{/root.requires}}, @r#" - "#); - - {{#expected.explanation}} - // {{expected.explanation}} - {{/expected.explanation}} - {{#expected.satisfiable}} - {{#expected.packages}} - context.assert_installed("{{module_name}}", "{{version}}"); - {{/expected.packages}} - {{/expected.satisfiable}} - {{^expected.satisfiable}} - {{#root.requires}} - context.assert_not_installed("{{module_name}}"); - {{/root.requires}} - {{/expected.satisfiable}} -} -{{/scenarios}} diff --git a/scripts/scenarios/templates/lock.mustache b/scripts/scenarios/templates/lock.mustache deleted file mode 100644 index 4082b582e0..0000000000 --- a/scripts/scenarios/templates/lock.mustache +++ /dev/null @@ -1,96 +0,0 @@ -//! DO NOT EDIT -//! -//! Generated with `{{generated_with}}` -//! Scenarios from <{{generated_from}}> -//! -#![cfg(all(feature = "test-python", feature = "test-pypi"))] -#![expect(clippy::needless_raw_string_hashes)] -#![expect(clippy::doc_markdown)] - -use anyhow::Result; -use assert_cmd::assert::OutputAssertExt; -use assert_fs::prelude::*; -use insta::assert_snapshot; - -use uv_static::EnvVars; - -use uv_test::{packse_index_url, uv_snapshot}; - -{{#scenarios}} - -/// {{description}} -/// -/// ```text -/// {{name}} -{{#tree}} -/// {{.}} -{{/tree}} -/// ``` -#[test] -fn {{module_name}}() -> Result<()> { - let context = uv_test::test_context!("{{environment.python}}"); - - // In addition to the standard filters, swap out package names for shorter messages - let mut filters = context.filters(); - filters.push((r"{{name}}-", "package-")); - - let pyproject_toml = context.temp_dir.child("pyproject.toml"); - pyproject_toml.write_str( - r###" - [project] - name = "project" - version = "0.1.0" - dependencies = [ - {{#root.requires}} - '''{{requirement}}''', - {{/root.requires}} - ] - {{#root.requires_python}} - requires-python = "{{.}}" - {{/root.requires_python}} - {{#resolver_options.has_required_environments}} - [tool.uv] - required-environments = [ - {{#resolver_options.required_environments}} - '''{{.}}''', - {{/resolver_options.required_environments}} - ] - {{/resolver_options.has_required_environments}} - "### - )?; - - let mut cmd = context.lock(); - cmd.env_remove(EnvVars::UV_EXCLUDE_NEWER); - cmd.arg("--index-url").arg(packse_index_url()); - {{#expected.explanation}} - // {{expected.explanation}} - {{/expected.explanation}} - uv_snapshot!(filters, cmd, @r###" - "### - ); - - {{#expected.satisfiable}} - let lock = context.read("uv.lock"); - insta::with_settings!({ - filters => filters, - }, { - assert_snapshot!( - lock, @r###" - "### - ); - }); - - // Assert the idempotence of `uv lock` when resolving from the lockfile (`--locked`). - context - .lock() - .arg("--locked") - .env_remove(EnvVars::UV_EXCLUDE_NEWER) - .arg("--index-url") - .arg(packse_index_url()) - .assert() - .success(); - {{/expected.satisfiable}} - - Ok(()) -} -{{/scenarios}} diff --git a/scripts/sync_scenarios.sh b/scripts/sync_scenarios.sh deleted file mode 100755 index 18a82427f7..0000000000 --- a/scripts/sync_scenarios.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/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/sync_scenarios.sh -# -# Additional arguments are passed to `./scripts/scenarios/generate.py`, for example: -# -# $ ./scripts/sync_scenarios.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. -# -# To update the packse version, run the following command first: -# -# $ uv pip compile --group scripts/scenarios/pyproject.toml:packse -o scripts/scenarios/pylock.toml --upgrade-package packse --universal -p 3.12 -# -# See `scripts/scenarios/` for supporting files. -set -eu - -script_root="$(realpath "$(dirname "$0")")" - - -cd "$script_root/scenarios" -echo "Setting up a temporary environment..." -uv venv -p 3.12 -c - -# shellcheck disable=SC1091 -source ".venv/bin/activate" -uv pip install -r "$script_root/scenarios/pylock.toml" --refresh-package packse - -echo "Fetching packse scenarios..." -packse fetch --dest "$script_root/scenarios/.downloads" --force - -unset VIRTUAL_ENV # Avoid warning due to venv mismatch -.venv/bin/python "$script_root/scenarios/generate.py" "$script_root/scenarios/.downloads" "$@" - -# Cleanup -rm -r "$script_root/scenarios/.downloads" diff --git a/test/scenarios/backtracking/backtrack-to-missing-package.toml b/test/scenarios/backtracking/backtrack-to-missing-package.toml new file mode 100644 index 0000000000..75f08c6ab4 --- /dev/null +++ b/test/scenarios/backtracking/backtrack-to-missing-package.toml @@ -0,0 +1,23 @@ +name = "backtrack-to-missing-package" +description = """There are two packages, `a` and `b`. All versions of `b` require a specific +version of `a`, but that version requires a package `c` that does not exist. The resolver +must backtrack through all versions of `b` and eventually fail because no solution exists.""" + +[expected] +satisfiable = false + +[root] +requires = ["a", "b"] + +[packages.a.versions] +# This version works but is incompatible with all versions of b +"2.0.0" = {} +# This version is required by b but depends on a missing package +"1.0.0" = { requires = ["c"] } + +[packages.b.versions] +"1.0.0" = { requires = ["a==1.0.0"] } +"2.0.0" = { requires = ["a==1.0.0"] } +"3.0.0" = { requires = ["a==1.0.0"] } + +# Note: package `c` is intentionally not defined (missing package) diff --git a/test/scenarios/backtracking/backtrack-with-missing-package.toml b/test/scenarios/backtracking/backtrack-with-missing-package.toml new file mode 100644 index 0000000000..e1413e3759 --- /dev/null +++ b/test/scenarios/backtracking/backtrack-with-missing-package.toml @@ -0,0 +1,27 @@ +name = "backtrack-with-missing-package" +description = """There are two packages, `a` and `b`. The latest version of `b` requires +a specific version of `a`. The older version of `b` requires a package `c` that does not +exist. The resolver should backtrack on `a` (not `b`) to find a solution without needing +to try `b==1.0.0` which would fail due to the missing package.""" + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" +b = "2.0.0" + +[root] +requires = ["a", "b"] + +[packages.a.versions] +"1.0.0" = {} +"2.0.0" = {} + +[packages.b.versions] +# Old version requires a missing package - resolver should not need this +"1.0.0" = { requires = ["c"] } +# New version requires specific a version - should work with backtracking +"2.0.0" = { requires = ["a==1.0.0"] } + +# Note: package `c` is intentionally not defined (missing package) diff --git a/test/scenarios/backtracking/wrong-backtracking-basic.toml b/test/scenarios/backtracking/wrong-backtracking-basic.toml new file mode 100644 index 0000000000..f1705e5a36 --- /dev/null +++ b/test/scenarios/backtracking/wrong-backtracking-basic.toml @@ -0,0 +1,38 @@ +name = "wrong-backtracking-basic" +description = """There are two packages, `a` and `b`. We select `a` with `a==2.0.0` first, and then `b`, but `a==2.0.0` conflicts with all new versions of `b`, so we backtrack through versions of `b`. + +We need to detect this conflict and prioritize `b` over `a` instead of backtracking down to the too old version of `b==1.0.0` that doesn't depend on `a` anymore.""" + +[expected] +satisfiable = true + +[resolver_options] +universal = true + +[expected.packages] +a = "1.0.0" +b = "2.0.9" + +[root] +requires = ["a", "b"] + +[packages.a.versions] +"1.0.0" = {} +"2.0.0" = {} + +[packages.too-old.versions] +"1.0.0" = {} + +[packages.b.versions] +# We must not backtrack to this very old versions +"1.0.0" = { requires = ["too-old"] } +"2.0.0" = { requires = ["a==1.0.0"] } +"2.0.1" = { requires = ["a==1.0.0"] } +"2.0.2" = { requires = ["a==1.0.0"] } +"2.0.3" = { requires = ["a==1.0.0"] } +"2.0.4" = { requires = ["a==1.0.0"] } +"2.0.5" = { requires = ["a==1.0.0"] } +"2.0.6" = { requires = ["a==1.0.0"] } +"2.0.7" = { requires = ["a==1.0.0"] } +"2.0.8" = { requires = ["a==1.0.0"] } +"2.0.9" = { requires = ["a==1.0.0"] } diff --git a/test/scenarios/backtracking/wrong-backtracking-indirect.toml b/test/scenarios/backtracking/wrong-backtracking-indirect.toml new file mode 100644 index 0000000000..a3f12979c3 --- /dev/null +++ b/test/scenarios/backtracking/wrong-backtracking-indirect.toml @@ -0,0 +1,45 @@ +name = "wrong-backtracking-indirect" +description = """There are three packages, `a`, `b` and `b-inner`. Unlike wrong-backtracking-basic, `b` depends on `b-inner` and `a` and `b-inner` conflict, to add a layer of indirection. + +We select `a` with `a==2.0.0` first, then `b`, and then `b-inner`, but `a==2.0.0` conflicts with all new versions of `b-inner`, so we backtrack through versions of `b-inner`. + +We need to detect this conflict and prioritize `b` and `b-inner` over `a` instead of backtracking down to the too old version of `b-inner==1.0.0` that doesn't depend on `a` anymore.""" + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +# TODO: https://github.com/astral-sh/uv/issues/12060 +# [expected.packages] +# a = "1.0.0" +# b = "1.0.0" +# b-inner = "2.0.9" + +[root] +requires = ["a", "b"] + +[packages.a.versions] +"1.0.0" = {} +"2.0.0" = {} + +[packages.too-old.versions] +"1.0.0" = {} + +[packages.b.versions] +"1.0.0" = { requires = ["b-inner"] } + +[packages.b-inner.versions] +# We must not backtrack to this very old versions +"1.0.0" = { requires = ["too-old"] } +"2.0.0" = { requires = ["a==1.0.0"] } +"2.0.1" = { requires = ["a==1.0.0"] } +"2.0.2" = { requires = ["a==1.0.0"] } +"2.0.3" = { requires = ["a==1.0.0"] } +"2.0.4" = { requires = ["a==1.0.0"] } +"2.0.5" = { requires = ["a==1.0.0"] } +"2.0.6" = { requires = ["a==1.0.0"] } +"2.0.7" = { requires = ["a==1.0.0"] } +"2.0.8" = { requires = ["a==1.0.0"] } +"2.0.9" = { requires = ["a==1.0.0"] } diff --git a/test/scenarios/does_not_exist/requires-exact-version-does-not-exist.toml b/test/scenarios/does_not_exist/requires-exact-version-does-not-exist.toml new file mode 100644 index 0000000000..fcaceaf2f1 --- /dev/null +++ b/test/scenarios/does_not_exist/requires-exact-version-does-not-exist.toml @@ -0,0 +1,10 @@ +name = "requires-exact-version-does-not-exist" +description = "The user requires an exact version of package `a` but only other versions exist" + +[root] +requires = ["a==2.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/does_not_exist/requires-greater-version-does-not-exist.toml b/test/scenarios/does_not_exist/requires-greater-version-does-not-exist.toml new file mode 100644 index 0000000000..ba1db63c47 --- /dev/null +++ b/test/scenarios/does_not_exist/requires-greater-version-does-not-exist.toml @@ -0,0 +1,12 @@ +name = "requires-greater-version-does-not-exist" +description = "The user requires a version of `a` greater than `1.0.0` but only smaller or equal versions exist" + +[root] +requires = ["a>1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."0.1.0"] + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/does_not_exist/requires-less-version-does-not-exist.toml b/test/scenarios/does_not_exist/requires-less-version-does-not-exist.toml new file mode 100644 index 0000000000..b690544c6b --- /dev/null +++ b/test/scenarios/does_not_exist/requires-less-version-does-not-exist.toml @@ -0,0 +1,14 @@ +name = "requires-less-version-does-not-exist" +description = "The user requires a version of `a` less than `1.0.0` but only larger versions exist" + +[root] +requires = ["a<2.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."2.0.0"] + +[packages.a.versions."3.0.0"] + +[packages.a.versions."4.0.0"] diff --git a/test/scenarios/does_not_exist/requires-package-does-not-exist.toml b/test/scenarios/does_not_exist/requires-package-does-not-exist.toml new file mode 100644 index 0000000000..001ae7b83a --- /dev/null +++ b/test/scenarios/does_not_exist/requires-package-does-not-exist.toml @@ -0,0 +1,10 @@ +name = "requires-package-does-not-exist" +description = "The user requires any version of package `a` which does not exist." + +[root] +requires = ["a"] + +[packages] + +[expected] +satisfiable = false diff --git a/test/scenarios/does_not_exist/transitive-requires-package-does-not-exist.toml b/test/scenarios/does_not_exist/transitive-requires-package-does-not-exist.toml new file mode 100644 index 0000000000..036b0a0138 --- /dev/null +++ b/test/scenarios/does_not_exist/transitive-requires-package-does-not-exist.toml @@ -0,0 +1,11 @@ +name = "transitive-requires-package-does-not-exist" +description = "The user requires package `a` but `a` requires package `b` which does not exist" + +[root] +requires = ["a"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["b"] diff --git a/test/scenarios/excluded/dependency-excludes-non-contiguous-range-of-compatible-versions.toml b/test/scenarios/excluded/dependency-excludes-non-contiguous-range-of-compatible-versions.toml new file mode 100644 index 0000000000..f51a230804 --- /dev/null +++ b/test/scenarios/excluded/dependency-excludes-non-contiguous-range-of-compatible-versions.toml @@ -0,0 +1,42 @@ +name = "dependency-excludes-non-contiguous-range-of-compatible-versions" +description = "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 `a` are incompatible for another reason e.g. dependency on non-existent package `d`." + +[root] +requires = ["a", "b>=2.0.0,<3.0.0", "c"] + +[expected] +satisfiable = false +explanation = "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." + +[packages.a.versions."1.0.0"] +requires = ["b==1.0.0"] + +[packages.a.versions."2.0.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."2.1.0"] +requires = ["b==2.0.0", "d"] + +[packages.a.versions."2.2.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."2.3.0"] +requires = ["b==2.0.0", "d"] + +[packages.a.versions."2.4.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."3.0.0"] +requires = ["b==3.0.0"] + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] + +[packages.b.versions."3.0.0"] + +[packages.c.versions."1.0.0"] +requires = ["a<2.0.0"] + +[packages.c.versions."2.0.0"] +requires = ["a>=3.0.0"] diff --git a/test/scenarios/excluded/dependency-excludes-range-of-compatible-versions.toml b/test/scenarios/excluded/dependency-excludes-range-of-compatible-versions.toml new file mode 100644 index 0000000000..1fe20d66e2 --- /dev/null +++ b/test/scenarios/excluded/dependency-excludes-range-of-compatible-versions.toml @@ -0,0 +1,39 @@ +name = "dependency-excludes-range-of-compatible-versions" +description = "There is a range of compatible versions for the requested package `a`, but another dependency `c` excludes that range." + +[root] +requires = ["a", "b>=2.0.0,<3.0.0", "c"] + +[expected] +satisfiable = false +explanation = "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." + +[packages.a.versions."1.0.0"] +requires = ["b==1.0.0"] + +[packages.a.versions."2.0.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."2.1.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."2.2.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."2.3.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."3.0.0"] +requires = ["b==3.0.0"] + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] + +[packages.b.versions."3.0.0"] + +[packages.c.versions."1.0.0"] +requires = ["a<2.0.0"] + +[packages.c.versions."2.0.0"] +requires = ["a>=3.0.0"] diff --git a/test/scenarios/excluded/excluded-only-compatible-version.toml b/test/scenarios/excluded/excluded-only-compatible-version.toml new file mode 100644 index 0000000000..64b45e95c9 --- /dev/null +++ b/test/scenarios/excluded/excluded-only-compatible-version.toml @@ -0,0 +1,24 @@ +name = "excluded-only-compatible-version" +description = "Only one version of the requested package `a` is compatible, but the user has banned that version." + +[root] +requires = ["a!=2.0.0", "b>=2.0.0,<3.0.0"] + +[expected] +satisfiable = false +explanation = "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." + +[packages.a.versions."1.0.0"] +requires = ["b==1.0.0"] + +[packages.a.versions."2.0.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."3.0.0"] +requires = ["b==3.0.0"] + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] + +[packages.b.versions."3.0.0"] diff --git a/test/scenarios/excluded/excluded-only-version.toml b/test/scenarios/excluded/excluded-only-version.toml new file mode 100644 index 0000000000..bcda82754a --- /dev/null +++ b/test/scenarios/excluded/excluded-only-version.toml @@ -0,0 +1,11 @@ +name = "excluded-only-version" +description = "Only one version of the requested package is available, but the user has banned that version." + +[root] +requires = ["a!=1.0.0"] + +[expected] +satisfiable = false +explanation = "Only `a==1.0.0` is available but the user excluded it." + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/extras/all-extras-required.toml b/test/scenarios/extras/all-extras-required.toml new file mode 100644 index 0000000000..013edf7450 --- /dev/null +++ b/test/scenarios/extras/all-extras-required.toml @@ -0,0 +1,22 @@ +name = "all-extras-required" +description = "Multiple optional dependencies are requested for the package via an 'all' extra." + +[root] +requires = ["a[all]"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" +b = "1.0.0" +c = "1.0.0" + +[packages.b.versions."1.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.a.versions."1.0.0".extras] +all = ["a[extra_b]", "a[extra_c]"] +extra_b = ["b"] +extra_c = ["c"] diff --git a/test/scenarios/extras/extra-does-not-exist-backtrack.toml b/test/scenarios/extras/extra-does-not-exist-backtrack.toml new file mode 100644 index 0000000000..172aa818c0 --- /dev/null +++ b/test/scenarios/extras/extra-does-not-exist-backtrack.toml @@ -0,0 +1,21 @@ +name = "extra-does-not-exist-backtrack" +description = "Optional dependencies are requested for the package, the extra is only available on an older version." + +[root] +requires = ["a[extra]"] + +[expected] +satisfiable = true +explanation = "The resolver should not backtrack to `a==1.0.0` because missing extras are allowed during resolution. `b` should not be installed." + +[expected.packages] +a = "3.0.0" + +[packages.a.versions."2.0.0"] + +[packages.a.versions."3.0.0"] + +[packages.b.versions."1.0.0"] + +[packages.a.versions."1.0.0".extras] +extra = ["b==1.0.0"] diff --git a/test/scenarios/extras/extra-incompatible-with-extra-not-requested.toml b/test/scenarios/extras/extra-incompatible-with-extra-not-requested.toml new file mode 100644 index 0000000000..915dd4d086 --- /dev/null +++ b/test/scenarios/extras/extra-incompatible-with-extra-not-requested.toml @@ -0,0 +1,21 @@ +name = "extra-incompatible-with-extra-not-requested" +description = "One of two incompatible optional dependencies are requested for the package." + +[root] +requires = ["a[extra_c]"] + +[expected] +satisfiable = true +explanation = "Because the user does not request both extras, it is okay that one is incompatible with the other." + +[expected.packages] +a = "1.0.0" +b = "2.0.0" + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] + +[packages.a.versions."1.0.0".extras] +extra_b = ["b==1.0.0"] +extra_c = ["b==2.0.0"] diff --git a/test/scenarios/extras/extra-incompatible-with-extra.toml b/test/scenarios/extras/extra-incompatible-with-extra.toml new file mode 100644 index 0000000000..14ad87ca7c --- /dev/null +++ b/test/scenarios/extras/extra-incompatible-with-extra.toml @@ -0,0 +1,17 @@ +name = "extra-incompatible-with-extra" +description = "Multiple optional dependencies are requested for the package, but they have conflicting requirements with each other." + +[root] +requires = ["a[extra_b,extra_c]"] + +[expected] +explanation = "Because both `extra_b` and `extra_c` are requested and they require incompatible versions of `b`, `a` cannot be installed." +satisfiable = false + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] + +[packages.a.versions."1.0.0".extras] +extra_b = ["b==1.0.0"] +extra_c = ["b==2.0.0"] diff --git a/test/scenarios/extras/extra-incompatible-with-root.toml b/test/scenarios/extras/extra-incompatible-with-root.toml new file mode 100644 index 0000000000..719960db0d --- /dev/null +++ b/test/scenarios/extras/extra-incompatible-with-root.toml @@ -0,0 +1,16 @@ +name = "extra-incompatible-with-root" +description = "Optional dependencies are requested for the package, but the extra is not compatible with other requested versions." + +[root] +requires = ["a[extra]", "b==2.0.0"] + +[expected] +explanation = "Because the user requested `b==2.0.0` but the requested extra requires `b==1.0.0`, the dependencies cannot be satisfied." +satisfiable = false + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] + +[packages.a.versions."1.0.0".extras] +extra = ["b==1.0.0"] diff --git a/test/scenarios/extras/extra-required.toml b/test/scenarios/extras/extra-required.toml new file mode 100644 index 0000000000..9094d18373 --- /dev/null +++ b/test/scenarios/extras/extra-required.toml @@ -0,0 +1,17 @@ +name = "extra-required" +description = "Optional dependencies are requested for the package." + +[root] +requires = ["a[extra]"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" +b = "1.0.0" + +[packages.b.versions."1.0.0"] + +[packages.a.versions."1.0.0".extras] +extra = ["b"] diff --git a/test/scenarios/extras/missing-extra.toml b/test/scenarios/extras/missing-extra.toml new file mode 100644 index 0000000000..42f065d42c --- /dev/null +++ b/test/scenarios/extras/missing-extra.toml @@ -0,0 +1,14 @@ +name = "missing-extra" +description = "Optional dependencies are requested for the package, but the extra does not exist." + +[root] +requires = ["a[extra]"] + +[expected] +satisfiable = true +explanation = "Missing extras are ignored during resolution." + +[expected.packages] +a = "1.0.0" + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/extras/multiple-extras-required.toml b/test/scenarios/extras/multiple-extras-required.toml new file mode 100644 index 0000000000..5d39616169 --- /dev/null +++ b/test/scenarios/extras/multiple-extras-required.toml @@ -0,0 +1,21 @@ +name = "multiple-extras-required" +description = "Multiple optional dependencies are requested for the package." + +[root] +requires = ["a[extra_b,extra_c]"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" +b = "1.0.0" +c = "1.0.0" + +[packages.b.versions."1.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.a.versions."1.0.0".extras] +extra_b = ["b"] +extra_c = ["c"] diff --git a/test/scenarios/fork/allows-non-conflicting-non-overlapping-dependencies.toml b/test/scenarios/fork/allows-non-conflicting-non-overlapping-dependencies.toml new file mode 100644 index 0000000000..12bc128c28 --- /dev/null +++ b/test/scenarios/fork/allows-non-conflicting-non-overlapping-dependencies.toml @@ -0,0 +1,24 @@ +name = "fork-allows-non-conflicting-non-overlapping-dependencies" +description = ''' +This test ensures that multiple non-conflicting but also +non-overlapping dependency specifications with the same package name +are allowed and supported. + +At time of writing, this provokes a fork in the resolver, but it +arguably shouldn't since the requirements themselves do not conflict +with one another. However, this does impact resolution. Namely, it +leaves the `a>=1` fork free to choose `a==2.0.0` since it behaves as if +the `a<2` constraint doesn't exist. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=1 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] diff --git a/test/scenarios/fork/allows-non-conflicting-repeated-dependencies.toml b/test/scenarios/fork/allows-non-conflicting-repeated-dependencies.toml new file mode 100644 index 0000000000..284a637330 --- /dev/null +++ b/test/scenarios/fork/allows-non-conflicting-repeated-dependencies.toml @@ -0,0 +1,26 @@ +name = "fork-allows-non-conflicting-repeated-dependencies" +description = ''' +This test ensures that multiple non-conflicting dependency +specifications with the same package name are allowed and supported. + +This test exists because the universal resolver forks itself based on +duplicate dependency specifications by looking at package name. So at +first glance, a case like this could perhaps cause an errant fork. +While it's difficult to test for "does not create a fork" (at time of +writing, the implementation does not fork), we can at least check that +this case is handled correctly without issue. Namely, forking should +only occur when there are duplicate dependency specifications with +disjoint marker expressions. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=1", "a<2"] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] diff --git a/test/scenarios/fork/basic.toml b/test/scenarios/fork/basic.toml new file mode 100644 index 0000000000..886cd54f8c --- /dev/null +++ b/test/scenarios/fork/basic.toml @@ -0,0 +1,17 @@ +name = "fork-basic" +description = ''' +An extremely basic test of universal resolution. In this case, the resolution +should contain two distinct versions of `a` depending on `sys_platform`. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] diff --git a/test/scenarios/fork/conflict-in-fork.toml b/test/scenarios/fork/conflict-in-fork.toml new file mode 100644 index 0000000000..cbaed09ece --- /dev/null +++ b/test/scenarios/fork/conflict-in-fork.toml @@ -0,0 +1,26 @@ +name = "conflict-in-fork" +description = ''' +We have a conflict after forking. This scenario exists to test the error message. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = false + +[root] +requires = ["a>=2 ; sys_platform == 'os1'", "a<2 ; sys_platform == 'os2'"] + +[packages.a.versions."1.0.0"] +requires = ["b", "c"] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["d==1"] + +[packages.c.versions."1.0.0"] +requires = ["d==2"] + +[packages.d.versions."1.0.0"] +[packages.d.versions."2.0.0"] diff --git a/test/scenarios/fork/conflict-unsatisfiable.toml b/test/scenarios/fork/conflict-unsatisfiable.toml new file mode 100644 index 0000000000..d1f8bd5b61 --- /dev/null +++ b/test/scenarios/fork/conflict-unsatisfiable.toml @@ -0,0 +1,23 @@ +name = "fork-conflict-unsatisfiable" +description = ''' +This test ensures that conflicting dependency specifications lead to an +unsatisfiable result. + +In particular, this is a case that should not fork even though there +are conflicting requirements because their marker expressions are +overlapping. (Well, there aren't any marker expressions here, which +means they are both unconditional.) +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = false + +[root] +requires = ["a>=2", "a<2"] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] +[packages.a.versions."3.0.0"] diff --git a/test/scenarios/fork/filter-sibling-dependencies.toml b/test/scenarios/fork/filter-sibling-dependencies.toml new file mode 100644 index 0000000000..eede687ddd --- /dev/null +++ b/test/scenarios/fork/filter-sibling-dependencies.toml @@ -0,0 +1,39 @@ +name = "fork-filter-sibling-dependencies" +description = ''' +This tests that sibling dependencies of a package that provokes a +fork are correctly filtered out of forks where they are otherwise +impossible. + +In this case, a previous version of the universal resolver would +include both `b` and `c` in *both* of the forks produced by the +conflicting dependency specifications on `a`. This in turn led to +transitive dependency specifications on both `d==1.0.0` and `d==2.0.0`. +Since the universal resolver only forks based on local conditions, this +led to a failed resolution. + +The correct thing to do here is to ensure that `b` is only part of the +`a==4.4.0` fork and `c` is only par of the `a==4.3.0` fork. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a==4.4.0 ; sys_platform == 'linux'", + "a==4.3.0 ; sys_platform == 'darwin'", + "b==1.0.0 ; sys_platform == 'linux'", + "c==1.0.0 ; sys_platform == 'darwin'", +] + +[packages.a.versions."4.3.0"] +[packages.a.versions."4.4.0"] +[packages.b.versions."1.0.0"] +requires = ["d==1.0.0"] +[packages.c.versions."1.0.0"] +requires = ["d==2.0.0"] +[packages.d.versions."1.0.0"] +[packages.d.versions."2.0.0"] diff --git a/test/scenarios/fork/fork-upgrade.toml b/test/scenarios/fork/fork-upgrade.toml new file mode 100644 index 0000000000..7cdc16f30d --- /dev/null +++ b/test/scenarios/fork/fork-upgrade.toml @@ -0,0 +1,28 @@ +name = "fork-upgrade" +description = ''' +This test checks that we discard fork markers when using `--upgrade`. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["foo"] + +[packages.foo.versions."1.0.0"] +requires = [ + # Provoke a fork + "bar==1; sys_platform == 'linux'", + "bar==2; sys_platform != 'linux'", +] +[packages.foo.versions."2.0.0"] +requires = [ + # No fork + "bar==2", +] + +[packages.bar.versions."1.0.0"] +[packages.bar.versions."2.0.0"] diff --git a/test/scenarios/fork/incomplete-markers.toml b/test/scenarios/fork/incomplete-markers.toml new file mode 100644 index 0000000000..51124ab1bc --- /dev/null +++ b/test/scenarios/fork/incomplete-markers.toml @@ -0,0 +1,29 @@ +name = "fork-incomplete-markers" +description = ''' +The root cause the resolver to fork over `a`, but the markers on the variant +of `a` don't cover the entire marker space, they are missing Python 3.13. +Later, we have a dependency this very hole, which we still need to select, +instead of having two forks around but without Python 3.13 and omitting +`c` from the solution. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a==1; python_version < '3.13'", + "a==2; python_version >= '3.14'", + "b", +] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c; python_version == '3.13'"] + +[packages.c.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-accrue.toml b/test/scenarios/fork/marker-accrue.toml new file mode 100644 index 0000000000..c2b35031b8 --- /dev/null +++ b/test/scenarios/fork/marker-accrue.toml @@ -0,0 +1,28 @@ +name = "fork-marker-accrue" +description = ''' +This is actually a non-forking test case that tests the tracking of marker +expressions in general. In this case, the dependency on `c` should have its +marker expressions automatically combined. In this case, it's `linux OR +darwin`, even though `linux OR darwin` doesn't actually appear verbatim as a +marker expression for any dependency on `c`. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a==1.0.0 ; implementation_name == 'cpython'", + "b==1.0.0 ; implementation_name == 'pypy'", +] + +[packages.a.versions."1.0.0"] +requires = ["c==1.0.0 ; sys_platform == 'linux'"] + +[packages.b.versions."1.0.0"] +requires = ["c==1.0.0 ; sys_platform == 'darwin'"] + +[packages.c.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-disjoint.toml b/test/scenarios/fork/marker-disjoint.toml new file mode 100644 index 0000000000..b9a54213a2 --- /dev/null +++ b/test/scenarios/fork/marker-disjoint.toml @@ -0,0 +1,26 @@ +name = "fork-marker-disjoint" +description = ''' +A basic test that ensures, at least in this one basic case, that forking in +universal resolution happens only when the corresponding marker expressions are +completely disjoint. Here, we provide two completely incompatible dependency +specifications with equivalent markers. Thus, they are trivially not disjoint, +and resolution should fail. + +NOTE: This acts a regression test for the initial version of universal +resolution that would fork whenever a package was repeated in the list of +dependency specifications. So previously, this would produce a resolution with +both `1.0.0` and `2.0.0` of `a`. But of course, the correct behavior is to fail +resolving. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = false + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'linux'"] + +[packages.a.versions."1.0.0"] +[packages.a.versions."2.0.0"] diff --git a/test/scenarios/fork/marker-inherit-combined-allowed.toml b/test/scenarios/fork/marker-inherit-combined-allowed.toml new file mode 100644 index 0000000000..3492a8159e --- /dev/null +++ b/test/scenarios/fork/marker-inherit-combined-allowed.toml @@ -0,0 +1,31 @@ +name = "fork-marker-inherit-combined-allowed" +description = ''' +This test builds on `fork-marker-inherit-combined`. Namely, we add +`or implementation_name == 'pypy'` to the dependency on `c`. While +`sys_platform == 'linux'` cannot be true because of the first fork, +the second fork which includes `b==1.0.0` happens precisely when +`implementation_name == 'pypy'`. So in this case, `c` should be +included. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +requires = [ + "b>=2 ; implementation_name == 'cpython'", + "b<2 ; implementation_name == 'pypy'", +] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c ; sys_platform == 'linux' or implementation_name == 'pypy'"] +[packages.b.versions."2.0.0"] + +[packages.c.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-inherit-combined-disallowed.toml b/test/scenarios/fork/marker-inherit-combined-disallowed.toml new file mode 100644 index 0000000000..229d8c9d24 --- /dev/null +++ b/test/scenarios/fork/marker-inherit-combined-disallowed.toml @@ -0,0 +1,32 @@ +name = "fork-marker-inherit-combined-disallowed" +description = ''' +This test builds on `fork-marker-inherit-combined`. Namely, we add +`or implementation_name == 'cpython'` to the dependency on `c`. +While `sys_platform == 'linux'` cannot be true because of the first +fork, the second fork which includes `b==1.0.0` happens precisely +when `implementation_name == 'pypy'`, which is *also* disjoint with +`implementation_name == 'cpython'`. Therefore, `c` should not be +included here. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +requires = [ + "b>=2 ; implementation_name == 'cpython'", + "b<2 ; implementation_name == 'pypy'", +] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c ; sys_platform == 'linux' or implementation_name == 'cpython'"] +[packages.b.versions."2.0.0"] + +[packages.c.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-inherit-combined.toml b/test/scenarios/fork/marker-inherit-combined.toml new file mode 100644 index 0000000000..e104f17c0f --- /dev/null +++ b/test/scenarios/fork/marker-inherit-combined.toml @@ -0,0 +1,33 @@ +name = "fork-marker-inherit-combined" +description = ''' +In this test, we check that marker expressions which provoke a fork +are carried through to subsequent forks. Here, the `a>=2` and `a<2` +dependency specifications create a fork, and then the `a<2` fork leads +to `a==1.0.0` with dependency specifications on `b>=2` and `b<2` that +provoke yet another fork. Finally, in the `b<2` fork, a dependency on +`c` is introduced whose marker expression is disjoint with the marker +expression that provoked the *first* fork. Therefore, `c` should be +entirely excluded from the resolution. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +requires = [ + "b>=2 ; implementation_name == 'cpython'", + "b<2 ; implementation_name == 'pypy'", +] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c ; sys_platform == 'linux'"] +[packages.b.versions."2.0.0"] + +[packages.c.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-inherit-isolated.toml b/test/scenarios/fork/marker-inherit-isolated.toml new file mode 100644 index 0000000000..677407a7d1 --- /dev/null +++ b/test/scenarios/fork/marker-inherit-isolated.toml @@ -0,0 +1,25 @@ +name = "fork-marker-inherit-isolated" +description = ''' +This is like `fork-marker-inherit`, but where both `a>=2` and `a<2` +have a conditional dependency on `b`. For `a>=2`, the conditional +dependency on `b` has overlap with the `a>=2` marker expression, and +thus, `b` should be included *only* in the dependencies for `a==2.0.0`. +As with `fork-marker-inherit`, the `a<2` path should exclude `b==1.0.0` +since their marker expressions are disjoint. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +requires = ["b ; sys_platform == 'linux'"] +[packages.a.versions."2.0.0"] +requires = ["b ; sys_platform == 'linux'"] + +[packages.b.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-inherit-transitive.toml b/test/scenarios/fork/marker-inherit-transitive.toml new file mode 100644 index 0000000000..1ed33341a8 --- /dev/null +++ b/test/scenarios/fork/marker-inherit-transitive.toml @@ -0,0 +1,30 @@ +name = "fork-marker-inherit-transitive" +description = ''' +This is like `fork-marker-inherit`, but tests that the marker +expressions that provoke a fork are carried transitively through the +dependency graph. In this case, `a<2 -> b -> c -> d`, but where the +last dependency on `d` requires a marker expression that is disjoint +with the initial `a<2` dependency. Therefore, it ought to be completely +excluded from the resolution. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +requires = ["b"] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c"] + +[packages.c.versions."1.0.0"] +requires = ["d ; sys_platform == 'linux'"] + +[packages.d.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-inherit.toml b/test/scenarios/fork/marker-inherit.toml new file mode 100644 index 0000000000..03c3fed78a --- /dev/null +++ b/test/scenarios/fork/marker-inherit.toml @@ -0,0 +1,28 @@ +name = "fork-marker-inherit" +description = ''' +This tests that markers which provoked a fork in the universal resolver +are used to ignore dependencies which cannot possibly be installed by a +resolution produced by that fork. + +In this example, the `a<2` dependency is only active on Darwin +platforms. But the `a==1.0.0` distribution has a dependency on `b` +that is only active on Linux, where as `a==2.0.0` does not. Therefore, +when the fork provoked by the `a<2` dependency considers `b`, it should +ignore it because it isn't possible for `sys_platform == 'linux'` and +`sys_platform == 'darwin'` to be simultaneously true. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a>=2 ; sys_platform == 'linux'", "a<2 ; sys_platform == 'darwin'"] + +[packages.a.versions."1.0.0"] +requires = ["b ; sys_platform == 'linux'"] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-limited-inherit.toml b/test/scenarios/fork/marker-limited-inherit.toml new file mode 100644 index 0000000000..fa167aaf0b --- /dev/null +++ b/test/scenarios/fork/marker-limited-inherit.toml @@ -0,0 +1,34 @@ +name = "fork-marker-limited-inherit" +description = ''' +This is like `fork-marker-inherit`, but it tests that dependency +filtering only occurs in the context of a fork. + +For example, as in `fork-marker-inherit`, the `c` dependency of +`a<2` should be entirely excluded here since it is possible for +`sys_platform` to be simultaneously equivalent to Darwin and Linux. +However, the unconditional dependency on `b`, which in turn depends on +`c` for Linux only, should still incorporate `c` as the dependency is +not part of any fork. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a>=2 ; sys_platform == 'linux'", + "a<2 ; sys_platform == 'darwin'", + "b", +] + +[packages.a.versions."1.0.0"] +requires = ["c ; sys_platform == 'linux'"] +[packages.a.versions."2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c ; sys_platform == 'linux'"] + +[packages.c.versions."1.0.0"] diff --git a/test/scenarios/fork/marker-selection.toml b/test/scenarios/fork/marker-selection.toml new file mode 100644 index 0000000000..bc697e9fab --- /dev/null +++ b/test/scenarios/fork/marker-selection.toml @@ -0,0 +1,28 @@ +name = "fork-marker-selection" +description = ''' +This tests a case where the resolver forks because of non-overlapping marker +expressions on `b`. In the original universal resolver implementation, this +resulted in multiple versions of `a` being unconditionally included in the lock +file. So this acts as a regression test to ensure that only one version of `a` +is selected. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a", + "b>=2 ; sys_platform == 'linux'", + "b<2 ; sys_platform == 'darwin'", +] + +[packages.a.versions."0.1.0"] +[packages.a.versions."0.2.0"] +requires = ["b>=2.0.0"] + +[packages.b.versions."1.0.0"] +[packages.b.versions."2.0.0"] diff --git a/test/scenarios/fork/marker-track.toml b/test/scenarios/fork/marker-track.toml new file mode 100644 index 0000000000..3f0a3d5d2d --- /dev/null +++ b/test/scenarios/fork/marker-track.toml @@ -0,0 +1,30 @@ +name = "fork-marker-track" +description = ''' +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a", + "b>=2.8 ; sys_platform == 'linux'", + "b<2.8 ; sys_platform == 'darwin'", +] + +[packages.a.versions."1.3.1"] +requires = ["c ; implementation_name == 'iron'"] +[packages.a.versions."2.0.0"] +requires = ["b>=2.8", "c ; implementation_name == 'cpython'"] +[packages.a.versions."3.1.0"] +requires = ["b>=2.8", "c ; implementation_name == 'pypy'"] +[packages.a.versions."4.3.0"] +requires = ["b>=2.8"] + +[packages.b.versions."2.7"] +[packages.b.versions."2.8"] + +[packages.c.versions."1.10"] diff --git a/test/scenarios/fork/non-fork-marker-transitive.toml b/test/scenarios/fork/non-fork-marker-transitive.toml new file mode 100644 index 0000000000..ecbe97a9b9 --- /dev/null +++ b/test/scenarios/fork/non-fork-marker-transitive.toml @@ -0,0 +1,24 @@ +name = "fork-non-fork-marker-transitive" +description = ''' +This is the same setup as `non-local-fork-marker-transitive`, but the disjoint +dependency specifications on `c` use the same constraints and thus depend on +the same version of `c`. In this case, there is no conflict. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["a==1.0.0", "b==1.0.0"] + +[packages.a.versions."1.0.0"] +requires = ["c>=2.0.0 ; sys_platform == 'linux'"] + +[packages.b.versions."1.0.0"] +requires = ["c>=2.0.0 ; sys_platform == 'darwin'"] + +[packages.c.versions."1.0.0"] +[packages.c.versions."2.0.0"] diff --git a/test/scenarios/fork/non-local-fork-marker-direct.toml b/test/scenarios/fork/non-local-fork-marker-direct.toml new file mode 100644 index 0000000000..23c44b3f16 --- /dev/null +++ b/test/scenarios/fork/non-local-fork-marker-direct.toml @@ -0,0 +1,28 @@ +name = "fork-non-local-fork-marker-direct" +description = ''' +This is like `non-local-fork-marker-transitive`, but the marker expressions are +placed on sibling dependency specifications. However, the actual dependency on +`c` is indirect, and thus, there's no fork detected by the universal resolver. +This in turn results in an unresolvable conflict on `c`. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = false + +[root] +requires = [ + "a==1.0.0 ; sys_platform == 'linux'", + "b==1.0.0 ; sys_platform == 'darwin'", +] + +[packages.a.versions."1.0.0"] +requires = ["c<2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c>=2.0.0"] + +[packages.c.versions."1.0.0"] +[packages.c.versions."2.0.0"] diff --git a/test/scenarios/fork/non-local-fork-marker-transitive.toml b/test/scenarios/fork/non-local-fork-marker-transitive.toml new file mode 100644 index 0000000000..19b48e3db5 --- /dev/null +++ b/test/scenarios/fork/non-local-fork-marker-transitive.toml @@ -0,0 +1,31 @@ +name = "fork-non-local-fork-marker-transitive" +description = ''' +This setup introduces dependencies on two distinct versions of `c`, where +each such dependency has a marker expression attached that would normally +make them disjoint. In a non-universal resolver, this is no problem. But in a +forking resolver that tries to create one universal resolution, this can lead +to two distinct versions of `c` in the resolution. This is in and of itself +not a problem, since that is an expected scenario for universal resolution. +The problem in this case is that because the dependency specifications for +`c` occur in two different points (i.e., they are not sibling dependency +specifications) in the dependency graph, the forking resolver does not "detect" +it, and thus never forks and thus this results in "no resolution." +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = false + +[root] +requires = ["a==1.0.0", "b==1.0.0"] + +[packages.a.versions."1.0.0"] +requires = ["c<2.0.0 ; sys_platform == 'linux'"] + +[packages.b.versions."1.0.0"] +requires = ["c>=2.0.0 ; sys_platform == 'darwin'"] + +[packages.c.versions."1.0.0"] +[packages.c.versions."2.0.0"] diff --git a/test/scenarios/fork/overlapping-markers-basic.toml b/test/scenarios/fork/overlapping-markers-basic.toml new file mode 100644 index 0000000000..410ff39b30 --- /dev/null +++ b/test/scenarios/fork/overlapping-markers-basic.toml @@ -0,0 +1,48 @@ +name = "fork-overlapping-markers-basic" +description = ''' +This scenario tests a very basic case of overlapping markers. Namely, +it emulates a common pattern in the ecosystem where marker expressions +are used to progressively increase the version constraints of a package +as the Python version increases. + +In this case, there is actually a split occurring between +`python_version < '3.13'` and the other marker expressions, so this +isn't just a scenario with overlapping but non-disjoint markers. + +In particular, this serves as a regression test. uv used to create a +lock file with a dependency on `a` with the following markers: + + python_version < '3.13' or python_version >= '3.14' + +But this implies that `a` won't be installed for Python 3.13, which is +clearly wrong. + +The issue was that uv was intersecting *all* marker expressions. So +that `a>=1.1.0` and `a>=1.2.0` fork was getting `python_version >= +'3.13' and python_version >= '3.14'`, which, of course, simplifies +to `python_version >= '3.14'`. But this is wrong! It should be +`python_version >= '3.13' or python_version >= '3.14'`, which of course +simplifies to `python_version >= '3.13'`. And thus, the resulting forks +are not just disjoint but complete in this case. + +Since there are no other constraints on `a`, this causes uv to select +`1.2.0` unconditionally. (The marker expressions get normalized out +entirely.) +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a>=1.0.0 ; python_version < '3.13'", + "a>=1.1.0 ; python_version >= '3.13'", + "a>=1.2.0 ; python_version >= '3.14'", +] + +[packages.a.versions."1.0.0"] +[packages.a.versions."1.1.0"] +[packages.a.versions."1.2.0"] diff --git a/test/scenarios/fork/preferences-dependent-forking-bistable.toml b/test/scenarios/fork/preferences-dependent-forking-bistable.toml new file mode 100644 index 0000000000..735c2c6230 --- /dev/null +++ b/test/scenarios/fork/preferences-dependent-forking-bistable.toml @@ -0,0 +1,77 @@ +name = "preferences-dependent-forking-bistable" +description = ''' +This test contains a bistable resolution scenario when not using ahead-of-time +splitting of resolution forks: We meet one of two fork points depending on the +preferences, creating a resolution whose preferences lead us the other fork +point. + +In the first case, we are in cleaver 2 and fork on `sys_platform`, in the +second case, we are in foo 1 or bar 1 amd fork over `os_name`. + +First case: We select cleaver 2, fork on `sys_platform`, we reject cleaver 2 +(missing fork `os_name`), we select cleaver 1 and don't fork on `os_name` in +`fork-if-not-forked`, done. +Second case: We have preference cleaver 1, fork on `os_name` in +`fork-if-not-forked`, we reject cleaver 1, we select cleaver 2, we fork on +`sys_platform`, we accept cleaver 2 since we forked on `os_name`, done. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["cleaver"] + +[packages.cleaver.versions."2.0.0"] +requires = [ + # Provoke a fork on sys_platform. + "fork-sys-platform==1; sys_platform == 'linux'", + "fork-sys-platform==2; sys_platform != 'linux'", + # Reject cleaver 2 if we didn't fork on os_name, without forking on os_name. + "reject-cleaver2==1; os_name == 'posix'", + "reject-cleaver2-proxy", +] + +[packages.fork-sys-platform.versions."1.0.0"] +[packages.fork-sys-platform.versions."2.0.0"] + +[packages.reject-cleaver2-proxy.versions."1.0.0"] +requires = ["reject-cleaver2==2; os_name != 'posix'"] + +[packages.reject-cleaver2.versions."1.0.0"] +[packages.reject-cleaver2.versions."2.0.0"] + +[packages.cleaver.versions."1.0.0"] +requires = [ + # Provoke a fork on os-name, but only if we didn't fork before. + "fork-if-not-forked!=2; sys_platform == 'linux'", + "fork-if-not-forked-proxy; sys_platform != 'linux'", + # Reject cleaver 1 if we didn't fork on sys_platform, without forking on sys_platform. + "reject-cleaver1==1; sys_platform == 'linux'", + "reject-cleaver1-proxy", +] + +[packages.fork-if-not-forked-proxy.versions."1.0.0"] +requires = ["fork-if-not-forked!=3"] + +[packages.reject-cleaver1-proxy.versions."1.0.0"] +requires = ["reject-cleaver1==2; sys_platform != 'linux'"] + +[packages.reject-cleaver1.versions."1.0.0"] +[packages.reject-cleaver1.versions."2.0.0"] + +[packages.fork-os-name.versions."1.0.0"] +[packages.fork-os-name.versions."2.0.0"] + +[packages.fork-if-not-forked.versions."1.0.0"] +requires = [ + # Actually provoke the fork for cleaver 1. + "fork-os-name==1; os_name == 'posix'", + "fork-os-name==2; os_name != 'posix'", + "reject-cleaver1-proxy", +] +[packages.fork-if-not-forked.versions."2.0.0"] +[packages.fork-if-not-forked.versions."3.0.0"] diff --git a/test/scenarios/fork/preferences-dependent-forking-conflicting.toml b/test/scenarios/fork/preferences-dependent-forking-conflicting.toml new file mode 100644 index 0000000000..814eec721e --- /dev/null +++ b/test/scenarios/fork/preferences-dependent-forking-conflicting.toml @@ -0,0 +1,79 @@ +name = "preferences-dependent-forking-conflicting" +description = ''' +Like `preferences-dependent-forking`, but when we don't fork the resolution fails. + +Consider a fresh run without preferences: +* We start with cleaver 2 +* We fork +* We reject cleaver 2 +* We find cleaver solution in fork 1 with foo 2 with bar 1 +* We find cleaver solution in fork 2 with foo 1 with bar 2 +* We write cleaver 1, foo 1, foo 2, bar 1 and bar 2 to the lockfile + +In a subsequent run, we read the preference cleaver 1 from the lockfile (the preferences for foo and bar don't matter): +* We start with cleaver 1 +* We're in universal mode, cleaver requires foo 1, bar 1 +* foo 1 requires bar 2, conflict + +Design sketch: +```text +root -> clear, foo, bar +# Cause a fork, then forget that version. +cleaver 2 -> unrelated-dep==1; fork==1 +cleaver 2 -> unrelated-dep==2; fork==2 +cleaver 2 -> reject-cleaver-2 +# Allow different versions when forking, but force foo 1, bar 1 in universal mode without forking. +cleaver 1 -> foo==1; fork==1 +cleaver 1 -> bar==1; fork==2 +# When we selected foo 1, bar 1 in universal mode for cleaver, this causes a conflict, otherwise we select bar 2. +foo 1 -> bar==2 +``` +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = false + +[root] +requires = [ + "cleaver", + # The reporter packages. + "foo", + "bar", +] + +[packages.cleaver.versions."2.0.0"] +requires = [ + # Provoke a fork + "unrelated-dep==1; sys_platform == 'linux'", + "unrelated-dep==2; sys_platform != 'linux'", + "reject-cleaver-2", +] + +[packages.reject-cleaver-2.versions."1.0.0"] +requires = [ + # That's a conflict with `cleaver==2`. + "unrelated-dep==3", +] + +[packages.unrelated-dep.versions."1.0.0"] +[packages.unrelated-dep.versions."2.0.0"] +[packages.unrelated-dep.versions."3.0.0"] + +[packages.cleaver.versions."1.0.0"] +requires = [ + # Allow different versions when forking, but force foo 1, bar 1 in universal mode without forking. + "foo==1; sys_platform == 'linux'", + "bar==1; sys_platform != 'linux'", +] + +[packages.foo.versions."1.0.0"] +requires = [ + # When we selected foo 1, bar 1 in universal mode for cleaver, this causes a conflict, otherwise we select bar 2. + "bar==2", +] +[packages.foo.versions."2.0.0"] +[packages.bar.versions."1.0.0"] +[packages.bar.versions."2.0.0"] diff --git a/test/scenarios/fork/preferences-dependent-forking-tristable.toml b/test/scenarios/fork/preferences-dependent-forking-tristable.toml new file mode 100644 index 0000000000..b2162e0193 --- /dev/null +++ b/test/scenarios/fork/preferences-dependent-forking-tristable.toml @@ -0,0 +1,86 @@ +name = "preferences-dependent-forking-tristable" +description = ''' +This test case is like "preferences-dependent-forking-bistable", but with three +states instead of two. The first two locks are in a different state, then we +enter the tristable state. + +It's not polished, but it's useful to have something with a higher period +than 2 in our test suite. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = ["cleaver", "foo", "bar"] + +[packages.cleaver.versions."2.0.0"] +requires = [ + "unrelated-dep==1; sys_platform == 'linux'", + "unrelated-dep==2; sys_platform != 'linux'", + "a", + "b", +] + +[packages.a.versions."1.0.0"] +requires = ["unrelated-dep3==1; os_name == 'posix'"] + +[packages.b.versions."1.0.0"] +requires = ["unrelated-dep3==2; os_name != 'posix'"] + +[packages.reject-cleaver-2.versions."1.0.0"] +requires = ["unrelated-dep3==3"] + +[packages.unrelated-dep.versions."1.0.0"] +[packages.unrelated-dep.versions."2.0.0"] +[packages.unrelated-dep.versions."3.0.0"] + +[packages.unrelated-dep3.versions."1.0.0"] +[packages.unrelated-dep3.versions."2.0.0"] +[packages.unrelated-dep3.versions."3.0.0"] + +[packages.cleaver.versions."1.0.0"] +requires = [ + "foo==1; sys_platform == 'linux'", + "bar==1; sys_platform != 'linux'", +] + +[packages.foo.versions."1.0.0"] +requires = [ + "c!=3; sys_platform == 'linux'", + "c!=2; sys_platform != 'linux'", + "reject-cleaver-1", +] +[packages.foo.versions."2.0.0"] +[packages.bar.versions."1.0.0"] +requires = [ + "c!=3; sys_platform == 'linux'", + "d; sys_platform != 'linux'", + "reject-cleaver-1", +] +[packages.bar.versions."2.0.0"] + +[packages.reject-cleaver-1.versions."1.0.0"] +requires = [ + "unrelated-dep2==1; sys_platform == 'linux'", + "unrelated-dep2==2; sys_platform != 'linux'", +] + +[packages.unrelated-dep2.versions."1.0.0"] +[packages.unrelated-dep2.versions."2.0.0"] +[packages.unrelated-dep2.versions."3.0.0"] + +[packages.c.versions."1.0.0"] +requires = [ + "unrelated-dep2==1; os_name == 'posix'", + "unrelated-dep2==2; os_name != 'posix'", + "reject-cleaver-1", +] +[packages.c.versions."2.0.0"] +[packages.c.versions."3.0.0"] + +[packages.d.versions."1.0.0"] +requires = ["c!=2"] diff --git a/test/scenarios/fork/preferences-dependent-forking.toml b/test/scenarios/fork/preferences-dependent-forking.toml new file mode 100644 index 0000000000..ccea44c70e --- /dev/null +++ b/test/scenarios/fork/preferences-dependent-forking.toml @@ -0,0 +1,76 @@ +name = "preferences-dependent-forking" +description = ''' +This test contains a scenario where the solution depends on whether we fork, and whether we fork depends on the +preferences. + +Consider a fresh run without preferences: +* We start with cleaver 2 +* We fork +* We reject cleaver 2 +* We find cleaver solution in fork 1 with foo 2 with bar 1 +* We find cleaver solution in fork 2 with foo 1 with bar 2 +* We write cleaver 1, foo 1, foo 2, bar 1 and bar 2 to the lockfile + +In a subsequent run, we read the preference cleaver 1 from the lockfile (the preferences for foo and bar don't matter): +* We start with cleaver 1 +* We're in universal mode, we resolve foo 1 and bar 1 +* We write cleaver 1 and bar 1 to the lockfile + +We call a resolution that's different on the second run to the first unstable. + +Design sketch: +```text +root -> clear, foo, bar +# Cause a fork, then forget that version. +cleaver 2 -> unrelated-dep==1; fork==1 +cleaver 2 -> unrelated-dep==2; fork==2 +cleaver 2 -> reject-cleaver-2 +# Allow different versions when forking, but force foo 1, bar 1 in universal mode without forking. +cleaver 1 -> foo==1; fork==1 +cleaver 1 -> bar==1; fork==2 +``` +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "cleaver", + # The reporter packages. + "foo", + "bar", +] + +[packages.cleaver.versions."2.0.0"] +requires = [ + # Provoke a fork + "unrelated-dep==1; sys_platform == 'linux'", + "unrelated-dep==2; sys_platform != 'linux'", + "reject-cleaver-2", +] + +[packages.reject-cleaver-2.versions."1.0.0"] +requires = [ + # That's a conflict with `cleaver==2`. + "unrelated-dep==3", +] + +[packages.unrelated-dep.versions."1.0.0"] +[packages.unrelated-dep.versions."2.0.0"] +[packages.unrelated-dep.versions."3.0.0"] + +[packages.cleaver.versions."1.0.0"] +requires = [ + # Allow different versions when forking, but force foo 1, bar 1 in universal mode without forking. + "foo==1; sys_platform == 'linux'", + "bar==1; sys_platform != 'linux'", +] + +[packages.foo.versions."1.0.0"] +[packages.foo.versions."2.0.0"] +[packages.bar.versions."1.0.0"] +[packages.bar.versions."2.0.0"] diff --git a/test/scenarios/fork/remaining-universe-partitioning.toml b/test/scenarios/fork/remaining-universe-partitioning.toml new file mode 100644 index 0000000000..dcfe62da24 --- /dev/null +++ b/test/scenarios/fork/remaining-universe-partitioning.toml @@ -0,0 +1,47 @@ +name = "fork-remaining-universe-partitioning" +description = ''' +This scenario tries to check that the "remaining universe" handling in +the universal resolver is correct. Namely, whenever we create forks +from disjoint markers that don't union to the universe, we need to +create *another* fork corresponding to the difference between the +universe and the union of the forks. + +But when we do this, that remaining universe fork needs to be created +like any other fork: it should start copying whatever set of forks +existed by the time we got to this point, intersecting the markers with +the markers describing the remaining universe and then filtering out +any dependencies that are disjoint with the resulting markers. + +This test exercises that logic by ensuring that a package `z` in the +remaining universe is excluded based on the combination of markers +from a parent fork. That is, if the remaining universe fork does not +pick up the markers from the parent forks, then `z` would be included +because the remaining universe for _just_ the `b` dependencies of `a` +is `os_name != 'linux' and os_name != 'darwin'`, which is satisfied by +`z`'s marker of `sys_platform == 'windows'`. However, `a 1.0.0` is only +selected in the context of `a < 2 ; sys_platform == 'illumos'`, so `z` +should never appear in the resolution. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[root] +requires = [ + "a>=2 ; sys_platform == 'windows'", + "a<2 ; sys_platform == 'illumos'", +] + +[packages.z.versions."1.0.0"] +[packages.a.versions."1.0.0"] +requires = [ + "b>=2 ; os_name == 'linux'", + "b<2 ; os_name == 'darwin'", + "z ; sys_platform == 'windows'", +] +[packages.a.versions."2.0.0"] +[packages.b.versions."1.0.0"] +[packages.b.versions."2.0.0"] diff --git a/test/scenarios/fork/requires-python-full-prerelease.toml b/test/scenarios/fork/requires-python-full-prerelease.toml new file mode 100644 index 0000000000..8e608a93d5 --- /dev/null +++ b/test/scenarios/fork/requires-python-full-prerelease.toml @@ -0,0 +1,23 @@ +name = "fork-requires-python-full-prerelease" +description = ''' +This tests that a `Requires-Python` specifier will result in the +exclusion of dependency specifications that cannot possibly satisfy it. + +In particular, this is tested via the `python_full_version` marker with +a pre-release version. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[environment] +python = "3.12" + +[root] +requires_python = ">=3.10" +requires = ["a==1.0.0 ; python_full_version == '3.9b1'"] + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/fork/requires-python-full.toml b/test/scenarios/fork/requires-python-full.toml new file mode 100644 index 0000000000..b2e72d8fd3 --- /dev/null +++ b/test/scenarios/fork/requires-python-full.toml @@ -0,0 +1,23 @@ +name = "fork-requires-python-full" +description = ''' +This tests that a `Requires-Python` specifier will result in the +exclusion of dependency specifications that cannot possibly satisfy it. + +In particular, this is tested via the `python_full_version` marker +instead of the more common `python_version` marker. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[environment] +python = "3.12" + +[root] +requires_python = ">=3.10" +requires = ["a==1.0.0 ; python_full_version == '3.9'"] + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/fork/requires-python-patch-overlap.toml b/test/scenarios/fork/requires-python-patch-overlap.toml new file mode 100644 index 0000000000..332a5a4fb5 --- /dev/null +++ b/test/scenarios/fork/requires-python-patch-overlap.toml @@ -0,0 +1,28 @@ +name = "fork-requires-python-patch-overlap" +description = ''' +This tests that a `Requires-Python` specifier that includes a Python +patch version will not result in excluded a dependency specification +with a `python_version == '3.10'` marker. + +This is a regression test for the universal resolver where it would +convert a `Requires-Python: >=3.10.1` specifier into a +`python_version >= '3.10.1'` marker expression, which would be +considered disjoint with `python_version == '3.10'`. Thus, the +dependency `a` below was erroneously excluded. It should be included. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[environment] +python = "3.12" + +[root] +requires_python = ">=3.10.1" +requires = ["a==1.0.0 ; python_version == '3.10'"] + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" diff --git a/test/scenarios/fork/requires-python.toml b/test/scenarios/fork/requires-python.toml new file mode 100644 index 0000000000..47295a7d07 --- /dev/null +++ b/test/scenarios/fork/requires-python.toml @@ -0,0 +1,20 @@ +name = "fork-requires-python" +description = ''' +This tests that a `Requires-Python` specifier will result in the +exclusion of dependency specifications that cannot possibly satisfy it. +''' + +[resolver_options] +universal = true + +[expected] +satisfiable = true + +[environment] +python = "3.12" + +[root] +requires_python = ">=3.10" +requires = ["a==1.0.0 ; python_version == '3.9'"] + +[packages.a.versions."1.0.0"] diff --git a/test/scenarios/incompatible_versions/direct-incompatible-versions.toml b/test/scenarios/incompatible_versions/direct-incompatible-versions.toml new file mode 100644 index 0000000000..c05bc7218d --- /dev/null +++ b/test/scenarios/incompatible_versions/direct-incompatible-versions.toml @@ -0,0 +1,12 @@ +name = "direct-incompatible-versions" +description = "The user requires two incompatible, existing versions of package `a`" + +[root] +requires = ["a==1.0.0", "a==2.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] + +[packages.a.versions."2.0.0"] diff --git a/test/scenarios/incompatible_versions/transitive-incompatible-versions.toml b/test/scenarios/incompatible_versions/transitive-incompatible-versions.toml new file mode 100644 index 0000000000..4650a461b3 --- /dev/null +++ b/test/scenarios/incompatible_versions/transitive-incompatible-versions.toml @@ -0,0 +1,11 @@ +name = "transitive-incompatible-versions" +description = "The user requires `a`, which requires two incompatible, existing versions of package `b`" + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["b==2.0.0", "b==1.0.0"] diff --git a/test/scenarios/incompatible_versions/transitive-incompatible-with-root-version.toml b/test/scenarios/incompatible_versions/transitive-incompatible-with-root-version.toml new file mode 100644 index 0000000000..3dd1c14718 --- /dev/null +++ b/test/scenarios/incompatible_versions/transitive-incompatible-with-root-version.toml @@ -0,0 +1,15 @@ +name = "transitive-incompatible-with-root-version" +description = "The user requires packages `a` and `b` but `a` requires a different version of `b`" + +[root] +requires = ["a", "b==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["b==2.0.0"] + +[packages.b.versions."1.0.0"] + +[packages.b.versions."2.0.0"] diff --git a/test/scenarios/incompatible_versions/transitive-incompatible-with-transitive.toml b/test/scenarios/incompatible_versions/transitive-incompatible-with-transitive.toml new file mode 100644 index 0000000000..e4e12f84d8 --- /dev/null +++ b/test/scenarios/incompatible_versions/transitive-incompatible-with-transitive.toml @@ -0,0 +1,18 @@ +name = "transitive-incompatible-with-transitive" +description = "The user requires package `a` and `b`; `a` and `b` require different versions of `c`" + +[root] +requires = ["a", "b"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["c==1.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c==2.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0"] diff --git a/test/scenarios/local/local-greater-than-or-equal.toml b/test/scenarios/local/local-greater-than-or-equal.toml new file mode 100644 index 0000000000..0342a0ccd6 --- /dev/null +++ b/test/scenarios/local/local-greater-than-or-equal.toml @@ -0,0 +1,16 @@ +name = "local-greater-than-or-equal" +description = "A local version should be included in inclusive ordered comparisons." + +[root] +requires = ["a>=1.2.3"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3+foo' satisfies the constraint '>=1.2.3'." + +[expected.packages] +a = "1.2.3+foo" + +[packages.a.versions."1.2.3+bar"] + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/local/local-greater-than.toml b/test/scenarios/local/local-greater-than.toml new file mode 100644 index 0000000000..2ffff88712 --- /dev/null +++ b/test/scenarios/local/local-greater-than.toml @@ -0,0 +1,10 @@ +name = "local-greater-than" +description = "A local version should be excluded in exclusive ordered comparisons." + +[root] +requires = ["a>1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/local/local-less-than-or-equal.toml b/test/scenarios/local/local-less-than-or-equal.toml new file mode 100644 index 0000000000..0e1c24c1e9 --- /dev/null +++ b/test/scenarios/local/local-less-than-or-equal.toml @@ -0,0 +1,16 @@ +name = "local-less-than-or-equal" +description = "A local version should be included in inclusive ordered comparisons." + +[root] +requires = ["a<=1.2.3"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3+foo' satisfies the constraint '<=1.2.3'." + +[expected.packages] +a = "1.2.3+foo" + +[packages.a.versions."1.2.3+bar"] + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/local/local-less-than.toml b/test/scenarios/local/local-less-than.toml new file mode 100644 index 0000000000..0e63ea4a1c --- /dev/null +++ b/test/scenarios/local/local-less-than.toml @@ -0,0 +1,10 @@ +name = "local-less-than" +description = "A local version should be excluded in exclusive ordered comparisons." + +[root] +requires = ["a<1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/local/local-not-latest.toml b/test/scenarios/local/local-not-latest.toml new file mode 100644 index 0000000000..324848a99c --- /dev/null +++ b/test/scenarios/local/local-not-latest.toml @@ -0,0 +1,22 @@ +name = "local-not-latest" +description = "Tests that we can select an older version with a local segment when newer versions are incompatible." + +[root] +requires = ["a>=1"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.2.1+foo" + +[packages.a.versions."1.2.3"] +wheel_tags = ["py3-any-macosx_10_0_ppc64"] +sdist = false + +[packages.a.versions."1.2.2+foo"] +wheel_tags = ["py3-any-macosx_10_0_ppc64"] +sdist = false + +[packages.a.versions."1.2.1+foo"] +sdist = true diff --git a/test/scenarios/local/local-not-used-with-sdist.toml b/test/scenarios/local/local-not-used-with-sdist.toml new file mode 100644 index 0000000000..b1de6a1ba6 --- /dev/null +++ b/test/scenarios/local/local-not-used-with-sdist.toml @@ -0,0 +1,18 @@ +name = "local-not-used-with-sdist" +description = "If there is a 1.2.3 version with an sdist published and no compatible wheels, then the sdist will be used." + +[root] +requires = ["a==1.2.3"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3' with an sdist satisfies the constraint '==1.2.3'." + +[expected.packages] +a = "1.2.3+foo" + +[packages.a.versions."1.2.3"] +wheel_tags = ["py3-any-macosx_10_0_ppc64"] +sdist = true + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/local/local-simple.toml b/test/scenarios/local/local-simple.toml new file mode 100644 index 0000000000..a9847c196b --- /dev/null +++ b/test/scenarios/local/local-simple.toml @@ -0,0 +1,16 @@ +name = "local-simple" +description = "A simple version constraint should not exclude published versions with local segments." + +[root] +requires = ["a==1.2.3"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3+foo' satisfies the constraint '==1.2.3'." + +[expected.packages] +a = "1.2.3+foo" + +[packages.a.versions."1.2.3+bar"] + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/local/local-transitive-backtrack.toml b/test/scenarios/local/local-transitive-backtrack.toml new file mode 100644 index 0000000000..792c25bf08 --- /dev/null +++ b/test/scenarios/local/local-transitive-backtrack.toml @@ -0,0 +1,23 @@ +name = "local-transitive-backtrack" +description = "A dependency depends on a conflicting local version of a direct dependency, but we can backtrack to a compatible version." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = true +explanation = "Backtracking to '1.0.0' gives us compatible local versions of b." + +[expected.packages] +a = "1.0.0" +b = "2.0.0+foo" + +[packages.a.versions."1.0.0"] +requires = ["b==2.0.0"] + +[packages.a.versions."2.0.0"] +requires = ["b==2.0.0+bar"] + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive-conflicting.toml b/test/scenarios/local/local-transitive-conflicting.toml new file mode 100644 index 0000000000..c3e43951ba --- /dev/null +++ b/test/scenarios/local/local-transitive-conflicting.toml @@ -0,0 +1,15 @@ +name = "local-transitive-conflicting" +description = "A dependency depends on a conflicting local version of a direct dependency." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["b==2.0.0+bar"] + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive-confounding.toml b/test/scenarios/local/local-transitive-confounding.toml new file mode 100644 index 0000000000..0637e4b7ca --- /dev/null +++ b/test/scenarios/local/local-transitive-confounding.toml @@ -0,0 +1,24 @@ +name = "local-transitive-confounding" +description = "A transitive dependency has both a non-local and local version published, but the non-local version is unusable." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "The version '2.0.0+foo' satisfies the constraint '==2.0.0'." + +[expected.packages] +a = "1.0.0" +b = "2.0.0+foo" + +[packages.a.versions."1.0.0"] +requires = ["b==2.0.0"] + +[packages.b.versions."2.0.0"] +wheel_tags = ["py3-any-macosx_10_0_ppc64"] +sdist = false + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive-greater-than-or-equal.toml b/test/scenarios/local/local-transitive-greater-than-or-equal.toml new file mode 100644 index 0000000000..08b8a9798a --- /dev/null +++ b/test/scenarios/local/local-transitive-greater-than-or-equal.toml @@ -0,0 +1,20 @@ +name = "local-transitive-greater-than-or-equal" +description = "A transitive constraint on a local version should match an inclusive ordered operator." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = true +explanation = "The version '2.0.0+foo' satisfies both >=2.0.0 and ==2.0.0+foo." + +[expected.packages] +a = "1.0.0" +b = "2.0.0+foo" + +[packages.a.versions."1.0.0"] +requires = ["b>=2.0.0"] + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive-greater-than.toml b/test/scenarios/local/local-transitive-greater-than.toml new file mode 100644 index 0000000000..d89eaa479e --- /dev/null +++ b/test/scenarios/local/local-transitive-greater-than.toml @@ -0,0 +1,15 @@ +name = "local-transitive-greater-than" +description = "A transitive constraint on a local version should not match an exclusive ordered operator." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["b>2.0.0"] + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive-less-than-or-equal.toml b/test/scenarios/local/local-transitive-less-than-or-equal.toml new file mode 100644 index 0000000000..f3fcd5feb8 --- /dev/null +++ b/test/scenarios/local/local-transitive-less-than-or-equal.toml @@ -0,0 +1,20 @@ +name = "local-transitive-less-than-or-equal" +description = "A transitive constraint on a local version should match an inclusive ordered operator." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = true +explanation = "The version '2.0.0+foo' satisfies both <=2.0.0 and ==2.0.0+foo." + +[expected.packages] +a = "1.0.0" +b = "2.0.0+foo" + +[packages.a.versions."1.0.0"] +requires = ["b<=2.0.0"] + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive-less-than.toml b/test/scenarios/local/local-transitive-less-than.toml new file mode 100644 index 0000000000..0c646b3905 --- /dev/null +++ b/test/scenarios/local/local-transitive-less-than.toml @@ -0,0 +1,15 @@ +name = "local-transitive-less-than" +description = "A transitive constraint on a local version should not match an exclusive ordered operator." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires = ["b<2.0.0"] + +[packages.b.versions."2.0.0+bar"] + +[packages.b.versions."2.0.0+foo"] diff --git a/test/scenarios/local/local-transitive.toml b/test/scenarios/local/local-transitive.toml new file mode 100644 index 0000000000..fdb6a2395c --- /dev/null +++ b/test/scenarios/local/local-transitive.toml @@ -0,0 +1,20 @@ +name = "local-transitive" +description = "A simple version constraint should not exclude published versions with local segments." + +[root] +requires = ["a", "b==2.0.0+foo"] + +[expected] +satisfiable = true +explanation = "The version '2.0.0+foo' satisfies both ==2.0.0 and ==2.0.0+foo." + +[expected.packages] +a = "1.0.0" +b = "2.0.0+foo" + +[packages.a.versions."1.0.0"] +requires = ["b==2.0.0"] + +[packages.b.versions."2.0.0+foo"] + +[packages.b.versions."2.0.0+bar"] diff --git a/test/scenarios/local/local-used-without-sdist.toml b/test/scenarios/local/local-used-without-sdist.toml new file mode 100644 index 0000000000..2c8a7f0bd6 --- /dev/null +++ b/test/scenarios/local/local-used-without-sdist.toml @@ -0,0 +1,18 @@ +name = "local-used-without-sdist" +description = "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." + +[root] +requires = ["a==1.2.3"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3+foo' satisfies the constraint '==1.2.3'." + +[expected.packages] +a = "1.2.3+foo" + +[packages.a.versions."1.2.3"] +wheel_tags = ["py3-any-macosx_10_0_ppc64"] +sdist = false + +[packages.a.versions."1.2.3+foo"] diff --git a/test/scenarios/post/post-equal-available.toml b/test/scenarios/post/post-equal-available.toml new file mode 100644 index 0000000000..de4957bb97 --- /dev/null +++ b/test/scenarios/post/post-equal-available.toml @@ -0,0 +1,16 @@ +name = "post-equal-available" +description = "An equal version constraint should match a post-release version if the post-release version is available." + +[root] +requires = ["a==1.2.3.post0"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3.post0' satisfies the constraint '==1.2.3.post0'." + +[expected.packages] +a = "1.2.3.post0" + +[packages.a.versions."1.2.3"] + +[packages.a.versions."1.2.3.post0"] diff --git a/test/scenarios/post/post-equal-not-available.toml b/test/scenarios/post/post-equal-not-available.toml new file mode 100644 index 0000000000..e214eae619 --- /dev/null +++ b/test/scenarios/post/post-equal-not-available.toml @@ -0,0 +1,12 @@ +name = "post-equal-not-available" +description = "An equal version constraint should not match a post-release version if the post-release version is not available." + +[root] +requires = ["a==1.2.3.post0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3"] + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-greater-than-or-equal-post.toml b/test/scenarios/post/post-greater-than-or-equal-post.toml new file mode 100644 index 0000000000..375b14ee22 --- /dev/null +++ b/test/scenarios/post/post-greater-than-or-equal-post.toml @@ -0,0 +1,16 @@ +name = "post-greater-than-or-equal-post" +description = "A greater-than-or-equal version constraint should match a post-release version if the constraint is itself a post-release version." + +[root] +requires = ["a>=1.2.3.post0"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3.post1' satisfies the constraint '>=1.2.3.post0'." + +[expected.packages] +a = "1.2.3.post1" + +[packages.a.versions."1.2.3.post0"] + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-greater-than-or-equal.toml b/test/scenarios/post/post-greater-than-or-equal.toml new file mode 100644 index 0000000000..66b5e26779 --- /dev/null +++ b/test/scenarios/post/post-greater-than-or-equal.toml @@ -0,0 +1,14 @@ +name = "post-greater-than-or-equal" +description = "A greater-than-or-equal version constraint should match a post-release version." + +[root] +requires = ["a>=1.2.3"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3.post1' satisfies the constraint '>=1.2.3'." + +[expected.packages] +a = "1.2.3.post1" + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-greater-than-post-not-available.toml b/test/scenarios/post/post-greater-than-post-not-available.toml new file mode 100644 index 0000000000..075c869c56 --- /dev/null +++ b/test/scenarios/post/post-greater-than-post-not-available.toml @@ -0,0 +1,14 @@ +name = "post-greater-than-post-not-available" +description = "A greater-than version constraint should not match a post-release version if the post-release version is not available." + +[root] +requires = ["a>1.2.3.post2"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3"] + +[packages.a.versions."1.2.3.post0"] + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-greater-than-post.toml b/test/scenarios/post/post-greater-than-post.toml new file mode 100644 index 0000000000..0478f08906 --- /dev/null +++ b/test/scenarios/post/post-greater-than-post.toml @@ -0,0 +1,16 @@ +name = "post-greater-than-post" +description = "A greater-than version constraint should match a post-release version if the constraint is itself a post-release version." + +[root] +requires = ["a>1.2.3.post0"] + +[expected] +satisfiable = true +explanation = "The version '1.2.3.post1' satisfies the constraint '>1.2.3.post0'." + +[expected.packages] +a = "1.2.3.post1" + +[packages.a.versions."1.2.3.post0"] + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-greater-than.toml b/test/scenarios/post/post-greater-than.toml new file mode 100644 index 0000000000..c62e2a96ec --- /dev/null +++ b/test/scenarios/post/post-greater-than.toml @@ -0,0 +1,10 @@ +name = "post-greater-than" +description = "A greater-than version constraint should not match a post-release version." + +[root] +requires = ["a>1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-less-than-or-equal.toml b/test/scenarios/post/post-less-than-or-equal.toml new file mode 100644 index 0000000000..a5d31897ff --- /dev/null +++ b/test/scenarios/post/post-less-than-or-equal.toml @@ -0,0 +1,10 @@ +name = "post-less-than-or-equal" +description = "A less-than-or-equal version constraint should not match a post-release version." + +[root] +requires = ["a<=1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-less-than.toml b/test/scenarios/post/post-less-than.toml new file mode 100644 index 0000000000..826d3a7280 --- /dev/null +++ b/test/scenarios/post/post-less-than.toml @@ -0,0 +1,10 @@ +name = "post-less-than" +description = "A less-than version constraint should not match a post-release version." + +[root] +requires = ["a<1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/post/post-local-greater-than-post.toml b/test/scenarios/post/post-local-greater-than-post.toml new file mode 100644 index 0000000000..b03335b622 --- /dev/null +++ b/test/scenarios/post/post-local-greater-than-post.toml @@ -0,0 +1,12 @@ +name = "post-local-greater-than-post" +description = "A greater-than version constraint should not match a post-release version with a local version identifier." + +[root] +requires = ["a>1.2.3.post1"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3.post1"] + +[packages.a.versions."1.2.3.post1+local"] diff --git a/test/scenarios/post/post-local-greater-than.toml b/test/scenarios/post/post-local-greater-than.toml new file mode 100644 index 0000000000..2c93a5d864 --- /dev/null +++ b/test/scenarios/post/post-local-greater-than.toml @@ -0,0 +1,12 @@ +name = "post-local-greater-than" +description = "A greater-than version constraint should not match a post-release version with a local version identifier." + +[root] +requires = ["a>1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3.post1"] + +[packages.a.versions."1.2.3.post1+local"] diff --git a/test/scenarios/post/post-simple.toml b/test/scenarios/post/post-simple.toml new file mode 100644 index 0000000000..199c267a82 --- /dev/null +++ b/test/scenarios/post/post-simple.toml @@ -0,0 +1,10 @@ +name = "post-simple" +description = "A simple version constraint should not match a post-release version." + +[root] +requires = ["a==1.2.3"] + +[expected] +satisfiable = false + +[packages.a.versions."1.2.3.post1"] diff --git a/test/scenarios/prereleases/package-multiple-prereleases-kinds.toml b/test/scenarios/prereleases/package-multiple-prereleases-kinds.toml new file mode 100644 index 0000000000..2819702046 --- /dev/null +++ b/test/scenarios/prereleases/package-multiple-prereleases-kinds.toml @@ -0,0 +1,18 @@ +name = "package-multiple-prereleases-kinds" +description = "The user requires `a` which has multiple prereleases available with different labels." + +[root] +requires = ["a>=1.0.0a1"] + +[expected] +satisfiable = true +explanation = "Release candidates should be the highest precedence prerelease kind." + +[expected.packages] +a = "1.0.0rc1" + +[packages.a.versions."1.0.0a1"] + +[packages.a.versions."1.0.0b1"] + +[packages.a.versions."1.0.0rc1"] diff --git a/test/scenarios/prereleases/package-multiple-prereleases-numbers.toml b/test/scenarios/prereleases/package-multiple-prereleases-numbers.toml new file mode 100644 index 0000000000..95701722d3 --- /dev/null +++ b/test/scenarios/prereleases/package-multiple-prereleases-numbers.toml @@ -0,0 +1,18 @@ +name = "package-multiple-prereleases-numbers" +description = "The user requires `a` which has multiple alphas available." + +[root] +requires = ["a>=1.0.0a1"] + +[expected] +satisfiable = true +explanation = "The latest alpha version should be selected." + +[expected.packages] +a = "1.0.0a3" + +[packages.a.versions."1.0.0a1"] + +[packages.a.versions."1.0.0a2"] + +[packages.a.versions."1.0.0a3"] diff --git a/test/scenarios/prereleases/package-only-prereleases-boundary.toml b/test/scenarios/prereleases/package-only-prereleases-boundary.toml new file mode 100644 index 0000000000..bead9a5427 --- /dev/null +++ b/test/scenarios/prereleases/package-only-prereleases-boundary.toml @@ -0,0 +1,18 @@ +name = "package-only-prereleases-boundary" +description = "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." + +[root] +requires = ["a<0.2.0"] + +[expected] +satisfiable = true +explanation = "Since there are only prerelease versions of `a` available, a prerelease is allowed. Since the user did not explicitly request a pre-release, pre-releases at the boundary should not be selected." + +[expected.packages] +a = "0.1.0a1" + +[packages.a.versions."0.1.0a1"] + +[packages.a.versions."0.2.0a1"] + +[packages.a.versions."0.3.0a1"] diff --git a/test/scenarios/prereleases/package-only-prereleases-in-range.toml b/test/scenarios/prereleases/package-only-prereleases-in-range.toml new file mode 100644 index 0000000000..df08ab73cb --- /dev/null +++ b/test/scenarios/prereleases/package-only-prereleases-in-range.toml @@ -0,0 +1,13 @@ +name = "package-only-prereleases-in-range" +description = "The user requires a version of package `a` which only matches prerelease versions but they did not include a prerelease specifier." + +[root] +requires = ["a>0.1.0"] + +[expected] +satisfiable = false +explanation = "Since there are stable versions of `a` available, prerelease versions should not be selected without explicit opt-in." + +[packages.a.versions."0.1.0"] + +[packages.a.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/package-only-prereleases.toml b/test/scenarios/prereleases/package-only-prereleases.toml new file mode 100644 index 0000000000..407deca817 --- /dev/null +++ b/test/scenarios/prereleases/package-only-prereleases.toml @@ -0,0 +1,14 @@ +name = "package-only-prereleases" +description = "The user requires any version of package `a` which only has prerelease versions available." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "Since there are only prerelease versions of `a` available, it should be installed even though the user did not include a prerelease specifier." + +[expected.packages] +a = "1.0.0a1" + +[packages.a.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/package-prerelease-specified-mixed-available.toml b/test/scenarios/prereleases/package-prerelease-specified-mixed-available.toml new file mode 100644 index 0000000000..f7d4bc770a --- /dev/null +++ b/test/scenarios/prereleases/package-prerelease-specified-mixed-available.toml @@ -0,0 +1,20 @@ +name = "package-prerelease-specified-mixed-available" +description = "The user requires a version of `a` with a prerelease specifier and both prerelease and stable releases are available." + +[root] +requires = ["a>=0.1.0a1"] + +[expected] +satisfiable = true +explanation = "Since the user provided a prerelease specifier, the latest prerelease version should be selected." + +[expected.packages] +a = "1.0.0a1" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."0.2.0a1"] + +[packages.a.versions."0.3.0"] + +[packages.a.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/package-prerelease-specified-only-final-available.toml b/test/scenarios/prereleases/package-prerelease-specified-only-final-available.toml new file mode 100644 index 0000000000..97f7043268 --- /dev/null +++ b/test/scenarios/prereleases/package-prerelease-specified-only-final-available.toml @@ -0,0 +1,18 @@ +name = "package-prerelease-specified-only-final-available" +description = "The user requires a version of `a` with a prerelease specifier and only stable releases are available." + +[root] +requires = ["a>=0.1.0a1"] + +[expected] +satisfiable = true +explanation = "The latest stable version should be selected." + +[expected.packages] +a = "0.3.0" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."0.2.0"] + +[packages.a.versions."0.3.0"] diff --git a/test/scenarios/prereleases/package-prerelease-specified-only-prerelease-available.toml b/test/scenarios/prereleases/package-prerelease-specified-only-prerelease-available.toml new file mode 100644 index 0000000000..8f1bcacb1e --- /dev/null +++ b/test/scenarios/prereleases/package-prerelease-specified-only-prerelease-available.toml @@ -0,0 +1,18 @@ +name = "package-prerelease-specified-only-prerelease-available" +description = "The user requires a version of `a` with a prerelease specifier and only prerelease releases are available." + +[root] +requires = ["a>=0.1.0a1"] + +[expected] +satisfiable = true +explanation = "The latest prerelease version should be selected." + +[expected.packages] +a = "0.3.0a1" + +[packages.a.versions."0.1.0a1"] + +[packages.a.versions."0.2.0a1"] + +[packages.a.versions."0.3.0a1"] diff --git a/test/scenarios/prereleases/package-prereleases-boundary.toml b/test/scenarios/prereleases/package-prereleases-boundary.toml new file mode 100644 index 0000000000..063c79491a --- /dev/null +++ b/test/scenarios/prereleases/package-prereleases-boundary.toml @@ -0,0 +1,21 @@ +name = "package-prereleases-boundary" +description = "The user requires a non-prerelease version of `a` but has enabled pre-releases. There are pre-releases on the boundary of their range." + +[root] +requires = ["a<0.2.0"] + +[expected] +satisfiable = true +explanation = "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." + +[expected.packages] +a = "0.1.0" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."0.2.0a1"] + +[packages.a.versions."0.3.0"] + +[resolver_options] +prereleases = true diff --git a/test/scenarios/prereleases/package-prereleases-global-boundary.toml b/test/scenarios/prereleases/package-prereleases-global-boundary.toml new file mode 100644 index 0000000000..0fba514ab8 --- /dev/null +++ b/test/scenarios/prereleases/package-prereleases-global-boundary.toml @@ -0,0 +1,21 @@ +name = "package-prereleases-global-boundary" +description = "The user requires a non-prerelease version of `a` but has enabled pre-releases. There are pre-releases on the boundary of their range." + +[root] +requires = ["a<0.2.0"] + +[expected] +satisfiable = true +explanation = "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." + +[expected.packages] +a = "0.1.0" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."0.2.0a1"] + +[packages.a.versions."0.3.0"] + +[resolver_options] +prereleases = true diff --git a/test/scenarios/prereleases/package-prereleases-specifier-boundary.toml b/test/scenarios/prereleases/package-prereleases-specifier-boundary.toml new file mode 100644 index 0000000000..c023fa82c8 --- /dev/null +++ b/test/scenarios/prereleases/package-prereleases-specifier-boundary.toml @@ -0,0 +1,24 @@ +name = "package-prereleases-specifier-boundary" +description = "The user requires a prerelease version of `a`. There are pre-releases on the boundary of their range." + +[root] +requires = ["a<0.2.0a2"] + +[expected] +satisfiable = true +explanation = "Since the user used a pre-release specifier, pre-releases at the boundary should be selected." + +[expected.packages] +a = "0.2.0a1" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."0.2.0"] + +[packages.a.versions."0.2.0a1"] + +[packages.a.versions."0.2.0a2"] + +[packages.a.versions."0.2.0a3"] + +[packages.a.versions."0.3.0"] diff --git a/test/scenarios/prereleases/requires-package-only-prereleases-in-range-global-opt-in.toml b/test/scenarios/prereleases/requires-package-only-prereleases-in-range-global-opt-in.toml new file mode 100644 index 0000000000..10a4874cc1 --- /dev/null +++ b/test/scenarios/prereleases/requires-package-only-prereleases-in-range-global-opt-in.toml @@ -0,0 +1,18 @@ +name = "requires-package-only-prereleases-in-range-global-opt-in" +description = "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." + +[root] +requires = ["a>0.1.0"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0a1" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."1.0.0a1"] + +[resolver_options] +prereleases = true diff --git a/test/scenarios/prereleases/requires-package-prerelease-and-final-any.toml b/test/scenarios/prereleases/requires-package-prerelease-and-final-any.toml new file mode 100644 index 0000000000..bff6d03e83 --- /dev/null +++ b/test/scenarios/prereleases/requires-package-prerelease-and-final-any.toml @@ -0,0 +1,16 @@ +name = "requires-package-prerelease-and-final-any" +description = "The user requires any version of package `a` has a prerelease version available and an older non-prerelease version." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "Since the user did not provide a prerelease specifier, the older stable version should be selected." + +[expected.packages] +a = "0.1.0" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/transitive-package-only-prereleases-in-range-opt-in.toml b/test/scenarios/prereleases/transitive-package-only-prereleases-in-range-opt-in.toml new file mode 100644 index 0000000000..9abd287e34 --- /dev/null +++ b/test/scenarios/prereleases/transitive-package-only-prereleases-in-range-opt-in.toml @@ -0,0 +1,20 @@ +name = "transitive-package-only-prereleases-in-range-opt-in" +description = "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." + +[root] +requires = ["a", "b>0.0.0a1"] + +[expected] +satisfiable = true +explanation = "Since the user included a dependency on `b` with a prerelease specifier, a prerelease version can be selected." + +[expected.packages] +a = "0.1.0" +b = "1.0.0a1" + +[packages.a.versions."0.1.0"] +requires = ["b>0.1"] + +[packages.b.versions."0.1.0"] + +[packages.b.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/transitive-package-only-prereleases-in-range.toml b/test/scenarios/prereleases/transitive-package-only-prereleases-in-range.toml new file mode 100644 index 0000000000..b6dd2ae5cc --- /dev/null +++ b/test/scenarios/prereleases/transitive-package-only-prereleases-in-range.toml @@ -0,0 +1,16 @@ +name = "transitive-package-only-prereleases-in-range" +description = "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." + +[root] +requires = ["a"] + +[expected] +satisfiable = false +explanation = "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." + +[packages.a.versions."0.1.0"] +requires = ["b>0.1"] + +[packages.b.versions."0.1.0"] + +[packages.b.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/transitive-package-only-prereleases.toml b/test/scenarios/prereleases/transitive-package-only-prereleases.toml new file mode 100644 index 0000000000..6ab9c6714e --- /dev/null +++ b/test/scenarios/prereleases/transitive-package-only-prereleases.toml @@ -0,0 +1,18 @@ +name = "transitive-package-only-prereleases" +description = "The user requires any version of package `a` which requires `b` which only has prerelease versions available." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "Since there are only prerelease versions of `b` available, it should be selected even though the user did not opt-in to prereleases." + +[expected.packages] +a = "0.1.0" +b = "1.0.0a1" + +[packages.a.versions."0.1.0"] +requires = ["b"] + +[packages.b.versions."1.0.0a1"] diff --git a/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-many-versions-holes.toml b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-many-versions-holes.toml new file mode 100644 index 0000000000..8d7b78265d --- /dev/null +++ b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-many-versions-holes.toml @@ -0,0 +1,53 @@ +name = "transitive-prerelease-and-stable-dependency-many-versions-holes" +description = "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." + +[root] +requires = ["a", "b"] + +[expected] +satisfiable = false +explanation = "Since the user did not explicitly opt-in to a prerelease, it cannot be selected." + +[packages.a.versions."1.0.0"] +requires = ["c>1.0.0,!=2.0.0a5,!=2.0.0a6,!=2.0.0a7,!=2.0.0b1,<2.0.0b5"] + +[packages.b.versions."1.0.0"] +requires = ["c>=1.0.0,<=3.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0a1"] + +[packages.c.versions."2.0.0a2"] + +[packages.c.versions."2.0.0a3"] + +[packages.c.versions."2.0.0a4"] + +[packages.c.versions."2.0.0a5"] + +[packages.c.versions."2.0.0a6"] + +[packages.c.versions."2.0.0a7"] + +[packages.c.versions."2.0.0a8"] + +[packages.c.versions."2.0.0a9"] + +[packages.c.versions."2.0.0b1"] + +[packages.c.versions."2.0.0b2"] + +[packages.c.versions."2.0.0b3"] + +[packages.c.versions."2.0.0b4"] + +[packages.c.versions."2.0.0b5"] + +[packages.c.versions."2.0.0b6"] + +[packages.c.versions."2.0.0b7"] + +[packages.c.versions."2.0.0b8"] + +[packages.c.versions."2.0.0b9"] diff --git a/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-many-versions.toml b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-many-versions.toml new file mode 100644 index 0000000000..d6531c8c49 --- /dev/null +++ b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-many-versions.toml @@ -0,0 +1,53 @@ +name = "transitive-prerelease-and-stable-dependency-many-versions" +description = "A transitive dependency has both a prerelease and a stable selector, but can only be satisfied by a prerelease. There are many prerelease versions." + +[root] +requires = ["a", "b"] + +[expected] +satisfiable = false +explanation = "Since the user did not explicitly opt-in to a prerelease, it cannot be selected." + +[packages.a.versions."1.0.0"] +requires = ["c>=2.0.0b1"] + +[packages.b.versions."1.0.0"] +requires = ["c>=1.0.0,<=3.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0a1"] + +[packages.c.versions."2.0.0a2"] + +[packages.c.versions."2.0.0a3"] + +[packages.c.versions."2.0.0a4"] + +[packages.c.versions."2.0.0a5"] + +[packages.c.versions."2.0.0a6"] + +[packages.c.versions."2.0.0a7"] + +[packages.c.versions."2.0.0a8"] + +[packages.c.versions."2.0.0a9"] + +[packages.c.versions."2.0.0b1"] + +[packages.c.versions."2.0.0b2"] + +[packages.c.versions."2.0.0b3"] + +[packages.c.versions."2.0.0b4"] + +[packages.c.versions."2.0.0b5"] + +[packages.c.versions."2.0.0b6"] + +[packages.c.versions."2.0.0b7"] + +[packages.c.versions."2.0.0b8"] + +[packages.c.versions."2.0.0b9"] diff --git a/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-opt-in.toml b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-opt-in.toml new file mode 100644 index 0000000000..a2049b6ba0 --- /dev/null +++ b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency-opt-in.toml @@ -0,0 +1,24 @@ +name = "transitive-prerelease-and-stable-dependency-opt-in" +description = "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." + +[root] +requires = ["a", "b", "c>=0.0.0a1"] + +[expected] +satisfiable = true +explanation = "Since the user explicitly opted-in to a prerelease for `c`, it can be installed." + +[expected.packages] +a = "1.0.0" +b = "1.0.0" +c = "2.0.0b1" + +[packages.a.versions."1.0.0"] +requires = ["c==2.0.0b1"] + +[packages.b.versions."1.0.0"] +requires = ["c>=1.0.0,<=3.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0b1"] diff --git a/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency.toml b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency.toml new file mode 100644 index 0000000000..fad7392533 --- /dev/null +++ b/test/scenarios/prereleases/transitive-prerelease-and-stable-dependency.toml @@ -0,0 +1,19 @@ +name = "transitive-prerelease-and-stable-dependency" +description = "A transitive dependency has both a prerelease and a stable selector, but can only be satisfied by a prerelease" + +[root] +requires = ["a", "b"] + +[expected] +satisfiable = false +explanation = "Since the user did not explicitly opt-in to a prerelease, it cannot be selected." + +[packages.a.versions."1.0.0"] +requires = ["c==2.0.0b1"] + +[packages.b.versions."1.0.0"] +requires = ["c>=1.0.0,<=3.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0b1"] diff --git a/test/scenarios/requires_python/compatible-python-incompatible-override.toml b/test/scenarios/requires_python/compatible-python-incompatible-override.toml new file mode 100644 index 0000000000..6f26a62d2f --- /dev/null +++ b/test/scenarios/requires_python/compatible-python-incompatible-override.toml @@ -0,0 +1,17 @@ +name = "compatible-python-incompatible-override" +description = "The user requires a package which requires a compatible Python version, but they request an incompatible Python version for package resolution." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" + +[resolver_options] +python = "3.9" + +[environment] +python = "3.11" diff --git a/test/scenarios/requires_python/incompatible-python-compatible-override-available-no-wheels.toml b/test/scenarios/requires_python/incompatible-python-compatible-override-available-no-wheels.toml new file mode 100644 index 0000000000..dc08e4bca8 --- /dev/null +++ b/test/scenarios/requires_python/incompatible-python-compatible-override-available-no-wheels.toml @@ -0,0 +1,23 @@ +name = "incompatible-python-compatible-override-available-no-wheels" +description = "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." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = true +explanation = "Since there is a compatible Python version available on the system, it should be used to build the source distributions." + +[expected.packages] +a = "1.0.0" + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" +wheel = false + +[resolver_options] +python = "3.11" + +[environment] +python = "3.9" +additional_python = ["3.11"] diff --git a/test/scenarios/requires_python/incompatible-python-compatible-override-no-compatible-wheels.toml b/test/scenarios/requires_python/incompatible-python-compatible-override-no-compatible-wheels.toml new file mode 100644 index 0000000000..77359d79a2 --- /dev/null +++ b/test/scenarios/requires_python/incompatible-python-compatible-override-no-compatible-wheels.toml @@ -0,0 +1,19 @@ +name = "incompatible-python-compatible-override-no-compatible-wheels" +description = "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." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = true +explanation = "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 determine its dependencies. However, the source distribution includes static metadata, which we can use to determine dependencies without building the package." + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" +wheel_tags = ["foo-none-any"] + +[resolver_options] +python = "3.11" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/incompatible-python-compatible-override-other-wheel.toml b/test/scenarios/requires_python/incompatible-python-compatible-override-other-wheel.toml new file mode 100644 index 0000000000..26988918a5 --- /dev/null +++ b/test/scenarios/requires_python/incompatible-python-compatible-override-other-wheel.toml @@ -0,0 +1,22 @@ +name = "incompatible-python-compatible-override-other-wheel" +description = "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." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "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 source distribution to determine its dependencies. However, the source distribution includes static metadata, which we can use to determine dependencies without building the package." + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" +wheel = false + +[packages.a.versions."2.0.0"] +requires_python = ">=3.12" + +[resolver_options] +python = "3.11" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/incompatible-python-compatible-override-unavailable-no-wheels.toml b/test/scenarios/requires_python/incompatible-python-compatible-override-unavailable-no-wheels.toml new file mode 100644 index 0000000000..24d81314de --- /dev/null +++ b/test/scenarios/requires_python/incompatible-python-compatible-override-unavailable-no-wheels.toml @@ -0,0 +1,19 @@ +name = "incompatible-python-compatible-override-unavailable-no-wheels" +description = "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." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = true +explanation = "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 dependencies. However, the source distribution includes static metadata, which we can use to determine dependencies without building the package." + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" +wheel = false + +[resolver_options] +python = "3.11" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/incompatible-python-compatible-override.toml b/test/scenarios/requires_python/incompatible-python-compatible-override.toml new file mode 100644 index 0000000000..9f4481fee3 --- /dev/null +++ b/test/scenarios/requires_python/incompatible-python-compatible-override.toml @@ -0,0 +1,20 @@ +name = "incompatible-python-compatible-override" +description = "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." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" + +[resolver_options] +python = "3.11" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/python-greater-than-current-backtrack.toml b/test/scenarios/requires_python/python-greater-than-current-backtrack.toml new file mode 100644 index 0000000000..492fabc6de --- /dev/null +++ b/test/scenarios/requires_python/python-greater-than-current-backtrack.toml @@ -0,0 +1,26 @@ +name = "python-greater-than-current-backtrack" +description = "The user requires a package where recent versions require a Python version greater than the current version, but an older version is compatible." + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" + +[packages.a.versions."1.0.0"] +requires_python = ">=3.9" + +[packages.a.versions."2.0.0"] +requires_python = ">=3.10" + +[packages.a.versions."3.0.0"] +requires_python = ">=3.11" + +[packages.a.versions."4.0.0"] +requires_python = ">=3.12" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/python-greater-than-current-excluded.toml b/test/scenarios/requires_python/python-greater-than-current-excluded.toml new file mode 100644 index 0000000000..97e3a1e356 --- /dev/null +++ b/test/scenarios/requires_python/python-greater-than-current-excluded.toml @@ -0,0 +1,23 @@ +name = "python-greater-than-current-excluded" +description = "The user requires a package where recent versions require a Python version greater than the current version, but an excluded older version is compatible." + +[root] +requires = ["a>=2.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires_python = ">=3.9" + +[packages.a.versions."2.0.0"] +requires_python = ">=3.10" + +[packages.a.versions."3.0.0"] +requires_python = ">=3.11" + +[packages.a.versions."4.0.0"] +requires_python = ">=3.12" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/python-greater-than-current-many.toml b/test/scenarios/requires_python/python-greater-than-current-many.toml new file mode 100644 index 0000000000..1d5661e66e --- /dev/null +++ b/test/scenarios/requires_python/python-greater-than-current-many.toml @@ -0,0 +1,47 @@ +name = "python-greater-than-current-many" +description = "The user requires a package which has many versions which all require a Python version greater than the current version" + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."2.0.0"] +requires_python = ">=3.10" + +[packages.a.versions."2.1.0"] +requires_python = ">=3.10" + +[packages.a.versions."2.2.0"] +requires_python = ">=3.10" + +[packages.a.versions."2.3.0"] +requires_python = ">=3.10" + +[packages.a.versions."2.4.0"] +requires_python = ">=3.10" + +[packages.a.versions."2.5.0"] +requires_python = ">=3.10" + +[packages.a.versions."3.0.0"] +requires_python = ">=3.11" + +[packages.a.versions."3.1.0"] +requires_python = ">=3.11" + +[packages.a.versions."3.2.0"] +requires_python = ">=3.11" + +[packages.a.versions."3.3.0"] +requires_python = ">=3.11" + +[packages.a.versions."3.4.0"] +requires_python = ">=3.11" + +[packages.a.versions."3.5.0"] +requires_python = ">=3.11" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/python-greater-than-current-patch.toml b/test/scenarios/requires_python/python-greater-than-current-patch.toml new file mode 100644 index 0000000000..ed189eb8aa --- /dev/null +++ b/test/scenarios/requires_python/python-greater-than-current-patch.toml @@ -0,0 +1,14 @@ +name = "python-greater-than-current-patch" +description = "The user requires a package which requires a Python version with a patch version greater than the current patch version" + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires_python = ">=3.13.2" + +[environment] +python = "3.13.0" diff --git a/test/scenarios/requires_python/python-greater-than-current.toml b/test/scenarios/requires_python/python-greater-than-current.toml new file mode 100644 index 0000000000..d3e0c00a30 --- /dev/null +++ b/test/scenarios/requires_python/python-greater-than-current.toml @@ -0,0 +1,14 @@ +name = "python-greater-than-current" +description = "The user requires a package which requires a Python version greater than the current version" + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/python-less-than-current.toml b/test/scenarios/requires_python/python-less-than-current.toml new file mode 100644 index 0000000000..80c34babc4 --- /dev/null +++ b/test/scenarios/requires_python/python-less-than-current.toml @@ -0,0 +1,15 @@ +name = "python-less-than-current" +description = "The user requires a package which requires a Python version less than the current version" + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = true +explanation = "We ignore the upper bound on Python requirements" + +[packages.a.versions."1.0.0"] +requires_python = "<=3.8" + +[environment] +python = "3.9" diff --git a/test/scenarios/requires_python/python-patch-override-no-patch.toml b/test/scenarios/requires_python/python-patch-override-no-patch.toml new file mode 100644 index 0000000000..395c109ab2 --- /dev/null +++ b/test/scenarios/requires_python/python-patch-override-no-patch.toml @@ -0,0 +1,18 @@ +name = "python-patch-override-no-patch" +description = "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." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false +explanation = "Since the resolver is asked to solve with 3.9, the minimum compatible Python requirement is treated as 3.9.0." + +[packages.a.versions."1.0.0"] +requires_python = ">=3.9.4" + +[resolver_options] +python = "3.9" + +[environment] +python = "3.9.21" diff --git a/test/scenarios/requires_python/python-patch-override-patch-compatible.toml b/test/scenarios/requires_python/python-patch-override-patch-compatible.toml new file mode 100644 index 0000000000..fd7bd2005b --- /dev/null +++ b/test/scenarios/requires_python/python-patch-override-patch-compatible.toml @@ -0,0 +1,20 @@ +name = "python-patch-override-patch-compatible" +description = "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." + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = true + +[expected.packages] +a = "1.0.0" + +[packages.a.versions."1.0.0"] +requires_python = ">=3.9.0" + +[resolver_options] +python = "3.9.0" + +[environment] +python = "3.9.21" diff --git a/test/scenarios/requires_python/python-version-does-not-exist.toml b/test/scenarios/requires_python/python-version-does-not-exist.toml new file mode 100644 index 0000000000..da7219cf86 --- /dev/null +++ b/test/scenarios/requires_python/python-version-does-not-exist.toml @@ -0,0 +1,11 @@ +name = "python-version-does-not-exist" +description = "The user requires a package which requires a Python version that does not exist" + +[root] +requires = ["a==1.0.0"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +requires_python = ">=3.30" diff --git a/test/scenarios/tag_and_markers/requires-python-wheels.toml b/test/scenarios/tag_and_markers/requires-python-wheels.toml new file mode 100644 index 0000000000..4c8e7e367c --- /dev/null +++ b/test/scenarios/tag_and_markers/requires-python-wheels.toml @@ -0,0 +1,19 @@ +name = "requires-python-wheels" +description = "Check that we only include wheels that match the required Python version" + +[root] +requires_python = ">=3.10" +requires = ["a==1.0.0"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +requires_python = ">=3.10" +wheel_tags = ["cp311-cp311-any", "cp310-cp310-any", "cp39-cp39-any"] + +[resolver_options] +universal = true + +[environment] +python = "3.12" diff --git a/test/scenarios/tag_and_markers/unreachable-package.toml b/test/scenarios/tag_and_markers/unreachable-package.toml new file mode 100644 index 0000000000..6554650ca8 --- /dev/null +++ b/test/scenarios/tag_and_markers/unreachable-package.toml @@ -0,0 +1,16 @@ +name = "unreachable-package" +description = "`c` is not reachable due to the markers, it should be excluded from the lockfile" + +[root] +requires = ["a==1.0.0; sys_platform == 'win32'"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +requires = ["b==1.0.0; sys_platform == 'linux'"] + +[packages.b.versions."1.0.0"] + +[resolver_options] +universal = true diff --git a/test/scenarios/tag_and_markers/unreachable-wheels.toml b/test/scenarios/tag_and_markers/unreachable-wheels.toml new file mode 100644 index 0000000000..1c72792063 --- /dev/null +++ b/test/scenarios/tag_and_markers/unreachable-wheels.toml @@ -0,0 +1,39 @@ +name = "unreachable-wheels" +description = "Check that we only include wheels that match the platform markers" + +[root] +requires = [ + "a==1.0.0; sys_platform == 'win32'", + "b==1.0.0; sys_platform == 'linux'", + "c==1.0.0; sys_platform == 'darwin'", +] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +wheel_tags = [ + "cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64", + "cp312-cp312-musllinux_1_1_armv7l", + "cp312-cp312-win_amd64", + "cp312-cp312-macosx_14_0_x86_64", +] + +[packages.b.versions."1.0.0"] +wheel_tags = [ + "cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64", + "cp312-cp312-musllinux_1_1_armv7l", + "cp312-cp312-win_amd64", + "cp312-cp312-macosx_14_0_x86_64", +] + +[packages.c.versions."1.0.0"] +wheel_tags = [ + "cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64", + "cp312-cp312-musllinux_1_1_armv7l", + "cp312-cp312-win_amd64", + "cp312-cp312-macosx_14_0_x86_64", +] + +[resolver_options] +universal = true diff --git a/test/scenarios/tag_and_markers/virtual-package-extra-priorities.toml b/test/scenarios/tag_and_markers/virtual-package-extra-priorities.toml new file mode 100644 index 0000000000..220a191b96 --- /dev/null +++ b/test/scenarios/tag_and_markers/virtual-package-extra-priorities.toml @@ -0,0 +1,26 @@ +name = "marker-variants-have-different-extras" +description = "Check the prioritization for virtual extra and marker packages" + +[expected] +satisfiable = true + +[environment] +python = "3.12" + +[root] +requires = [ + "psycopg[binary] ; platform_python_implementation != 'PyPy'", + "psycopg ; platform_python_implementation == 'PyPy'", +] + +[packages.psycopg.versions."1.0.0"] +requires = ['tzdata; sys_platform == "win32"'] + +[packages.psycopg.versions."1.0.0".extras] +binary = ["psycopg-binary; implementation_name != 'pypy'"] + +[packages.psycopg-binary.versions."1.0.0"] +[packages.tzdata.versions."1.0.0"] + +[resolver_options] +universal = true diff --git a/test/scenarios/tag_and_markers/virtual-package-marker-priorities.toml b/test/scenarios/tag_and_markers/virtual-package-marker-priorities.toml new file mode 100644 index 0000000000..51aae438b1 --- /dev/null +++ b/test/scenarios/tag_and_markers/virtual-package-marker-priorities.toml @@ -0,0 +1,23 @@ +name = "virtual-package-extra-priorities" +description = "Check the prioritization for virtual marker packages" + +[root] +requires_python = ">=3.12" +requires = ["a==1; python_version >= '3.8'", "b; python_version >= '3.9'"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +requires = ["b==1 ; python_version >= '3.10'"] +[packages.a.versions."2.0.0"] +requires = ["b==1 ; python_version >= '3.10'"] + +[packages.b.versions."1.0.0"] +[packages.b.versions."2.0.0"] + +[resolver_options] +universal = true + +[environment] +python = "3.12" diff --git a/test/scenarios/wheels/no-binary.toml b/test/scenarios/wheels/no-binary.toml new file mode 100644 index 0000000000..375b130893 --- /dev/null +++ b/test/scenarios/wheels/no-binary.toml @@ -0,0 +1,14 @@ +name = "no-binary" +description = "Both wheels and source distributions are available, and the user has disabled binaries." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "The source distribution should be used for install" + +[packages.a.versions."1.0.0"] + +[resolver_options] +no_binary = ["a"] diff --git a/test/scenarios/wheels/no-build.toml b/test/scenarios/wheels/no-build.toml new file mode 100644 index 0000000000..f1a082fad3 --- /dev/null +++ b/test/scenarios/wheels/no-build.toml @@ -0,0 +1,14 @@ +name = "no-build" +description = "Both wheels and source distributions are available, and the user has disabled builds." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "The wheel should be used for install" + +[packages.a.versions."1.0.0"] + +[resolver_options] +no_build = ["a"] diff --git a/test/scenarios/wheels/no-sdist-no-wheels-with-matching-abi.toml b/test/scenarios/wheels/no-sdist-no-wheels-with-matching-abi.toml new file mode 100644 index 0000000000..73a5a4f0c6 --- /dev/null +++ b/test/scenarios/wheels/no-sdist-no-wheels-with-matching-abi.toml @@ -0,0 +1,15 @@ +name = "no-sdist-no-wheels-with-matching-abi" +description = "No wheels with matching ABI tags are available, nor are any source distributions available" + +[root] +requires = ["a"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +wheel_tags = ["py3-graalpy240_310_native-any"] +sdist = false + +[resolver_options] +python_platform = "x86_64-manylinux2014" diff --git a/test/scenarios/wheels/no-sdist-no-wheels-with-matching-platform.toml b/test/scenarios/wheels/no-sdist-no-wheels-with-matching-platform.toml new file mode 100644 index 0000000000..35ddcaa5d8 --- /dev/null +++ b/test/scenarios/wheels/no-sdist-no-wheels-with-matching-platform.toml @@ -0,0 +1,15 @@ +name = "no-sdist-no-wheels-with-matching-platform" +description = "No wheels with matching platform tags are available, nor are any source distributions available" + +[root] +requires = ["a"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +wheel_tags = ["py3-none-macosx_10_0_ppc64"] +sdist = false + +[resolver_options] +python_platform = "x86_64-manylinux2014" diff --git a/test/scenarios/wheels/no-sdist-no-wheels-with-matching-python.toml b/test/scenarios/wheels/no-sdist-no-wheels-with-matching-python.toml new file mode 100644 index 0000000000..9b5d28cba3 --- /dev/null +++ b/test/scenarios/wheels/no-sdist-no-wheels-with-matching-python.toml @@ -0,0 +1,15 @@ +name = "no-sdist-no-wheels-with-matching-python" +description = "No wheels with matching Python tags are available, nor are any source distributions available" + +[root] +requires = ["a"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +wheel_tags = ["graalpy310-none-any"] +sdist = false + +[resolver_options] +python_platform = "x86_64-manylinux2014" diff --git a/test/scenarios/wheels/no-wheels-no-build.toml b/test/scenarios/wheels/no-wheels-no-build.toml new file mode 100644 index 0000000000..ac07da46e0 --- /dev/null +++ b/test/scenarios/wheels/no-wheels-no-build.toml @@ -0,0 +1,14 @@ +name = "no-wheels-no-build" +description = "No wheels are available, only source distributions but the user has disabled builds." + +[root] +requires = ["a"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +wheel = false + +[resolver_options] +no_build = ["a"] diff --git a/test/scenarios/wheels/no-wheels-with-matching-platform.toml b/test/scenarios/wheels/no-wheels-with-matching-platform.toml new file mode 100644 index 0000000000..d24d42f956 --- /dev/null +++ b/test/scenarios/wheels/no-wheels-with-matching-platform.toml @@ -0,0 +1,11 @@ +name = "no-wheels-with-matching-platform" +description = "No wheels with matching platform tags are available, just source distributions." + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +wheel_tags = ["py3-any-macosx_10_0_ppc64"] diff --git a/test/scenarios/wheels/no-wheels.toml b/test/scenarios/wheels/no-wheels.toml new file mode 100644 index 0000000000..235eeee9a3 --- /dev/null +++ b/test/scenarios/wheels/no-wheels.toml @@ -0,0 +1,11 @@ +name = "no-wheels" +description = "No wheels are available, only source distributions." + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +wheel = false diff --git a/test/scenarios/wheels/only-wheels-no-binary.toml b/test/scenarios/wheels/only-wheels-no-binary.toml new file mode 100644 index 0000000000..1076721a62 --- /dev/null +++ b/test/scenarios/wheels/only-wheels-no-binary.toml @@ -0,0 +1,14 @@ +name = "only-wheels-no-binary" +description = "No source distributions are available, only wheels but the user has disabled using pre-built binaries." + +[root] +requires = ["a"] + +[expected] +satisfiable = false + +[packages.a.versions."1.0.0"] +sdist = false + +[resolver_options] +no_binary = ["a"] diff --git a/test/scenarios/wheels/only-wheels.toml b/test/scenarios/wheels/only-wheels.toml new file mode 100644 index 0000000000..cc66787830 --- /dev/null +++ b/test/scenarios/wheels/only-wheels.toml @@ -0,0 +1,11 @@ +name = "only-wheels" +description = "No source distributions are available, only wheels." + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +sdist = false diff --git a/test/scenarios/wheels/requires-python-subset.toml b/test/scenarios/wheels/requires-python-subset.toml new file mode 100644 index 0000000000..2d5e92e311 --- /dev/null +++ b/test/scenarios/wheels/requires-python-subset.toml @@ -0,0 +1,16 @@ +name = "requires-python-subset" +description = "While both Linux and Windows are required and `win-only` has only a Windows wheel, `win-only` is also used only on Windows." + +[resolver_options] +universal = true +required_environments = ['sys_platform == "linux"', 'sys_platform == "win32"'] + +[root] +requires = ["win-only; sys_platform == 'win32'"] + +[expected] +satisfiable = true + +[packages.win-only.versions."1.0.0"] +sdist = false +wheel_tags = ["cp312-abi3-win_amd64"] diff --git a/test/scenarios/wheels/specific-architecture.toml b/test/scenarios/wheels/specific-architecture.toml new file mode 100644 index 0000000000..13443f7923 --- /dev/null +++ b/test/scenarios/wheels/specific-architecture.toml @@ -0,0 +1,54 @@ +name = "specific-architecture" +description = "When a dependency is only required on a specific platform (like x86_64), omit wheels that target other platforms (like aarch64)." + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +requires = [ + "b ; platform_machine == 'x86_64'", + "c ; platform_machine == 'aarch64'", + "d ; platform_machine == 'i686'", +] + +[packages.b.versions."1.0.0"] +wheel_tags = [ + "cp313-cp313-manylinux2010_x86_64", + "cp313-cp313-manylinux2010_aarch64", + "cp313-cp313-manylinux2010_i686", + "cp313-cp313-freebsd_13_x86_64", + "cp313-cp313-freebsd_13_aarch64", + "cp313-cp313-macosx_10_9_x86_64", + "cp313-cp313-macosx_10_9_arm64", +] +sdist = false + +[packages.c.versions."1.0.0"] +wheel_tags = [ + "cp313-cp313-manylinux2010_x86_64", + "cp313-cp313-manylinux2010_aarch64", + "cp313-cp313-manylinux2010_i686", + "cp313-cp313-freebsd_13_x86_64", + "cp313-cp313-freebsd_13_aarch64", + "cp313-cp313-macosx_10_9_x86_64", + "cp313-cp313-macosx_10_9_arm64", +] +sdist = false + +[packages.d.versions."1.0.0"] +wheel_tags = [ + "cp313-cp313-manylinux2010_x86_64", + "cp313-cp313-manylinux2010_aarch64", + "cp313-cp313-manylinux2010_i686", + "cp313-cp313-freebsd_13_x86_64", + "cp313-cp313-freebsd_13_aarch64", + "cp313-cp313-macosx_10_9_x86_64", + "cp313-cp313-macosx_10_9_arm64", +] +sdist = false + +[resolver_options] +universal = true diff --git a/test/scenarios/wheels/specific-tag-and-default.toml b/test/scenarios/wheels/specific-tag-and-default.toml new file mode 100644 index 0000000000..c2eb78a320 --- /dev/null +++ b/test/scenarios/wheels/specific-tag-and-default.toml @@ -0,0 +1,14 @@ +name = "specific-tag-and-default" +description = "A wheel for a specific platform is available alongside the default." + +[root] +requires = ["a"] + +[expected] +satisfiable = true + +[packages.a.versions."1.0.0"] +wheel_tags = [ + "cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64", + "py3-none-any", +] diff --git a/test/scenarios/yanked/package-only-yanked-in-range.toml b/test/scenarios/yanked/package-only-yanked-in-range.toml new file mode 100644 index 0000000000..b529642bf5 --- /dev/null +++ b/test/scenarios/yanked/package-only-yanked-in-range.toml @@ -0,0 +1,14 @@ +name = "package-only-yanked-in-range" +description = "The user requires a version of package `a` which only matches yanked versions." + +[root] +requires = ["a>0.1.0"] + +[expected] +satisfiable = false +explanation = "Since there are other versions of `a` available, yanked versions should not be selected without explicit opt-in." + +[packages.a.versions."0.1.0"] + +[packages.a.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/package-only-yanked.toml b/test/scenarios/yanked/package-only-yanked.toml new file mode 100644 index 0000000000..c39abe828a --- /dev/null +++ b/test/scenarios/yanked/package-only-yanked.toml @@ -0,0 +1,12 @@ +name = "package-only-yanked" +description = "The user requires any version of package `a` which only has yanked versions available." + +[root] +requires = ["a"] + +[expected] +satisfiable = false +explanation = "Yanked versions should not be installed, even if they are the only one available." + +[packages.a.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/package-yanked-specified-mixed-available.toml b/test/scenarios/yanked/package-yanked-specified-mixed-available.toml new file mode 100644 index 0000000000..3577b0207f --- /dev/null +++ b/test/scenarios/yanked/package-yanked-specified-mixed-available.toml @@ -0,0 +1,22 @@ +name = "package-yanked-specified-mixed-available" +description = "The user requires any version of `a` and both yanked and unyanked releases are available." + +[root] +requires = ["a>=0.1.0"] + +[expected] +satisfiable = true +explanation = "The latest unyanked version should be selected." + +[expected.packages] +a = "0.3.0" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."0.2.0"] +yanked = true + +[packages.a.versions."0.3.0"] + +[packages.a.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/requires-package-yanked-and-unyanked-any.toml b/test/scenarios/yanked/requires-package-yanked-and-unyanked-any.toml new file mode 100644 index 0000000000..c65b6851f7 --- /dev/null +++ b/test/scenarios/yanked/requires-package-yanked-and-unyanked-any.toml @@ -0,0 +1,17 @@ +name = "requires-package-yanked-and-unyanked-any" +description = "The user requires any version of package `a` has a yanked version available and an older unyanked version." + +[root] +requires = ["a"] + +[expected] +satisfiable = true +explanation = "The unyanked version should be selected." + +[expected.packages] +a = "0.1.0" + +[packages.a.versions."0.1.0"] + +[packages.a.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/transitive-package-only-yanked-in-range-opt-in.toml b/test/scenarios/yanked/transitive-package-only-yanked-in-range-opt-in.toml new file mode 100644 index 0000000000..19d0fbbe7b --- /dev/null +++ b/test/scenarios/yanked/transitive-package-only-yanked-in-range-opt-in.toml @@ -0,0 +1,21 @@ +name = "transitive-package-only-yanked-in-range-opt-in" +description = "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." + +[root] +requires = ["a", "b==1.0.0"] + +[expected] +satisfiable = true +explanation = "Since the user included a dependency on `b` with an exact specifier, the yanked version can be selected." + +[expected.packages] +a = "0.1.0" +b = "1.0.0" + +[packages.a.versions."0.1.0"] +requires = ["b>0.1"] + +[packages.b.versions."0.1.0"] + +[packages.b.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/transitive-package-only-yanked-in-range.toml b/test/scenarios/yanked/transitive-package-only-yanked-in-range.toml new file mode 100644 index 0000000000..14933ebffd --- /dev/null +++ b/test/scenarios/yanked/transitive-package-only-yanked-in-range.toml @@ -0,0 +1,17 @@ +name = "transitive-package-only-yanked-in-range" +description = "The user requires package `a` which has a dependency on a package which only matches yanked versions." + +[root] +requires = ["a"] + +[expected] +satisfiable = false +explanation = "Yanked versions should not be installed, even if they are the only valid version in a range." + +[packages.a.versions."0.1.0"] +requires = ["b>0.1"] + +[packages.b.versions."0.1.0"] + +[packages.b.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/transitive-package-only-yanked.toml b/test/scenarios/yanked/transitive-package-only-yanked.toml new file mode 100644 index 0000000000..f91564ed06 --- /dev/null +++ b/test/scenarios/yanked/transitive-package-only-yanked.toml @@ -0,0 +1,15 @@ +name = "transitive-package-only-yanked" +description = "The user requires any version of package `a` which requires `b` which only has yanked versions available." + +[root] +requires = ["a"] + +[expected] +satisfiable = false +explanation = "Yanked versions should not be installed, even if they are the only one available." + +[packages.a.versions."0.1.0"] +requires = ["b"] + +[packages.b.versions."1.0.0"] +yanked = true diff --git a/test/scenarios/yanked/transitive-yanked-and-unyanked-dependency-opt-in.toml b/test/scenarios/yanked/transitive-yanked-and-unyanked-dependency-opt-in.toml new file mode 100644 index 0000000000..ed38f310cf --- /dev/null +++ b/test/scenarios/yanked/transitive-yanked-and-unyanked-dependency-opt-in.toml @@ -0,0 +1,25 @@ +name = "transitive-yanked-and-unyanked-dependency-opt-in" +description = "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." + +[root] +requires = ["a", "b", "c==2.0.0"] + +[expected] +satisfiable = true +explanation = "Since the user explicitly selected the yanked version of `c`, it can be installed." + +[expected.packages] +a = "1.0.0" +b = "1.0.0" +c = "2.0.0" + +[packages.a.versions."1.0.0"] +requires = ["c==2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c>=1.0.0,<=3.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0"] +yanked = true diff --git a/test/scenarios/yanked/transitive-yanked-and-unyanked-dependency.toml b/test/scenarios/yanked/transitive-yanked-and-unyanked-dependency.toml new file mode 100644 index 0000000000..9be759f6fb --- /dev/null +++ b/test/scenarios/yanked/transitive-yanked-and-unyanked-dependency.toml @@ -0,0 +1,20 @@ +name = "transitive-yanked-and-unyanked-dependency" +description = "A transitive dependency has both a yanked and an unyanked version, but can only be satisfied by a yanked version" + +[root] +requires = ["a", "b"] + +[expected] +satisfiable = false +explanation = "Since the user did not explicitly select the yanked version, it cannot be used." + +[packages.a.versions."1.0.0"] +requires = ["c==2.0.0"] + +[packages.b.versions."1.0.0"] +requires = ["c>=1.0.0,<=3.0.0"] + +[packages.c.versions."1.0.0"] + +[packages.c.versions."2.0.0"] +yanked = true