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`
  • Loading branch information
zanieb committed Oct 16, 2024
1 parent f701089 commit f192195
Show file tree
Hide file tree
Showing 10 changed files with 197 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
25 changes: 17 additions & 8 deletions crates/uv/tests/it/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4081,8 +4081,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 Down Expand Up @@ -4112,8 +4116,13 @@ fn add_group() -> Result<()> {
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Audited in [TIME]
Resolved 10 packages in [TIME]
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ attrs==23.2.0
+ outcome==1.3.0.post0
+ sortedcontainers==2.4.0
+ trio==0.25.0
"###);

let pyproject_toml = context.read("pyproject.toml");
Expand All @@ -4132,7 +4141,7 @@ fn add_group() -> Result<()> {
[dependency-groups]
test = [
"anyio==3.7.0",
"trio",
"trio>=0.25.0",
]
"###
);
Expand All @@ -4144,8 +4153,8 @@ fn add_group() -> Result<()> {
----- stdout -----
----- stderr -----
Resolved 1 package in [TIME]
Audited in [TIME]
Resolved 10 packages in [TIME]
Audited 3 packages in [TIME]
"###);

let pyproject_toml = context.read("pyproject.toml");
Expand All @@ -4164,7 +4173,7 @@ fn add_group() -> Result<()> {
[dependency-groups]
test = [
"anyio==3.7.0",
"trio",
"trio>=0.25.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 @@ -12319,7 +12319,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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv/tests/ecosystem.rs
source: crates/uv/tests/it/ecosystem.rs
expression: lock
---
version = 1
Expand Down Expand Up @@ -1299,11 +1299,31 @@ source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2d/eb/9c097e058c9d5bb7cd39b32730397d645856a81360b4e49cafe16ec1f358/pillow-avif-plugin-1.4.6.tar.gz", hash = "sha256:855cf50d03f6fc16e1fd5e364b3cea0b79f4bf90d39ff2123969735d851e08ba", size = 19632 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b2/f7/460c854c3f4a9802aabd0a25b4814a7e5902c776a6501498a4078bf2a0d3/pillow_avif_plugin-1.4.6-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e2087daa49881421a5e703fcff80aa2cbcb5a455cf73114ed5f0ea2a697794c8", size = 7980980 },
{ url = "https://files.pythonhosted.org/packages/26/ce/4e84b4caf933c4e938076b238e57245157a501d9a990451024d91e3dae9d/pillow_avif_plugin-1.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc3256fd23f5c7bef4bcc562db9d2cd04634a7b01dee41ea35e8e92a2334a949", size = 8016324 },
{ url = "https://files.pythonhosted.org/packages/f5/11/2f0fa7d135f91a8e34d9040b18a899d185776a642f5773ca33d45b0996ba/pillow_avif_plugin-1.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5bacc0802516f054f98d9f218ada17b2e8a756e35cb71e7401bb8422848fe796", size = 5743257 },
{ url = "https://files.pythonhosted.org/packages/ab/11/d26281b45864aed5a157896ecc77d2d941d072ffac840d2a2c4a81b9f1a1/pillow_avif_plugin-1.4.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbd9a1a45d982e346063ec4f4ce100021c565ee3102f9ff7f678019d5febada8", size = 12296355 },
{ url = "https://files.pythonhosted.org/packages/24/b6/5a2fda66a192c0a372bcd7968c5914ccc6dcd48cd57b2f6cccba4587e209/pillow_avif_plugin-1.4.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e74e53951228c3e6ff5141121bd2876e8aecdb27d5f12d01cc519258e0073d8b", size = 6431301 },
{ url = "https://files.pythonhosted.org/packages/ac/1d/2d6f816e15e56b053758fbd6d625fbd79b5cf22e775fce9967b83ede8c31/pillow_avif_plugin-1.4.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37e1314500cec3457210f4c8a7583afe35751f076efa8122faa0f205403d645", size = 7984138 },
{ url = "https://files.pythonhosted.org/packages/0e/05/a3750549f914763d34c9a1dc1c4d78e2fe5021ad26faa71892f18c34b073/pillow_avif_plugin-1.4.6-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3008acd3edf86e8e2291d40e9f9eae86f5140415431d21f219df5ca8e8210115", size = 14667148 },
{ url = "https://files.pythonhosted.org/packages/86/36/32e9576c512fb53096ee050a112a12c6054c4e9c6ce2ec9e7e6f4d9d5d11/pillow_avif_plugin-1.4.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d643db246d6c07994fbb98b5fa6c6ae8f9b19b4ed24566bc06942b7dad10ad47", size = 8123272 },
{ url = "https://files.pythonhosted.org/packages/73/39/8955b693f0983d4f9326dae2df3135bb455011d474baee6ba9273ccf56ac/pillow_avif_plugin-1.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d0d96859d1ebfd7c6c8daa761a05ee9df0a70278ec3011b3b5c6e56ac4996fa7", size = 12489803 },
{ url = "https://files.pythonhosted.org/packages/b2/0f/327be9b4aca874d7d8b05b4e3300e55b3123611ee26f6ec1f055d1cb0f74/pillow_avif_plugin-1.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d1654a1048ad09b6e7553d4eb6e6bd3848be512bab2e283275585609dbda8b0", size = 14911324 },
{ url = "https://files.pythonhosted.org/packages/f0/5f/0bb9ec1910a5ece813ac6324b1d0f148cf71a0e5297ab8fcfce1e48a4ebe/pillow_avif_plugin-1.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:f262547edeec00ad287c8845ac6c9d7d822ef4b00d1832175c4c8fd692e34eba", size = 10564587 },
{ url = "https://files.pythonhosted.org/packages/3c/e2/eb6f75563b04188991bbe17d0c0350d9bb61886fa2660b749f34e555f3f2/pillow_avif_plugin-1.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1d43a5de556e2ab8437e9d8b07da96e968e0498cb3c0d448c34198c7bac0387c", size = 8016288 },
{ url = "https://files.pythonhosted.org/packages/46/c3/675d2de2a68761f5125751f724217d6177ca6e8e78b889db6207441b1e3d/pillow_avif_plugin-1.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ce3fd54c76845c24dcd1cbc73fa5c72969df191bf7cd388a446f2f38342885c", size = 5676479 },
{ url = "https://files.pythonhosted.org/packages/21/33/7ee19bbbfb0568ea2f0dba644325e4111e65f7f7e99b9f1dcc956aa2d304/pillow_avif_plugin-1.4.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3c8d4f32a36ef4c345660a708067b6074dd380d0585589867333f7cb350bb9d", size = 12296248 },
{ url = "https://files.pythonhosted.org/packages/ce/40/51ba62d38a3ece90f5f22e641f0be952972a3c8238dd740e63c8d483dea3/pillow_avif_plugin-1.4.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4563e5cd130016f8ea17602422b36abe4f63d2073ba98f2dbed42377b2f91c", size = 12214782 },
{ url = "https://files.pythonhosted.org/packages/bb/e2/7c6bce378c0d6c7a1a9f8a544e71e06a0a519c6d9ffc8e538dd015977a10/pillow_avif_plugin-1.4.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070b07e47012a3490ab56e62dd629cbe694240159df333f01692c3e5fb8acff8", size = 14394606 },
{ url = "https://files.pythonhosted.org/packages/31/33/a2f0785fcf20542fdfacea4390d3d1c5da26e7faeb10d9fb26cf374cd131/pillow_avif_plugin-1.4.6-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:57e883963205b7cfe2981ca98db6554c488fbff2b5a6751cfcf065c6e657a922", size = 14666959 },
{ url = "https://files.pythonhosted.org/packages/cd/12/be3b001225d9ed4b2e3119682315b4a51bf3e3114a24664096b73bd31512/pillow_avif_plugin-1.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ba1ae4dd323f019e0a1750555b02d91934724e4d556638baa60b5ca62e30f353", size = 12489897 },
{ url = "https://files.pythonhosted.org/packages/21/0a/6aa0f761308054b2aa7488ef9583c3125d1d2260b7d7482da0592a96ecaa/pillow_avif_plugin-1.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:73b840b612a6ce840e2206a9f097f4ad07c1ca4ed99a3b0d14444224daf55e88", size = 14911538 },
{ url = "https://files.pythonhosted.org/packages/19/80/3bdee7cd75ce7a1f20c2e1039d4a0e469185551032b95c68a58159b9fc9b/pillow_avif_plugin-1.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:a79fc89bae89be6bede9b4b01aff3967cd009c02edec1e70e6de8e52ef93b5fc", size = 10728675 },
{ url = "https://files.pythonhosted.org/packages/db/7e/fb4b950f16c3f43a030604a7f0672137c3c4a4b3a4fa98f3390dd88d0fa2/pillow_avif_plugin-1.4.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b1461731cde80ea246bce6ff87320367dbba206ee51a3370d99e679540dbcc17", size = 8016566 },
{ url = "https://files.pythonhosted.org/packages/1d/fb/0e3e2a252f2d92a8f453633144d35060d9bf53f5f5c7810f1fd0120bc1dd/pillow_avif_plugin-1.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fe32db84ba0c9d9b364e2b36a55620d6112133107f82854f19a4fdaa93fce66b", size = 5675393 },
{ url = "https://files.pythonhosted.org/packages/82/03/57b1c557ae05bf81cbcb2aa4cefd357861bdcfd1b055d53afd23d02902e8/pillow_avif_plugin-1.4.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ac4e238f172806b2f75a719895414ff8c67500ab0bfa691e53ee2a99cb85722", size = 14396630 },
{ url = "https://files.pythonhosted.org/packages/c2/00/12ec4d85453b85285b021e61bb027cadbf1bbb76b75d081ba0df7bc98d2a/pillow_avif_plugin-1.4.6-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38a3934bcce34eb1f457434b336e4df8da1cf1eaea9306ca9f12ab5fa466e5a9", size = 14671480 },
{ url = "https://files.pythonhosted.org/packages/9d/9e/c922cdc8168c526201e3927fee37605b2d0df93dc9e6b2586f65cd6fd35a/pillow_avif_plugin-1.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:70b4e26bc604d7af87a5e5605b3480db9832eab0dbcf0b86565a813716653cce", size = 14914496 },
{ url = "https://files.pythonhosted.org/packages/30/1e/315c22079058ffc2b8c6028cb18e33f7dc420ec56c806e8428419347f427/pillow_avif_plugin-1.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:10e9b2ef297a9825b461715359ae233d6518d9863c877a8652c14d6acae6e9f0", size = 10729094 },
]

[[package]]
Expand Down
80 changes: 80 additions & 0 deletions crates/uv/tests/it/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,86 @@ 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 = ["trio"]
"#,
)?;

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

uv_snapshot!(context.filters(), context.sync(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 11 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 11 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 11 packages in [TIME]
Prepared 4 packages in [TIME]
Uninstalled 2 packages in [TIME]
Installed 4 packages in [TIME]
- anyio==4.3.0
+ attrs==23.2.0
+ outcome==1.3.0.post0
+ sortedcontainers==2.4.0
+ trio==0.25.0
- typing-extensions==4.10.0
"###);

uv_snapshot!(context.filters(), context.sync().arg("--group").arg("foo").arg("--group").arg("bar"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 11 packages in [TIME]
Installed 2 packages in [TIME]
+ anyio==4.3.0
+ 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 @@ -1407,6 +1407,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>

</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 @@ -1545,6 +1549,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 f192195

Please sign in to comment.