mirror of https://github.com/astral-sh/uv
Add `--workspace` flag to `uv add` (#14496)
## Summary You can now pass `--workspace` to `uv add` to add a path dependency as a workspace member. Closes https://github.com/astral-sh/uv/issues/14464.
This commit is contained in:
parent
b1dc2b71a3
commit
4d061a6fc3
|
|
@ -3632,7 +3632,8 @@ pub struct AddArgs {
|
|||
long,
|
||||
conflicts_with = "dev",
|
||||
conflicts_with = "optional",
|
||||
conflicts_with = "package"
|
||||
conflicts_with = "package",
|
||||
conflicts_with = "workspace"
|
||||
)]
|
||||
pub script: Option<PathBuf>,
|
||||
|
||||
|
|
@ -3648,6 +3649,13 @@ pub struct AddArgs {
|
|||
value_parser = parse_maybe_string,
|
||||
)]
|
||||
pub python: Option<Maybe<String>>,
|
||||
|
||||
/// Add the dependency as a workspace member.
|
||||
///
|
||||
/// When used with a path dependency, the package will be added to the workspace's `members`
|
||||
/// list in the root `pyproject.toml` file.
|
||||
#[arg(long)]
|
||||
pub workspace: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ pub(crate) async fn add(
|
|||
extras_of_dependency: Vec<ExtraName>,
|
||||
package: Option<PackageName>,
|
||||
python: Option<String>,
|
||||
workspace: bool,
|
||||
install_mirrors: PythonInstallMirrors,
|
||||
settings: ResolverInstallerSettings,
|
||||
network_settings: NetworkSettings,
|
||||
|
|
@ -151,7 +152,7 @@ pub(crate) async fn add(
|
|||
// Default groups we need the actual project for, interpreter discovery will use this!
|
||||
let defaulted_groups;
|
||||
|
||||
let target = if let Some(script) = script {
|
||||
let mut target = if let Some(script) = script {
|
||||
// If we found a PEP 723 script and the user provided a project-only setting, warn.
|
||||
if package.is_some() {
|
||||
warn_user_once!(
|
||||
|
|
@ -478,6 +479,9 @@ pub(crate) async fn add(
|
|||
}
|
||||
}
|
||||
|
||||
// Store the content prior to any modifications.
|
||||
let snapshot = target.snapshot().await?;
|
||||
|
||||
// If the user provides a single, named index, pin all requirements to that index.
|
||||
let index = indexes
|
||||
.first()
|
||||
|
|
@ -488,7 +492,72 @@ pub(crate) async fn add(
|
|||
debug!("Pinning all requirements to index: `{index}`");
|
||||
});
|
||||
|
||||
// Add the requirements to the `pyproject.toml` or script.
|
||||
// Track modification status, for reverts.
|
||||
let mut modified = false;
|
||||
|
||||
// If `--workspace` is provided, add any members to the `workspace` section of the
|
||||
// `pyproject.toml` file.
|
||||
if workspace {
|
||||
let AddTarget::Project(project, python_target) = target else {
|
||||
unreachable!("`--workspace` and `--script` are conflicting options");
|
||||
};
|
||||
|
||||
let workspace = project.workspace();
|
||||
let mut toml = PyProjectTomlMut::from_toml(
|
||||
&workspace.pyproject_toml().raw,
|
||||
DependencyTarget::PyProjectToml,
|
||||
)?;
|
||||
|
||||
// Check each requirement to see if it's a path dependency
|
||||
for requirement in &requirements {
|
||||
if let RequirementSource::Directory { install_path, .. } = &requirement.source {
|
||||
let absolute_path = if install_path.is_absolute() {
|
||||
install_path.to_path_buf()
|
||||
} else {
|
||||
project.root().join(install_path)
|
||||
};
|
||||
|
||||
// Check if the path is not already included in the workspace.
|
||||
if !workspace.includes(&absolute_path)? {
|
||||
let relative_path = absolute_path
|
||||
.strip_prefix(workspace.install_path())
|
||||
.unwrap_or(&absolute_path);
|
||||
|
||||
toml.add_workspace(relative_path)?;
|
||||
modified |= true;
|
||||
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Added `{}` to workspace members",
|
||||
relative_path.user_display().cyan()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we modified the workspace root, we need to reload it entirely, since this can impact
|
||||
// the discovered members, etc.
|
||||
target = if modified {
|
||||
let workspace_content = toml.to_string();
|
||||
fs_err::write(
|
||||
workspace.install_path().join("pyproject.toml"),
|
||||
&workspace_content,
|
||||
)?;
|
||||
|
||||
AddTarget::Project(
|
||||
VirtualProject::discover(
|
||||
project.root(),
|
||||
&DiscoveryOptions::default(),
|
||||
&WorkspaceCache::default(),
|
||||
)
|
||||
.await?,
|
||||
python_target,
|
||||
)
|
||||
} else {
|
||||
AddTarget::Project(project, python_target)
|
||||
}
|
||||
}
|
||||
|
||||
let mut toml = match &target {
|
||||
AddTarget::Script(script, _) => {
|
||||
PyProjectTomlMut::from_toml(&script.metadata.raw, DependencyTarget::Script)
|
||||
|
|
@ -498,6 +567,7 @@ pub(crate) async fn add(
|
|||
DependencyTarget::PyProjectToml,
|
||||
),
|
||||
}?;
|
||||
|
||||
let edits = edits(
|
||||
requirements,
|
||||
&target,
|
||||
|
|
@ -543,7 +613,7 @@ pub(crate) async fn add(
|
|||
let content = toml.to_string();
|
||||
|
||||
// Save the modified `pyproject.toml` or script.
|
||||
let modified = target.write(&content)?;
|
||||
modified |= target.write(&content)?;
|
||||
|
||||
// If `--frozen`, exit early. There's no reason to lock and sync, since we don't need a `uv.lock`
|
||||
// to exist at all.
|
||||
|
|
@ -563,9 +633,6 @@ pub(crate) async fn add(
|
|||
}
|
||||
}
|
||||
|
||||
// Store the content prior to any modifications.
|
||||
let snapshot = target.snapshot().await?;
|
||||
|
||||
// Update the `pypackage.toml` in-memory.
|
||||
let target = target.update(&content)?;
|
||||
|
||||
|
|
@ -1296,6 +1363,16 @@ impl AddTargetSnapshot {
|
|||
Ok(())
|
||||
}
|
||||
Self::Project(project, lock) => {
|
||||
// Write the workspace `pyproject.toml` back to disk.
|
||||
let workspace = project.workspace();
|
||||
if workspace.install_path() != project.root() {
|
||||
debug!("Reverting changes to workspace `pyproject.toml`");
|
||||
fs_err::write(
|
||||
workspace.install_path().join("pyproject.toml"),
|
||||
workspace.pyproject_toml().as_ref(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Write the `pyproject.toml` back to disk.
|
||||
debug!("Reverting changes to `pyproject.toml`");
|
||||
fs_err::write(
|
||||
|
|
|
|||
|
|
@ -1965,6 +1965,7 @@ async fn run_project(
|
|||
args.extras,
|
||||
args.package,
|
||||
args.python,
|
||||
args.workspace,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
globals.network_settings,
|
||||
|
|
|
|||
|
|
@ -1326,6 +1326,7 @@ pub(crate) struct AddSettings {
|
|||
pub(crate) package: Option<PackageName>,
|
||||
pub(crate) script: Option<PathBuf>,
|
||||
pub(crate) python: Option<String>,
|
||||
pub(crate) workspace: bool,
|
||||
pub(crate) install_mirrors: PythonInstallMirrors,
|
||||
pub(crate) refresh: Refresh,
|
||||
pub(crate) indexes: Vec<Index>,
|
||||
|
|
@ -1363,6 +1364,7 @@ impl AddSettings {
|
|||
package,
|
||||
script,
|
||||
python,
|
||||
workspace,
|
||||
} = args;
|
||||
|
||||
let dependency_type = if let Some(extra) = optional {
|
||||
|
|
@ -1463,6 +1465,7 @@ impl AddSettings {
|
|||
package,
|
||||
script,
|
||||
python: python.and_then(Maybe::into_option),
|
||||
workspace,
|
||||
editable: flag(editable, no_editable, "editable"),
|
||||
extras: extra.unwrap_or_default(),
|
||||
refresh: Refresh::from(refresh),
|
||||
|
|
|
|||
|
|
@ -7210,6 +7210,7 @@ fn remove_include_default_groups() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Revert changes to the `pyproject.toml` and `uv.lock` when the `add` operation fails.
|
||||
#[test]
|
||||
fn fail_to_add_revert_project() -> Result<()> {
|
||||
|
|
@ -7401,6 +7402,256 @@ fn fail_to_edit_revert_project() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Revert changes to the root `pyproject.toml` and `uv.lock` when the `add` operation fails.
|
||||
#[test]
|
||||
fn fail_to_add_revert_workspace_root() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
context
|
||||
.temp_dir
|
||||
.child("pyproject.toml")
|
||||
.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
"#})?;
|
||||
|
||||
// Add a dependency on a package that declares static metadata (so can always resolve), but
|
||||
// can't be installed.
|
||||
let pyproject_toml = context.temp_dir.child("child/pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#})?;
|
||||
context
|
||||
.temp_dir
|
||||
.child("child")
|
||||
.child("setup.py")
|
||||
.write_str("1/0")?;
|
||||
|
||||
// Add a dependency on a package that declares static metadata (so can always resolve), but
|
||||
// can't be installed.
|
||||
let pyproject_toml = context.temp_dir.child("broken").child("pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "broken"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#})?;
|
||||
context
|
||||
.temp_dir
|
||||
.child("broken")
|
||||
.child("setup.py")
|
||||
.write_str("1/0")?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.add().arg("--workspace").arg("./broken"), @r#"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Added `broken` to workspace members
|
||||
Resolved 3 packages in [TIME]
|
||||
× Failed to build `broken @ file://[TEMP_DIR]/broken`
|
||||
├─▶ The build backend returned an error
|
||||
╰─▶ Call to `setuptools.build_meta.build_editable` failed (exit status: 1)
|
||||
|
||||
[stderr]
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 14, in <module>
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 448, in get_requires_for_build_editable
|
||||
return self.get_requires_for_build_wheel(config_settings)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 325, in get_requires_for_build_wheel
|
||||
return self._get_build_requires(config_settings, requirements=['wheel'])
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 295, in _get_build_requires
|
||||
self.run_setup()
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 311, in run_setup
|
||||
exec(code, locals())
|
||||
File "<string>", line 1, in <module>
|
||||
ZeroDivisionError: division by zero
|
||||
|
||||
hint: This usually indicates a problem with the package or the build environment.
|
||||
help: If you want to add the package regardless of the failed resolution, provide the `--frozen` flag to skip locking and syncing.
|
||||
"#);
|
||||
|
||||
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
|
||||
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject_toml, @r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
||||
// The lockfile should not exist, even though resolution succeeded.
|
||||
assert!(!context.temp_dir.join("uv.lock").exists());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Revert changes to the root `pyproject.toml` and `uv.lock` when the `add` operation fails.
|
||||
#[test]
|
||||
fn fail_to_add_revert_workspace_member() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
context
|
||||
.temp_dir
|
||||
.child("pyproject.toml")
|
||||
.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["child"]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["child"]
|
||||
|
||||
[tool.uv.sources]
|
||||
child = { workspace = true }
|
||||
"#})?;
|
||||
|
||||
// Add a workspace dependency.
|
||||
let project = context.temp_dir.child("child");
|
||||
project.child("pyproject.toml").write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
"#})?;
|
||||
project
|
||||
.child("src")
|
||||
.child("child")
|
||||
.child("__init__.py")
|
||||
.touch()?;
|
||||
|
||||
// Add a dependency on a package that declares static metadata (so can always resolve), but
|
||||
// can't be installed.
|
||||
let pyproject_toml = context.temp_dir.child("broken/pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "broken"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#})?;
|
||||
context
|
||||
.temp_dir
|
||||
.child("broken")
|
||||
.child("setup.py")
|
||||
.write_str("1/0")?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.add().current_dir(&project).arg("--workspace").arg("../broken"), @r#"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Added `broken` to workspace members
|
||||
Resolved 4 packages in [TIME]
|
||||
× Failed to build `broken @ file://[TEMP_DIR]/broken`
|
||||
├─▶ The build backend returned an error
|
||||
╰─▶ Call to `setuptools.build_meta.build_editable` failed (exit status: 1)
|
||||
|
||||
[stderr]
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 14, in <module>
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 448, in get_requires_for_build_editable
|
||||
return self.get_requires_for_build_wheel(config_settings)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 325, in get_requires_for_build_wheel
|
||||
return self._get_build_requires(config_settings, requirements=['wheel'])
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 295, in _get_build_requires
|
||||
self.run_setup()
|
||||
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 311, in run_setup
|
||||
exec(code, locals())
|
||||
File "<string>", line 1, in <module>
|
||||
ZeroDivisionError: division by zero
|
||||
|
||||
hint: This usually indicates a problem with the package or the build environment.
|
||||
help: If you want to add the package regardless of the failed resolution, provide the `--frozen` flag to skip locking and syncing.
|
||||
"#);
|
||||
|
||||
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject_toml, @r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["child"]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["child"]
|
||||
|
||||
[tool.uv.sources]
|
||||
child = { workspace = true }
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
||||
let pyproject_toml =
|
||||
fs_err::read_to_string(context.temp_dir.join("child").join("pyproject.toml"))?;
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
pyproject_toml, @r#"
|
||||
[project]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
"#
|
||||
);
|
||||
});
|
||||
|
||||
// The lockfile should not exist, even though resolution succeeded.
|
||||
assert!(!context.temp_dir.join("uv.lock").exists());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure that the added dependencies are sorted if the dependency list was already sorted prior
|
||||
/// to the operation.
|
||||
#[test]
|
||||
|
|
@ -12629,3 +12880,163 @@ fn add_bounds_requirement_over_bounds_kind() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a path dependency with `--workspace` flag to add it to workspace members. The root already
|
||||
/// contains a workspace definition, so the package should be added to the workspace members.
|
||||
#[test]
|
||||
fn add_path_with_existing_workspace() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let workspace_toml = context.temp_dir.child("pyproject.toml");
|
||||
workspace_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["project"]
|
||||
"#})?;
|
||||
|
||||
// Create a project within the workspace.
|
||||
let project_dir = context.temp_dir.child("project");
|
||||
project_dir.create_dir_all()?;
|
||||
|
||||
let project_toml = project_dir.child("pyproject.toml");
|
||||
project_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
"#})?;
|
||||
|
||||
// Create a dependency package outside the workspace members.
|
||||
let dep_dir = context.temp_dir.child("dep");
|
||||
dep_dir.create_dir_all()?;
|
||||
|
||||
let dep_toml = dep_dir.child("pyproject.toml");
|
||||
dep_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "dep"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
"#})?;
|
||||
|
||||
// Add the dependency with `--workspace` flag from the project directory.
|
||||
uv_snapshot!(context.filters(), context
|
||||
.add()
|
||||
.current_dir(&project_dir)
|
||||
.arg("../dep")
|
||||
.arg("--workspace"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Added `dep` to workspace members
|
||||
Resolved 3 packages in [TIME]
|
||||
Audited in [TIME]
|
||||
");
|
||||
|
||||
let pyproject_toml = context.read("pyproject.toml");
|
||||
assert_snapshot!(
|
||||
pyproject_toml, @r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = [
|
||||
"project",
|
||||
"dep",
|
||||
]
|
||||
"#
|
||||
);
|
||||
|
||||
let pyproject_toml = context.read("project/pyproject.toml");
|
||||
assert_snapshot!(
|
||||
pyproject_toml, @r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"dep",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
dep = { workspace = true }
|
||||
"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a path dependency with `--workspace` flag to add it to workspace members. The root doesn't
|
||||
/// contain a workspace definition, so `uv add` should create one.
|
||||
#[test]
|
||||
fn add_path_with_workspace() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let workspace_toml = context.temp_dir.child("pyproject.toml");
|
||||
workspace_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
"#})?;
|
||||
|
||||
// Create a dependency package outside the workspace members.
|
||||
let dep_dir = context.temp_dir.child("dep");
|
||||
dep_dir.create_dir_all()?;
|
||||
|
||||
let dep_toml = dep_dir.child("pyproject.toml");
|
||||
dep_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "dep"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
"#})?;
|
||||
|
||||
// Add the dependency with `--workspace` flag from the project directory.
|
||||
uv_snapshot!(context.filters(), context
|
||||
.add()
|
||||
.arg("./dep")
|
||||
.arg("--workspace"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Added `dep` to workspace members
|
||||
Resolved 2 packages in [TIME]
|
||||
Audited in [TIME]
|
||||
");
|
||||
|
||||
let pyproject_toml = context.read("pyproject.toml");
|
||||
assert_snapshot!(
|
||||
pyproject_toml, @r#"
|
||||
[project]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"dep",
|
||||
]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = [
|
||||
"dep",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
dep = { workspace = true }
|
||||
"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -582,6 +582,8 @@ uv add [OPTIONS] <PACKAGES|--requirements <REQUIREMENTS>>
|
|||
</dd><dt id="uv-add--upgrade-package"><a href="#uv-add--upgrade-package"><code>--upgrade-package</code></a>, <code>-P</code> <i>upgrade-package</i></dt><dd><p>Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies <code>--refresh-package</code></p>
|
||||
</dd><dt id="uv-add--verbose"><a href="#uv-add--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
|
||||
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>
|
||||
</dd><dt id="uv-add--workspace"><a href="#uv-add--workspace"><code>--workspace</code></a></dt><dd><p>Add the dependency as a workspace member.</p>
|
||||
<p>When used with a path dependency, the package will be added to the workspace's <code>members</code> list in the root <code>pyproject.toml</code> file.</p>
|
||||
</dd></dl>
|
||||
|
||||
## uv remove
|
||||
|
|
|
|||
Loading…
Reference in New Issue