From be37eda61336a8363c39b02ebdb590f292cc4e6a Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 9 Dec 2024 16:26:54 -0500 Subject: [PATCH] Avoid enforcing non-conflicts in uv export --- crates/uv-resolver/src/lock/mod.rs | 5 - .../uv-resolver/src/lock/requirements_txt.rs | 5 +- crates/uv/tests/it/export.rs | 97 +++++++++++++++++++ 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index f25e174117cf..0803c7495e06 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -4143,11 +4143,6 @@ enum LockErrorKind { #[source] err: DependencyGroupError, }, - /// An error that occurs when trying to export a `uv.lock` with - /// conflicting extras/groups specified to `requirements.txt`. - /// (Because `requirements.txt` cannot encode them.) - #[error("Cannot represent `uv.lock` with conflicting extras or groups as `requirements.txt`")] - ConflictsNotAllowedInRequirementsTxt, } /// An error that occurs when a source string could not be parsed. diff --git a/crates/uv-resolver/src/lock/requirements_txt.rs b/crates/uv-resolver/src/lock/requirements_txt.rs index b84be3e42364..1adde8e8e656 100644 --- a/crates/uv-resolver/src/lock/requirements_txt.rs +++ b/crates/uv-resolver/src/lock/requirements_txt.rs @@ -19,7 +19,7 @@ use uv_pep508::MarkerTree; use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl}; use crate::graph_ops::marker_reachability; -use crate::lock::{LockErrorKind, Package, PackageId, Source}; +use crate::lock::{Package, PackageId, Source}; use crate::universal_marker::UniversalMarker; use crate::{InstallTarget, LockError}; @@ -41,9 +41,6 @@ impl<'lock> RequirementsTxtExport<'lock> { hashes: bool, install_options: &'lock InstallOptions, ) -> Result { - if !target.lock().conflicts().is_empty() { - return Err(LockErrorKind::ConflictsNotAllowedInRequirementsTxt.into()); - } let size_guess = target.lock().packages.len(); let mut petgraph = Graph::with_capacity(size_guess, size_guess); let mut inverse = FxHashMap::with_capacity_and_hasher(size_guess, FxBuildHasher); diff --git a/crates/uv/tests/it/export.rs b/crates/uv/tests/it/export.rs index 9a2181a4093a..3800a798029d 100644 --- a/crates/uv/tests/it/export.rs +++ b/crates/uv/tests/it/export.rs @@ -1687,3 +1687,100 @@ fn export_group() -> Result<()> { Ok(()) } + +#[test] +fn conflicts() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["iniconfig==1.1.1"] + + [tool.uv] + conflicts = [ + [ + { extra = "extra1" }, + { extra = "extra2" }, + ], + ] + + [project.optional-dependencies] + extra1 = ["sortedcontainers==2.3.0"] + extra2 = ["sortedcontainers==2.4.0"] + + [build-system] + requires = ["setuptools>=42"] + build-backend = "setuptools.build_meta" + "#, + )?; + + context.lock().assert().success(); + + uv_snapshot!(context.filters(), context.export(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] + -e . + iniconfig==1.1.1 \ + --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ + --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 + + ----- stderr ----- + Resolved 4 packages in [TIME] + "###); + + uv_snapshot!(context.filters(), context.export().arg("--extra").arg("extra1"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --extra extra1 + -e . + iniconfig==1.1.1 \ + --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ + --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 + sortedcontainers==2.3.0 \ + --hash=sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f \ + --hash=sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1 + + ----- stderr ----- + Resolved 4 packages in [TIME] + "###); + + uv_snapshot!(context.filters(), context.export().arg("--extra").arg("extra2"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --extra extra2 + -e . + iniconfig==1.1.1 \ + --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ + --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 + sortedcontainers==2.4.0 \ + --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \ + --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 + + ----- stderr ----- + Resolved 4 packages in [TIME] + "###); + + uv_snapshot!(context.filters(), context.export().arg("--extra").arg("extra1").arg("--extra").arg("extra2"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + Resolved 4 packages in [TIME] + error: Extras `extra1` and `extra2` are incompatible with the declared conflicts: {`project[extra1]`, `project[extra2]`} + "###); + + Ok(()) +}