Skip to content

Commit bee824f

Browse files
committed
Default to --workspace when adding subdirectories
1 parent 421e2c7 commit bee824f

File tree

5 files changed

+521
-41
lines changed

5 files changed

+521
-41
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3713,10 +3713,19 @@ pub struct AddArgs {
37133713

37143714
/// Add the dependency as a workspace member.
37153715
///
3716-
/// When used with a path dependency, the package will be added to the workspace's `members`
3717-
/// list in the root `pyproject.toml` file.
3718-
#[arg(long)]
3716+
/// By default, uv will add path dependencies that are within the workspace directory
3717+
/// as workspace members. When used with a path dependency, the package will be added
3718+
/// to the workspace's `members` list in the root `pyproject.toml` file.
3719+
#[arg(long, overrides_with = "no_workspace")]
37193720
pub workspace: bool,
3721+
3722+
/// Don't add the dependency as a workspace member.
3723+
///
3724+
/// By default, when adding a dependency that's a local path and is within the workspace
3725+
/// directory, uv will add it as a workspace member; pass `--no-workspace` to add the package
3726+
/// as direct path dependency instead.
3727+
#[arg(long, overrides_with = "workspace")]
3728+
pub no_workspace: bool,
37203729
}
37213730

37223731
#[derive(Args)]

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

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub(crate) async fn add(
8383
extras_of_dependency: Vec<ExtraName>,
8484
package: Option<PackageName>,
8585
python: Option<String>,
86-
workspace: bool,
86+
workspace: Option<bool>,
8787
install_mirrors: PythonInstallMirrors,
8888
settings: ResolverInstallerSettings,
8989
network_settings: NetworkSettings,
@@ -495,16 +495,41 @@ pub(crate) async fn add(
495495
// Track modification status, for reverts.
496496
let mut modified = false;
497497

498-
// If `--workspace` is provided, add any members to the `workspace` section of the
498+
// Determine whether to use workspace mode.
499+
let use_workspace = match workspace {
500+
Some(workspace) => workspace,
501+
None => {
502+
// Check if we're in a project (not a script), and if any requirements are path
503+
// dependencies within the workspace.
504+
if let AddTarget::Project(ref project, _) = target {
505+
let workspace_root = project.workspace().install_path();
506+
requirements.iter().any(|req| {
507+
if let RequirementSource::Directory { install_path, .. } = &req.source {
508+
let absolute_path = if install_path.is_absolute() {
509+
install_path.to_path_buf()
510+
} else {
511+
project.root().join(install_path)
512+
};
513+
absolute_path.starts_with(workspace_root)
514+
} else {
515+
false
516+
}
517+
})
518+
} else {
519+
false
520+
}
521+
}
522+
};
523+
524+
// If workspace mode is enabled, add any members to the `workspace` section of the
499525
// `pyproject.toml` file.
500-
if workspace {
526+
if use_workspace {
501527
let AddTarget::Project(project, python_target) = target else {
502528
unreachable!("`--workspace` and `--script` are conflicting options");
503529
};
504530

505-
let workspace = project.workspace();
506531
let mut toml = PyProjectTomlMut::from_toml(
507-
&workspace.pyproject_toml().raw,
532+
&project.workspace().pyproject_toml().raw,
508533
DependencyTarget::PyProjectToml,
509534
)?;
510535

@@ -517,21 +542,32 @@ pub(crate) async fn add(
517542
project.root().join(install_path)
518543
};
519544

520-
// Check if the path is not already included in the workspace.
521-
if !workspace.includes(&absolute_path)? {
522-
let relative_path = absolute_path
523-
.strip_prefix(workspace.install_path())
524-
.unwrap_or(&absolute_path);
525-
526-
toml.add_workspace(relative_path)?;
527-
modified |= true;
545+
// Either `--workspace` was provided explicitly, or it was omitted but the path is
546+
// within the workspace root.
547+
let use_workspace = workspace.unwrap_or_else(|| {
548+
absolute_path.starts_with(project.workspace().install_path())
549+
});
550+
if !use_workspace {
551+
continue;
552+
}
528553

529-
writeln!(
530-
printer.stderr(),
531-
"Added `{}` to workspace members",
532-
relative_path.user_display().cyan()
533-
)?;
554+
// If the project is already a member of the workspace, skip it.
555+
if project.workspace().includes(&absolute_path)? {
556+
continue;
534557
}
558+
559+
let relative_path = absolute_path
560+
.strip_prefix(project.workspace().install_path())
561+
.unwrap_or(&absolute_path);
562+
563+
toml.add_workspace(relative_path)?;
564+
modified |= true;
565+
566+
writeln!(
567+
printer.stderr(),
568+
"Added `{}` to workspace members",
569+
relative_path.user_display().cyan()
570+
)?;
535571
}
536572
}
537573

@@ -540,7 +576,7 @@ pub(crate) async fn add(
540576
target = if modified {
541577
let workspace_content = toml.to_string();
542578
fs_err::write(
543-
workspace.install_path().join("pyproject.toml"),
579+
project.workspace().install_path().join("pyproject.toml"),
544580
&workspace_content,
545581
)?;
546582

@@ -745,13 +781,13 @@ fn edits(
745781
.and_then(|tool| tool.uv.as_ref())
746782
.and_then(|uv| uv.sources.as_ref())
747783
.map(ToolUvSources::inner);
748-
let workspace = project
784+
let is_workspace_member = project
749785
.workspace()
750786
.packages()
751787
.contains_key(&requirement.name);
752788
resolve_requirement(
753789
requirement,
754-
workspace,
790+
is_workspace_member,
755791
editable,
756792
index.cloned(),
757793
rev.map(ToString::to_string),

crates/uv/src/settings.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,7 @@ pub(crate) struct AddSettings {
13291329
pub(crate) package: Option<PackageName>,
13301330
pub(crate) script: Option<PathBuf>,
13311331
pub(crate) python: Option<String>,
1332-
pub(crate) workspace: bool,
1332+
pub(crate) workspace: Option<bool>,
13331333
pub(crate) install_mirrors: PythonInstallMirrors,
13341334
pub(crate) refresh: Refresh,
13351335
pub(crate) indexes: Vec<Index>,
@@ -1368,6 +1368,7 @@ impl AddSettings {
13681368
script,
13691369
python,
13701370
workspace,
1371+
no_workspace,
13711372
} = args;
13721373

13731374
let dependency_type = if let Some(extra) = optional {
@@ -1468,7 +1469,7 @@ impl AddSettings {
14681469
package,
14691470
script,
14701471
python: python.and_then(Maybe::into_option),
1471-
workspace,
1472+
workspace: flag(workspace, no_workspace, "workspace"),
14721473
editable: flag(editable, no_editable, "editable"),
14731474
extras: extra.unwrap_or_default(),
14741475
refresh: Refresh::from(refresh),

0 commit comments

Comments
 (0)