Skip to content

Commit

Permalink
Add --group and --only-group to uv sync and includes all groups…
Browse files Browse the repository at this point in the history
… in `uv lock` (#8110)

Part of #8090

Adds the ability to include a group (`--group`) in the sync or _only_
sync a group (`--only-group`). Includes all groups in the resolution,
which will have the same limitations as extras as described in #6981.

There's a great deal of refactoring of the "development" concept into
"groups" behind the scenes that I am continuing to defer here to
minimize the diff.

Additionally, this does not yet resolve interactions with the existing
`dev` group — we'll tackle that separately as well. I probably won't
merge the stack until that design is resolved. The current proposal is
that we'll just "combine' the `dev-dependencies` contents into the `dev`
group.
  • Loading branch information
zanieb authored and charliermarsh committed Oct 22, 2024
1 parent a328d2e commit fad72f8
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 20 deletions.
14 changes: 14 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2788,6 +2788,20 @@ pub struct SyncArgs {
#[arg(long, conflicts_with("no_dev"))]
pub only_dev: bool,

/// Include dependencies from the specified local dependency group.
///
/// May be provided multiple times.
#[arg(long, conflicts_with("only_group"))]
pub group: Vec<GroupName>,

/// Only include dependencies from the specified local dependency group.
///
/// May be provided multiple times.
///
/// The project itself will also be omitted.
#[arg(long, conflicts_with("group"))]
pub only_group: Vec<GroupName>,

/// Install any editable dependencies, including the project and any workspace members, as
/// non-editable.
#[arg(long)]
Expand Down
36 changes: 36 additions & 0 deletions crates/uv-configuration/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,39 @@ impl From<DevMode> for DevSpecification {
}
}
}

