Skip to content

Commit 8b864be

Browse files
committed
Add support for default-groups
1 parent 0d12174 commit 8b864be

File tree

17 files changed

+510
-128
lines changed

17 files changed

+510
-128
lines changed

crates/uv-configuration/src/dev.rs

Lines changed: 99 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ impl DevMode {
2828
matches!(self, Self::Exclude | Self::Include)
2929
}
3030

31+
/// Returns `true` if the specification only includes development dependencies.
32+
pub fn only(&self) -> bool {
33+
matches!(self, Self::Only)
34+
}
35+
3136
/// Returns the flag that was used to request development dependencies.
3237
pub fn as_flag(&self) -> &'static str {
3338
match self {
@@ -38,7 +43,7 @@ impl DevMode {
3843
}
3944
}
4045

41-
#[derive(Debug, Clone)]
46+
#[derive(Default, Debug, Clone)]
4247
pub struct DevGroupsSpecification {
4348
/// Legacy option for `dependency-group.dev` and `tool.uv.dev-dependencies`.
4449
///
@@ -48,15 +53,13 @@ pub struct DevGroupsSpecification {
4853
/// The groups to include.
4954
///
5055
/// Requested via the `--group` and `--only-group` options.
51-
groups: GroupsSpecification,
56+
groups: Option<GroupsSpecification>,
5257
}
5358

5459
#[derive(Debug, Clone)]
5560
pub enum GroupsSpecification {
5661
/// Include dependencies from the specified groups.
5762
Include(Vec<GroupName>),
58-
/// Do not include dependencies from groups.
59-
Exclude,
6063
/// Only include dependencies from the specified groups, exclude all other dependencies.
6164
Only(Vec<GroupName>),
6265
}
@@ -65,20 +68,23 @@ impl GroupsSpecification {
6568
/// Returns an [`Iterator`] over the group names to include.
6669
pub fn iter(&self) -> impl Iterator<Item = &GroupName> {
6770
match self {
68-
Self::Exclude => Either::Left(std::iter::empty()),
69-
Self::Include(groups) | Self::Only(groups) => Either::Right(groups.iter()),
71+
Self::Include(groups) | Self::Only(groups) => groups.iter(),
7072
}
7173
}
7274

7375
/// Returns `true` if the specification allows for production dependencies.
7476
pub fn prod(&self) -> bool {
75-
matches!(self, Self::Exclude | Self::Include(_))
77+
matches!(self, Self::Include(_))
78+
}
79+
80+
/// Returns `true` if the specification is limited to a select set of groups.
81+
pub fn only(&self) -> bool {
82+
matches!(self, Self::Only(_))
7683
}
7784

7885
/// Returns the option that was used to request the groups, if any.
7986
pub fn as_flag(&self) -> Option<Cow<'_, str>> {
8087
match self {
81-
Self::Exclude => None,
8288
Self::Include(groups) => match groups.as_slice() {
8389
[] => None,
8490
[group] => Some(Cow::Owned(format!("--group {group}"))),
@@ -96,9 +102,13 @@ impl GroupsSpecification {
96102
impl DevGroupsSpecification {
97103
/// Returns an [`Iterator`] over the group names to include.
98104
pub fn iter(&self) -> impl Iterator<Item = &GroupName> {
99-
match self.dev {
100-
None => Either::Left(self.groups.iter()),
101-
Some(ref dev_mode) => Either::Right(self.groups.iter().chain(dev_mode.iter())),
105+
match (self.dev.as_ref(), self.groups.as_ref()) {
106+
(None, None) => Either::Left(Either::Left(std::iter::empty())),
107+
(Some(dev), None) => Either::Left(Either::Right(dev.iter())),
108+
(None, Some(groups)) => Either::Right(Either::Left(groups.iter())),
109+
(Some(dev), Some(groups)) => {
110+
Either::Right(Either::Right(dev.iter().chain(groups.iter())))
111+
}
102112
}
103113
}
104114

@@ -110,7 +120,7 @@ impl DevGroupsSpecification {
110120
group: Vec<GroupName>,
111121
only_group: Vec<GroupName>,
112122
) -> Self {
113-
let dev_mode = if only_dev {
123+
let dev = if only_dev {
114124
Some(DevMode::Only)
115125
} else if no_dev {
116126
Some(DevMode::Exclude)
@@ -121,70 +131,120 @@ impl DevGroupsSpecification {
121131
};
122132

123133
let groups = if !group.is_empty() {
124-
if matches!(dev_mode, Some(DevMode::Only)) {
134+
if matches!(dev, Some(DevMode::Only)) {
125135
unreachable!("cannot specify both `--only-dev` and `--group`")
126136
};
127-
GroupsSpecification::Include(group)
137+
Some(GroupsSpecification::Include(group))
128138
} else if !only_group.is_empty() {
129-
if matches!(dev_mode, Some(DevMode::Include)) {
139+
if matches!(dev, Some(DevMode::Include)) {
130140
unreachable!("cannot specify both `--dev` and `--only-group`")
131141
};
132-
GroupsSpecification::Only(only_group)
142+
Some(GroupsSpecification::Only(only_group))
133143
} else {
134-
GroupsSpecification::Exclude
144+
None
135145
};
136146

137-
Self {
138-
dev: dev_mode,
139-
groups,
140-
}
147+
Self { dev, groups }
141148
}
142149

143150
/// Return a new [`DevGroupsSpecification`] with development dependencies included by default.
144151
///
145152
/// This is appropriate in projects, where the `dev` group is synced by default.
146153
#[must_use]
147-
pub fn with_default_dev(self) -> Self {
148-
match self.dev {
149-
Some(_) => self,
150-
None => match self.groups {
151-
// Only include the default `dev` group if `--only-group` wasn't used
152-
GroupsSpecification::Only(_) => self,
153-
GroupsSpecification::Exclude | GroupsSpecification::Include(_) => Self {
154-
dev: Some(DevMode::Include),
155-
..self
156-
},
157-
},
154+
pub fn with_defaults(self, defaults: Vec<GroupName>) -> DevGroupsManifest {
155+
DevGroupsManifest {
156+
spec: self,
157+
defaults,
158158
}
159159
}
160160

161161
/// Returns `true` if the specification allows for production dependencies.
162162
pub fn prod(&self) -> bool {
163-
(self.dev.is_none() || self.dev.as_ref().is_some_and(DevMode::prod)) && self.groups.prod()
163+
self.dev.as_ref().map_or(true, DevMode::prod)
164+
&& self.groups.as_ref().map_or(true, GroupsSpecification::prod)
164165
}
165166

166-
/// Returns the flag that was used to request development dependencies.
167+
/// Returns `true` if the specification is limited to a select set of groups.
168+
pub fn only(&self) -> bool {
169+
self.dev.as_ref().is_some_and(DevMode::only)
170+
|| self.groups.as_ref().is_some_and(GroupsSpecification::only)
171+
}
172+
173+
/// Returns the flag that was used to request development dependencies, if specified.
167174
pub fn dev_mode(&self) -> Option<&DevMode> {
168175
self.dev.as_ref()
169176
}
170177

171-
/// Returns the list of groups to include.
172-
pub fn groups(&self) -> &GroupsSpecification {
173-
&self.groups
178+
/// Returns the list of groups to include, if specified.
179+
pub fn groups(&self) -> Option<&GroupsSpecification> {
180+
self.groups.as_ref()
174181
}
175182
}
176183

177184
impl From<DevMode> for DevGroupsSpecification {
178185
fn from(dev: DevMode) -> Self {
179186
Self {
180187
dev: Some(dev),
181-
groups: GroupsSpecification::Exclude,
188+
groups: None,
182189
}
183190
}
184191
}
185192

186193
impl From<GroupsSpecification> for DevGroupsSpecification {
187194
fn from(groups: GroupsSpecification) -> Self {
188-
Self { dev: None, groups }
195+
Self {
196+
dev: None,
197+
groups: Some(groups),
198+
}
199+
}
200+
}
201+
202+
/// The manifest of `dependency-groups` to include, taking into account the user-provided
203+
/// [`DevGroupsSpecification`] and the project-specific default groups.
204+
#[derive(Debug, Clone)]
205+
pub struct DevGroupsManifest {
206+
/// The specification for the development dependencies.
207+
pub(crate) spec: DevGroupsSpecification,
208+
/// The default groups to include.
209+
pub(crate) defaults: Vec<GroupName>,
210+
}
211+
212+
impl DevGroupsManifest {
213+
/// Returns a new [`DevGroupsManifest`] with the given default groups.
214+
pub fn from_defaults(defaults: Vec<GroupName>) -> Self {
215+
Self {
216+
spec: DevGroupsSpecification::default(),
217+
defaults,
218+
}
219+
}
220+
221+
/// Returns a new [`DevGroupsManifest`] with the given specification.
222+
pub fn from_spec(spec: DevGroupsSpecification) -> Self {
223+
Self {
224+
spec,
225+
defaults: Vec::new(),
226+
}
227+
}
228+
229+
/// Returns an [`Iterator`] over the group names to include.
230+
pub fn iter(&self) -> impl Iterator<Item = &GroupName> {
231+
if self.spec.only() {
232+
Either::Left(self.spec.iter())
233+
} else {
234+
Either::Right(
235+
self.spec
236+
.iter()
237+
.chain(self.defaults.iter().filter(|default| {
238+
// If `--no-dev` was provided, exclude the `dev` group from the list of defaults.
239+
!matches!(self.spec.dev_mode(), Some(DevMode::Exclude))
240+
|| *default != &*DEV_DEPENDENCIES
241+
})),
242+
)
243+
}
244+
}
245+
246+
/// Returns `true` if the specification allows for production dependencies.
247+
pub fn prod(&self) -> bool {
248+
self.spec.prod()
189249
}
190250
}

crates/uv-resolver/src/lock/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
1515
use url::Url;
1616

1717
use uv_cache_key::RepositoryUrl;
18-
use uv_configuration::{BuildOptions, DevGroupsSpecification, ExtrasSpecification, InstallOptions};
18+
use uv_configuration::{BuildOptions, DevGroupsManifest, ExtrasSpecification, InstallOptions};
1919
use uv_distribution::DistributionDatabase;
2020
use uv_distribution_filename::{DistExtension, ExtensionError, SourceDistExtension, WheelFilename};
2121
use uv_distribution_types::{
@@ -575,7 +575,7 @@ impl Lock {
575575
marker_env: &ResolverMarkerEnvironment,
576576
tags: &Tags,
577577
extras: &ExtrasSpecification,
578-
dev: &DevGroupsSpecification,
578+
dev: &DevGroupsManifest,
579579
build_options: &BuildOptions,
580580
install_options: &InstallOptions,
581581
) -> Result<Resolution, LockError> {

crates/uv-resolver/src/lock/requirements_txt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use petgraph::{Directed, Graph};
1010
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
1111
use url::Url;
1212

13-
use uv_configuration::{DevGroupsSpecification, EditableMode, ExtrasSpecification, InstallOptions};
13+
use uv_configuration::{DevGroupsManifest, EditableMode, ExtrasSpecification, InstallOptions};
1414
use uv_distribution_filename::{DistExtension, SourceDistExtension};
1515
use uv_fs::Simplified;
1616
use uv_git::GitReference;
@@ -43,7 +43,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
4343
lock: &'lock Lock,
4444
root_name: &PackageName,
4545
extras: &ExtrasSpecification,
46-
dev: &DevGroupsSpecification,
46+
dev: &DevGroupsManifest,
4747
editable: EditableMode,
4848
hashes: bool,
4949
install_options: &'lock InstallOptions,

crates/uv-resolver/src/lock/tree.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::collections::BTreeSet;
44
use itertools::Itertools;
55
use rustc_hash::{FxHashMap, FxHashSet};
66

7-
use uv_configuration::DevGroupsSpecification;
7+
use uv_configuration::DevGroupsManifest;
88
use uv_normalize::{ExtraName, GroupName, PackageName};
99
use uv_pypi_types::ResolverMarkerEnvironment;
1010

@@ -26,7 +26,7 @@ pub struct TreeDisplay<'env> {
2626
/// Maximum display depth of the dependency tree.
2727
depth: usize,
2828
/// Whether to include development dependencies in the display.
29-
dev: DevGroupsSpecification,
29+
dev: DevGroupsManifest,
3030
/// Prune the given packages from the display of the dependency tree.
3131
prune: Vec<PackageName>,
3232
/// Display only the specified packages.
@@ -43,7 +43,7 @@ impl<'env> TreeDisplay<'env> {
4343
depth: usize,
4444
prune: Vec<PackageName>,
4545
packages: Vec<PackageName>,
46-
dev: DevGroupsSpecification,
46+
dev: DevGroupsManifest,
4747
no_dedupe: bool,
4848
invert: bool,
4949
) -> Self {

crates/uv-settings/src/settings.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,11 +1563,13 @@ pub struct OptionsWire {
15631563
#[allow(dead_code)]
15641564
sources: Option<serde::de::IgnoredAny>,
15651565
#[allow(dead_code)]
1566-
dev_dependencies: Option<serde::de::IgnoredAny>,
1567-
#[allow(dead_code)]
15681566
managed: Option<serde::de::IgnoredAny>,
15691567
#[allow(dead_code)]
15701568
r#package: Option<serde::de::IgnoredAny>,
1569+
#[allow(dead_code)]
1570+
default_groups: Option<serde::de::IgnoredAny>,
1571+
#[allow(dead_code)]
1572+
dev_dependencies: Option<serde::de::IgnoredAny>,
15711573
}
15721574

15731575
impl From<OptionsWire> for Options {
@@ -1618,9 +1620,10 @@ impl From<OptionsWire> for Options {
16181620
trusted_publishing,
16191621
workspace: _,
16201622
sources: _,
1621-
dev_dependencies: _,
16221623
managed: _,
16231624
package: _,
1625+
default_groups: _,
1626+
dev_dependencies: _,
16241627
} = value;
16251628

16261629
Self {

0 commit comments

Comments
 (0)