Skip to content

Commit 6bb37e4

Browse files
committed
Add to dependency-groups.dev in uv add --dev
1 parent a6b83fa commit 6bb37e4

File tree

3 files changed

+330
-26
lines changed

3 files changed

+330
-26
lines changed

crates/uv/src/commands/project/add.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use uv_distribution::DistributionDatabase;
2121
use uv_distribution_types::{Index, IndexName, UnresolvedRequirement, VersionId};
2222
use uv_fs::Simplified;
2323
use uv_git::{GitReference, GIT_STORE};
24-
use uv_normalize::PackageName;
24+
use uv_normalize::{PackageName, DEV_DEPENDENCIES};
2525
use uv_pep508::{ExtraName, Requirement, UnnamedRequirement, VersionOrUrl};
2626
use uv_pypi_types::{redact_credentials, ParsedUrl, RequirementSource, VerbatimParsedUrl};
2727
use uv_python::{
@@ -463,8 +463,32 @@ pub(crate) async fn add(
463463
_ => source,
464464
};
465465

466+
// Determine the dependency type.
467+
let dependency_type = match &dependency_type {
468+
DependencyType::Production => DependencyType::Production,
469+
DependencyType::Optional(extra) => DependencyType::Optional(extra.clone()),
470+
// If the requirement already exists in `dev-dependencies`; use that. Otherwise,
471+
// use `dependency-groups.dev`.
472+
DependencyType::Dev => {
473+
if toml.find_dependency(&requirement.name, None).is_empty() {
474+
DependencyType::Group(DEV_DEPENDENCIES.clone())
475+
} else {
476+
DependencyType::Dev
477+
}
478+
}
479+
DependencyType::Group(group) => {
480+
if group == &*DEV_DEPENDENCIES
481+
&& !toml.find_dependency(&requirement.name, None).is_empty()
482+
{
483+
DependencyType::Dev
484+
} else {
485+
DependencyType::Group(group.clone())
486+
}
487+
}
488+
};
489+
466490
// Update the `pyproject.toml`.
467-
let edit = match dependency_type {
491+
let edit = match &dependency_type {
468492
DependencyType::Production => toml.add_dependency(&requirement, source.as_ref())?,
469493
DependencyType::Dev => toml.add_dev_dependency(&requirement, source.as_ref())?,
470494
DependencyType::Optional(ref extra) => {
@@ -478,7 +502,7 @@ pub(crate) async fn add(
478502
// If the edit was inserted before the end of the list, update the existing edits.
479503
if let ArrayEdit::Add(index) = &edit {
480504
for edit in &mut edits {
481-
if *edit.dependency_type == dependency_type {
505+
if edit.dependency_type == dependency_type {
482506
match &mut edit.edit {
483507
ArrayEdit::Add(existing) => {
484508
if *existing >= *index {
@@ -496,7 +520,7 @@ pub(crate) async fn add(
496520
}
497521

498522
edits.push(DependencyEdit {
499-
dependency_type: &dependency_type,
523+
dependency_type,
500524
requirement,
501525
source,
502526
edit,
@@ -646,7 +670,7 @@ pub(crate) async fn add(
646670
async fn lock_and_sync(
647671
mut project: VirtualProject,
648672
toml: &mut PyProjectTomlMut,
649-
edits: &[DependencyEdit<'_>],
673+
edits: &[DependencyEdit],
650674
venv: &PythonEnvironment,
651675
state: SharedState,
652676
locked: bool,
@@ -983,8 +1007,8 @@ impl Target {
9831007
}
9841008

9851009
#[derive(Debug, Clone)]
986-
struct DependencyEdit<'a> {
987-
dependency_type: &'a DependencyType,
1010+
struct DependencyEdit {
1011+
dependency_type: DependencyType,
9881012
requirement: Requirement,
9891013
source: Option<Source>,
9901014
edit: ArrayEdit,

crates/uv/src/commands/project/remove.rs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use uv_configuration::{
99
Concurrency, DevGroupsManifest, EditableMode, ExtrasSpecification, InstallOptions, LowerBound,
1010
};
1111
use uv_fs::Simplified;
12+
use uv_normalize::DEV_DEPENDENCIES;
1213
use uv_pep508::PackageName;
1314
use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
1415
use uv_scripts::Pep723Script;
@@ -106,11 +107,13 @@ pub(crate) async fn remove(
106107
}
107108
}
108109
DependencyType::Dev => {
109-
let deps = toml.remove_dev_dependency(&package)?;
110-
if deps.is_empty() {
110+
let dev_deps = toml.remove_dev_dependency(&package)?;
111+
let group_deps =
112+
toml.remove_dependency_group_requirement(&package, &DEV_DEPENDENCIES)?;
113+
if dev_deps.is_empty() && group_deps.is_empty() {
111114
warn_if_present(&package, &toml);
112115
anyhow::bail!(
113-
"The dependency `{package}` could not be found in `dev-dependencies`"
116+
"The dependency `{package}` could not be found in `dev-dependencies` or `dependency-groups.dev`"
114117
);
115118
}
116119
}
@@ -124,12 +127,24 @@ pub(crate) async fn remove(
124127
}
125128
}
126129
DependencyType::Group(ref group) => {
127-
let deps = toml.remove_dependency_group_requirement(&package, group)?;
128-
if deps.is_empty() {
129-
warn_if_present(&package, &toml);
130-
anyhow::bail!(
131-
"The dependency `{package}` could not be found in `dependency-groups`"
132-
);
130+
if group == &*DEV_DEPENDENCIES {
131+
let dev_deps = toml.remove_dev_dependency(&package)?;
132+
let group_deps =
133+
toml.remove_dependency_group_requirement(&package, &DEV_DEPENDENCIES)?;
134+
if dev_deps.is_empty() && group_deps.is_empty() {
135+
warn_if_present(&package, &toml);
136+
anyhow::bail!(
137+
"The dependency `{package}` could not be found in `dev-dependencies` or `dependency-groups.dev`"
138+
);
139+
}
140+
} else {
141+
let deps = toml.remove_dependency_group_requirement(&package, group)?;
142+
if deps.is_empty() {
143+
warn_if_present(&package, &toml);
144+
anyhow::bail!(
145+
"The dependency `{package}` could not be found in `dependency-groups`"
146+
);
147+
}
133148
}
134149
}
135150
}
@@ -262,8 +277,10 @@ fn warn_if_present(name: &PackageName, pyproject: &PyProjectTomlMut) {
262277
"`{name}` is an optional dependency; try calling `uv remove --optional {group}`",
263278
);
264279
}
265-
DependencyType::Group(_) => {
266-
// TODO(zanieb): Once we support `remove --group`, add a warning here.
280+
DependencyType::Group(group) => {
281+
warn_user!(
282+
"`{name}` is a dependency in the `{group}` group; try calling `uv remove --group {group}`",
283+
);
267284
}
268285
}
269286
}

0 commit comments

Comments
 (0)