impl DevSpecification {
/// Determine the [`DevSpecification`] policy from the command-line arguments.
pub fn from_args(
dev: bool,
no_dev: bool,
only_dev: bool,
group: Vec<GroupName>,
only_group: Vec<GroupName>,
) -> Self {
let from_mode = DevSpecification::from(DevMode::from_args(dev, no_dev, only_dev));
if !group.is_empty() {
match from_mode {
DevSpecification::Exclude => Self::Include(group),
DevSpecification::Include(dev) => {
Self::Include(group.into_iter().chain(dev).collect())
}
DevSpecification::Only(_) => {
unreachable!("cannot specify both `--only-dev` and `--group`")
}
}
} else if !only_group.is_empty() {
match from_mode {
DevSpecification::Exclude => Self::Only(only_group),
DevSpecification::Only(dev) => {
Self::Only(only_group.into_iter().chain(dev).collect())
}
// TODO(zanieb): `dev` defaults to true we can't tell if `--dev` was provided in
// conflict with `--only-group` here
DevSpecification::Include(_) => Self::Only(only_group),
}
} else {
from_mode
}
}
}
8 changes: 7 additions & 1 deletion crates/uv/src/commands/project/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,13 @@ async fn do_lock(
let requirements = workspace.non_project_requirements().collect::<Vec<_>>();
let overrides = workspace.overrides().into_iter().collect::<Vec<_>>();
let constraints = workspace.constraints();
let dev = vec![DEV_DEPENDENCIES.clone()];
let dev: Vec<_> = workspace
.pyproject_toml()
.dependency_groups
.iter()
.flat_map(|groups| groups.keys().cloned())
.chain(std::iter::once(DEV_DEPENDENCIES.clone()))
.collect();
let source_trees = vec![];

// Collect the list of members.
Expand Down
6 changes: 3 additions & 3 deletions crates/uv/src/commands/project/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use uv_auth::store_credentials;
use uv_cache::Cache;
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, Constraints, DevMode, DevSpecification, EditableMode, ExtrasSpecification,
Concurrency, Constraints, DevSpecification, EditableMode, ExtrasSpecification,
HashCheckingMode, InstallOptions, LowerBound,
};
use uv_dispatch::BuildDispatch;
Expand Down Expand Up @@ -43,7 +43,7 @@ pub(crate) async fn sync(
frozen: bool,
package: Option<PackageName>,
extras: ExtrasSpecification,
dev: DevMode,
dev: DevSpecification,
editable: EditableMode,
install_options: InstallOptions,
modifications: Modifications,
Expand Down Expand Up @@ -154,7 +154,7 @@ pub(crate) async fn sync(
&venv,
&lock,
&extras,
&DevSpecification::from(dev),
&dev,
editable,
install_options,
modifications,
Expand Down
14 changes: 8 additions & 6 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ use uv_cli::{
};
use uv_client::Connectivity;
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, DevMode, EditableMode, ExportFormat,
ExtrasSpecification, HashCheckingMode, IndexStrategy, InstallOptions, KeyringProviderType,
NoBinary, NoBuild, PreviewMode, ProjectBuildBackend, Reinstall, SourceStrategy, TargetTriple,
TrustedHost, TrustedPublishing, Upgrade, VersionControlSystem,
BuildOptions, Concurrency, ConfigSettings, DevMode, DevSpecification, EditableMode,
ExportFormat, ExtrasSpecification, HashCheckingMode, IndexStrategy, InstallOptions,
KeyringProviderType, NoBinary, NoBuild, PreviewMode, ProjectBuildBackend, Reinstall,
SourceStrategy, TargetTriple, TrustedHost, TrustedPublishing, Upgrade, VersionControlSystem,
};
use uv_distribution_types::{DependencyMetadata, Index, IndexLocations};
use uv_install_wheel::linker::LinkMode;
Expand Down Expand Up @@ -690,7 +690,7 @@ pub(crate) struct SyncSettings {
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) extras: ExtrasSpecification,
pub(crate) dev: DevMode,
pub(crate) dev: DevSpecification,
pub(crate) editable: EditableMode,
pub(crate) install_options: InstallOptions,
pub(crate) modifications: Modifications,
Expand All @@ -711,6 +711,8 @@ impl SyncSettings {
dev,
no_dev,
only_dev,
group,
only_group,
no_editable,
inexact,
exact,
Expand Down Expand Up @@ -738,7 +740,7 @@ impl SyncSettings {
flag(all_extras, no_all_extras).unwrap_or_default(),
extra.unwrap_or_default(),
),
dev: DevMode::from_args(dev, no_dev, only_dev),
dev: DevSpecification::from_args(dev, no_dev, only_dev, group, only_group),
editable: EditableMode::from_args(no_editable),
install_options: InstallOptions::new(
no_install_project,
Expand Down
27 changes: 18 additions & 9 deletions crates/uv/tests/it/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4247,8 +4247,12 @@ fn add_group() -> Result<()> {
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Audited in [TIME]
Resolved 4 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==3.7.0
+ idna==3.6
+ sniffio==1.3.1
"###);

let pyproject_toml = context.read("pyproject.toml");
Expand All @@ -4272,14 +4276,19 @@ fn add_group() -> Result<()> {
);
});

uv_snapshot!(context.filters(), context.add().arg("trio").arg("--group").arg("test"), @r###"
uv_snapshot!(context.filters(), context.add().arg("requests").arg("--group").arg("test"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Audited in [TIME]
Resolved 8 packages in [TIME]
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ certifi==2024.2.2
+ charset-normalizer==3.3.2
+ requests==2.31.0
+ urllib3==2.2.1
"###);

let pyproject_toml = context.read("pyproject.toml");
Expand All @@ -4298,7 +4307,7 @@ fn add_group() -> Result<()> {
[dependency-groups]
test = [
"anyio==3.7.0",
"trio",
"requests>=2.31.0",
]
"###
);
Expand All @@ -4310,8 +4319,8 @@ fn add_group() -> Result<()> {
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Audited in [TIME]
Resolved 8 packages in [TIME]
Audited 3 packages in [TIME]
"###);

let pyproject_toml = context.read("pyproject.toml");
Expand All @@ -4330,7 +4339,7 @@ fn add_group() -> Result<()> {
[dependency-groups]
test = [
"anyio==3.7.0",
"trio",
"requests>=2.31.0",
]
second = [
"anyio==3.7.0",
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/it/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12792,7 +12792,7 @@ fn lock_named_index_cli() -> Result<()> {

----- stderr -----
error: Failed to build: `project @ file://[TEMP_DIR]/`
Caused by: Failed to parse entry for: `jinja2`
Caused by: Failed to parse entry: `jinja2`
Caused by: Package `jinja2` references an undeclared index: `pytorch`
"###);

Expand Down
82 changes: 82 additions & 0 deletions crates/uv/tests/it/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,88 @@ fn sync_dev() -> Result<()> {
Ok(())
}

#[test]
fn sync_group() -> 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 = ["typing-extensions"]
[dependency-groups]
foo = ["anyio"]
bar = ["requests"]
"#,
)?;

context.lock().assert().success();

uv_snapshot!(context.filters(), context.sync(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ typing-extensions==4.10.0
"###);

uv_snapshot!(context.filters(), context.sync().arg("--group").arg("foo"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.3.0
+ idna==3.6
+ sniffio==1.3.1
"###);

uv_snapshot!(context.filters(), context.sync().arg("--only-group").arg("bar"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
Prepared 4 packages in [TIME]
Uninstalled 3 packages in [TIME]
Installed 4 packages in [TIME]
- anyio==4.3.0
+ certifi==2024.2.2
+ charset-normalizer==3.3.2
+ requests==2.31.0
- sniffio==1.3.1
- typing-extensions==4.10.0
+ urllib3==2.2.1
"###);

uv_snapshot!(context.filters(), context.sync().arg("--group").arg("foo").arg("--group").arg("bar"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 9 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.3.0
+ sniffio==1.3.1
+ typing-extensions==4.10.0
"###);

Ok(())
}

/// Regression test for <https://github.com/astral-sh/uv/issues/6316>.
///
/// Previously, we would read metadata statically from pyproject.toml and write that to `uv.lock`. In
Expand Down
10 changes: 10 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,10 @@ uv sync [OPTIONS]
<p>Instead of checking if the lockfile is up-to-date, uses the versions in the lockfile as the source of truth. If the lockfile is missing, uv will exit with an error. If the <code>pyproject.toml</code> includes changes to dependencies that have not been included in the lockfile yet, they will not be present in the environment.</p>

<p>May also be set with the <code>UV_FROZEN</code> environment variable.</p>
</dd><dt><code>--group</code> <i>group</i></dt><dd><p>Include dependencies from the specified local dependency group.</p>

<p>May be provided multiple times.</p>

</dd><dt><code>--help</code>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>

</dd><dt><code>--index</code> <i>index</i></dt><dd><p>The URLs to use when resolving dependencies, in addition to the default index.</p>
Expand Down Expand Up @@ -1555,6 +1559,12 @@ uv sync [OPTIONS]

<p>The project itself will also be omitted.</p>

</dd><dt><code>--only-group</code> <i>only-group</i></dt><dd><p>Only include dependencies from the specified local dependency group.</p>

<p>May be provided multiple times.</p>

<p>The project itself will also be omitted.</p>

</dd><dt><code>--package</code> <i>package</i></dt><dd><p>Sync for a specific package in the workspace.</p>

<p>The workspace&#8217;s environment (<code>.venv</code>) is updated to reflect the subset of dependencies declared by the specified workspace member package.</p>
Expand Down

0 comments on commit fad72f8

Please sign in to comment.