Skip to content

Commit e497300

Browse files
authored
fix: avoid editable-flag conflict with transitive tool.uv.sources (#6131)
1 parent 3314609 commit e497300

2 files changed

Lines changed: 99 additions & 6 deletions

File tree

crates/pixi/tests/integration_rust/solve_group_tests.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,100 @@ version = "0.1.0"
10711071
);
10721072
}
10731073

1074+
/// Regression test for #6121: `core` declared editable both as a direct
1075+
/// pixi pypi-dependency and via the transitive `[tool.uv.sources]` of
1076+
/// `middle` must not produce a "conflicting URLs" error.
1077+
#[tokio::test]
1078+
async fn test_transitive_uv_sources_editable_consistency() {
1079+
setup_tracing();
1080+
1081+
// Create a fake channel with Python
1082+
let mut package_database = MockRepoData::default();
1083+
package_database.add_package(Package::build("python", "3.10.0").finish());
1084+
1085+
let channel_dir = TempDir::new().unwrap();
1086+
package_database
1087+
.write_repodata(channel_dir.path())
1088+
.await
1089+
.unwrap();
1090+
1091+
let channel = Url::from_file_path(channel_dir.path()).unwrap();
1092+
let platform = Platform::current();
1093+
1094+
let pixi = PixiControl::from_manifest(&format!(
1095+
r#"
1096+
[project]
1097+
name = "test-transitive-editable"
1098+
channels = ["{channel}"]
1099+
platforms = ["{platform}"]
1100+
conda-pypi-map = {{}} # disable mapping
1101+
1102+
[dependencies]
1103+
python = "*"
1104+
1105+
[pypi-dependencies]
1106+
core = {{ path = "./core", editable = true }}
1107+
middle = {{ path = "./middle", editable = true }}
1108+
"#
1109+
))
1110+
.unwrap();
1111+
1112+
let project_path = pixi.workspace_path();
1113+
1114+
let core_dir = project_path.join("core");
1115+
fs_err::create_dir_all(&core_dir).unwrap();
1116+
fs_err::write(
1117+
core_dir.join("pyproject.toml"),
1118+
r#"
1119+
[build-system]
1120+
requires = ["setuptools"]
1121+
build-backend = "setuptools.build_meta"
1122+
1123+
[project]
1124+
name = "core"
1125+
version = "0.1.0"
1126+
"#,
1127+
)
1128+
.unwrap();
1129+
let core_src = core_dir.join("core");
1130+
fs_err::create_dir_all(&core_src).unwrap();
1131+
fs_err::write(core_src.join("__init__.py"), "").unwrap();
1132+
1133+
let middle_dir = project_path.join("middle");
1134+
fs_err::create_dir_all(&middle_dir).unwrap();
1135+
fs_err::write(
1136+
middle_dir.join("pyproject.toml"),
1137+
r#"
1138+
[build-system]
1139+
requires = ["setuptools"]
1140+
build-backend = "setuptools.build_meta"
1141+
1142+
[project]
1143+
name = "middle"
1144+
version = "0.1.0"
1145+
dependencies = ["core"]
1146+
1147+
[tool.uv.sources]
1148+
core = { path = "../core", editable = true }
1149+
"#,
1150+
)
1151+
.unwrap();
1152+
let middle_src = middle_dir.join("middle");
1153+
fs_err::create_dir_all(&middle_src).unwrap();
1154+
fs_err::write(middle_src.join("__init__.py"), "").unwrap();
1155+
1156+
let lock_file = pixi.update_lock_file().await.unwrap();
1157+
1158+
assert!(
1159+
lock_file.contains_pypi_package("default", platform, "core"),
1160+
"default environment should contain core"
1161+
);
1162+
assert!(
1163+
lock_file.contains_pypi_package("default", platform, "middle"),
1164+
"default environment should contain middle"
1165+
);
1166+
}
1167+
10741168
#[tokio::test]
10751169
async fn test_missing_mapping_file_error_includes_path() {
10761170
setup_tracing();

crates/pixi_uv_conversions/src/requirements.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,11 @@ pub fn as_uv_req(
173173
if canonicalized.is_dir() {
174174
RequirementSource::Directory {
175175
install_path: canonicalized.into_boxed_path(),
176-
// Always set editable to false during resolution.
177-
// Editability doesn't affect resolution and is looked up from the
178-
// manifest at install time. This allows different environments in a
179-
// solve-group to have different editability settings without causing
180-
// "conflicting URLs" errors from the uv resolver.
181-
editable: Some(false),
176+
// Editability is applied at install time from the manifest
177+
// (`is_editable_from_manifest`). Leaving it unspecified
178+
// avoids uv "conflicting URLs" errors across solve-group
179+
// environments and transitive `[tool.uv.sources]` (#6121).
180+
editable: None,
182181
url: verbatim,
183182
// TODO: we could see if we ever need this
184183
// AFAICS it would be useful for constraining dependencies

0 commit comments

Comments
 (0)