mirror of
https://github.com/astral-sh/uv
synced 2026-06-21 13:47:25 +00:00
Activate source selection contexts during sync
This commit is contained in:
@@ -64,6 +64,15 @@ pub trait Installable<'lock> {
|
||||
let mut activated_projects: Vec<&PackageName> = vec![];
|
||||
let mut activated_extras: Vec<(&PackageName, &ExtraName)> = vec![];
|
||||
let mut activated_groups: Vec<(&PackageName, &GroupName)> = vec![];
|
||||
let needs_activation_context = !self.lock().conflicts().is_empty()
|
||||
|| self.lock().packages.iter().any(|package| {
|
||||
package
|
||||
.dependencies
|
||||
.iter()
|
||||
.chain(package.optional_dependencies.values().flatten())
|
||||
.chain(package.dependency_groups.values().flatten())
|
||||
.any(|dependency| !dependency.complexified_marker.conflict().is_true())
|
||||
});
|
||||
|
||||
let root = petgraph.add_node(Node::Root);
|
||||
|
||||
@@ -74,7 +83,7 @@ pub trait Installable<'lock> {
|
||||
// marker. But at that point, we don't know the full set of activated extras; this is only
|
||||
// computed below. We somehow need to add the dependency groups _after_ we've computed all
|
||||
// enabled extras, but the groups themselves could depend on the set of enabled extras.
|
||||
if !self.lock().conflicts().is_empty() {
|
||||
if needs_activation_context {
|
||||
for root_name in self.roots() {
|
||||
let dist = self
|
||||
.lock()
|
||||
@@ -347,7 +356,7 @@ pub trait Installable<'lock> {
|
||||
// activated extras. This includes the extras explicitly enabled on
|
||||
// the CLI (which were gathered above) and the extras enabled via
|
||||
// dependency specifications like `foo[extra]`. We need to do this
|
||||
// to correctly support conflicting extras.
|
||||
// to correctly support conflicting extras and source-selection markers.
|
||||
//
|
||||
// In particular, the way conflicting extras works is by forking the
|
||||
// resolver based on the extras that are declared as conflicting. But
|
||||
@@ -366,12 +375,7 @@ pub trait Installable<'lock> {
|
||||
// if it's enabled, we need to traverse the entire dependency graph
|
||||
// first to inspect which extras are enabled!
|
||||
//
|
||||
// Of course, we don't need to do this at all if there aren't any
|
||||
// conflicts. In which case, we skip all of this and just do the one
|
||||
// traversal below.
|
||||
if !self.lock().conflicts().is_empty() {
|
||||
let mut activated_extras_set: BTreeSet<(&PackageName, &ExtraName)> =
|
||||
activated_extras.iter().copied().collect();
|
||||
if needs_activation_context {
|
||||
let mut queue = queue.clone();
|
||||
let mut seen = seen.clone();
|
||||
while let Some((package, extra)) = queue.pop_front() {
|
||||
@@ -421,7 +425,6 @@ pub trait Installable<'lock> {
|
||||
// there are, we raise an error. ---AG
|
||||
|
||||
for key in additional_activated_extras {
|
||||
activated_extras_set.insert(key);
|
||||
activated_extras.push(key);
|
||||
}
|
||||
let dep_dist = self.lock().find_by_id(&dep.package_id);
|
||||
@@ -436,6 +439,11 @@ pub trait Installable<'lock> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.lock().conflicts().is_empty() {
|
||||
let activated_extras_set: BTreeSet<(&PackageName, &ExtraName)> =
|
||||
activated_extras.iter().copied().collect();
|
||||
// At time of writing, it's somewhat expected that the set of
|
||||
// conflicting extras is pretty small. With that said, the
|
||||
// time complexity of the following routine is pretty gross.
|
||||
|
||||
@@ -10463,6 +10463,45 @@ fn sync_multiple_sources_extra_url_conflict() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_multiple_sources_group_url_without_conflicts() -> Result<()> {
|
||||
let context = uv_test::test_context!("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>=2"]
|
||||
|
||||
[dependency-groups]
|
||||
alt = ["iniconfig"]
|
||||
|
||||
[tool.uv.sources]
|
||||
iniconfig = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", group = "alt" },
|
||||
]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.sync().arg("--group").arg("alt"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ iniconfig==2.0.0 (from https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl)
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_derivation_chain() -> Result<()> {
|
||||
let context = uv_test::test_context!("3.12");
|
||||
|
||||
Reference in New Issue
Block a user