mirror of https://github.com/astral-sh/uv
Default to `--workspace` when adding subdirectories (#14529)
If `--workspace` is provided, we add all paths as workspace members. If `--no-workspace` is provided, we add all paths as direct path dependencies. If neither is provided, then we add any paths that are under the workspace root as workspace members, and the rest as direct path dependencies. Closes #14524.
This commit is contained in:
parent
e4c04af32d
commit
c3d7d3899c
|
|
@ -3726,10 +3726,19 @@ pub struct AddArgs {
|
||||||
|
|
||||||
/// Add the dependency as a workspace member.
|
/// Add the dependency as a workspace member.
|
||||||
///
|
///
|
||||||
/// When used with a path dependency, the package will be added to the workspace's `members`
|
/// By default, uv will add path dependencies that are within the workspace directory
|
||||||
/// list in the root `pyproject.toml` file.
|
/// as workspace members. When used with a path dependency, the package will be added
|
||||||
#[arg(long)]
|
/// to the workspace's `members` list in the root `pyproject.toml` file.
|
||||||
|
#[arg(long, overrides_with = "no_workspace")]
|
||||||
pub workspace: bool,
|
pub workspace: bool,
|
||||||
|
|
||||||
|
/// Don't add the dependency as a workspace member.
|
||||||
|
///
|
||||||
|
/// By default, when adding a dependency that's a local path and is within the workspace
|
||||||
|
/// directory, uv will add it as a workspace member; pass `--no-workspace` to add the package
|
||||||
|
/// as direct path dependency instead.
|
||||||
|
#[arg(long, overrides_with = "workspace")]
|
||||||
|
pub no_workspace: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ pub(crate) async fn add(
|
||||||
extras_of_dependency: Vec<ExtraName>,
|
extras_of_dependency: Vec<ExtraName>,
|
||||||
package: Option<PackageName>,
|
package: Option<PackageName>,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
workspace: bool,
|
workspace: Option<bool>,
|
||||||
install_mirrors: PythonInstallMirrors,
|
install_mirrors: PythonInstallMirrors,
|
||||||
settings: ResolverInstallerSettings,
|
settings: ResolverInstallerSettings,
|
||||||
network_settings: NetworkSettings,
|
network_settings: NetworkSettings,
|
||||||
|
|
@ -497,16 +497,41 @@ pub(crate) async fn add(
|
||||||
// Track modification status, for reverts.
|
// Track modification status, for reverts.
|
||||||
let mut modified = false;
|
let mut modified = false;
|
||||||
|
|
||||||
// If `--workspace` is provided, add any members to the `workspace` section of the
|
// Determine whether to use workspace mode.
|
||||||
|
let use_workspace = match workspace {
|
||||||
|
Some(workspace) => workspace,
|
||||||
|
None => {
|
||||||
|
// Check if we're in a project (not a script), and if any requirements are path
|
||||||
|
// dependencies within the workspace.
|
||||||
|
if let AddTarget::Project(ref project, _) = target {
|
||||||
|
let workspace_root = project.workspace().install_path();
|
||||||
|
requirements.iter().any(|req| {
|
||||||
|
if let RequirementSource::Directory { install_path, .. } = &req.source {
|
||||||
|
let absolute_path = if install_path.is_absolute() {
|
||||||
|
install_path.to_path_buf()
|
||||||
|
} else {
|
||||||
|
project.root().join(install_path)
|
||||||
|
};
|
||||||
|
absolute_path.starts_with(workspace_root)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If workspace mode is enabled, add any members to the `workspace` section of the
|
||||||
// `pyproject.toml` file.
|
// `pyproject.toml` file.
|
||||||
if workspace {
|
if use_workspace {
|
||||||
let AddTarget::Project(project, python_target) = target else {
|
let AddTarget::Project(project, python_target) = target else {
|
||||||
unreachable!("`--workspace` and `--script` are conflicting options");
|
unreachable!("`--workspace` and `--script` are conflicting options");
|
||||||
};
|
};
|
||||||
|
|
||||||
let workspace = project.workspace();
|
|
||||||
let mut toml = PyProjectTomlMut::from_toml(
|
let mut toml = PyProjectTomlMut::from_toml(
|
||||||
&workspace.pyproject_toml().raw,
|
&project.workspace().pyproject_toml().raw,
|
||||||
DependencyTarget::PyProjectToml,
|
DependencyTarget::PyProjectToml,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -519,10 +544,22 @@ pub(crate) async fn add(
|
||||||
project.root().join(install_path)
|
project.root().join(install_path)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the path is not already included in the workspace.
|
// Either `--workspace` was provided explicitly, or it was omitted but the path is
|
||||||
if !workspace.includes(&absolute_path)? {
|
// within the workspace root.
|
||||||
|
let use_workspace = workspace.unwrap_or_else(|| {
|
||||||
|
absolute_path.starts_with(project.workspace().install_path())
|
||||||
|
});
|
||||||
|
if !use_workspace {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the project is already a member of the workspace, skip it.
|
||||||
|
if project.workspace().includes(&absolute_path)? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let relative_path = absolute_path
|
let relative_path = absolute_path
|
||||||
.strip_prefix(workspace.install_path())
|
.strip_prefix(project.workspace().install_path())
|
||||||
.unwrap_or(&absolute_path);
|
.unwrap_or(&absolute_path);
|
||||||
|
|
||||||
toml.add_workspace(relative_path)?;
|
toml.add_workspace(relative_path)?;
|
||||||
|
|
@ -535,14 +572,13 @@ pub(crate) async fn add(
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If we modified the workspace root, we need to reload it entirely, since this can impact
|
// If we modified the workspace root, we need to reload it entirely, since this can impact
|
||||||
// the discovered members, etc.
|
// the discovered members, etc.
|
||||||
target = if modified {
|
target = if modified {
|
||||||
let workspace_content = toml.to_string();
|
let workspace_content = toml.to_string();
|
||||||
fs_err::write(
|
fs_err::write(
|
||||||
workspace.install_path().join("pyproject.toml"),
|
project.workspace().install_path().join("pyproject.toml"),
|
||||||
&workspace_content,
|
&workspace_content,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -747,13 +783,13 @@ fn edits(
|
||||||
.and_then(|tool| tool.uv.as_ref())
|
.and_then(|tool| tool.uv.as_ref())
|
||||||
.and_then(|uv| uv.sources.as_ref())
|
.and_then(|uv| uv.sources.as_ref())
|
||||||
.map(ToolUvSources::inner);
|
.map(ToolUvSources::inner);
|
||||||
let workspace = project
|
let is_workspace_member = project
|
||||||
.workspace()
|
.workspace()
|
||||||
.packages()
|
.packages()
|
||||||
.contains_key(&requirement.name);
|
.contains_key(&requirement.name);
|
||||||
resolve_requirement(
|
resolve_requirement(
|
||||||
requirement,
|
requirement,
|
||||||
workspace,
|
is_workspace_member,
|
||||||
editable,
|
editable,
|
||||||
index.cloned(),
|
index.cloned(),
|
||||||
rev.map(ToString::to_string),
|
rev.map(ToString::to_string),
|
||||||
|
|
|
||||||
|
|
@ -1351,7 +1351,7 @@ pub(crate) struct AddSettings {
|
||||||
pub(crate) package: Option<PackageName>,
|
pub(crate) package: Option<PackageName>,
|
||||||
pub(crate) script: Option<PathBuf>,
|
pub(crate) script: Option<PathBuf>,
|
||||||
pub(crate) python: Option<String>,
|
pub(crate) python: Option<String>,
|
||||||
pub(crate) workspace: bool,
|
pub(crate) workspace: Option<bool>,
|
||||||
pub(crate) install_mirrors: PythonInstallMirrors,
|
pub(crate) install_mirrors: PythonInstallMirrors,
|
||||||
pub(crate) refresh: Refresh,
|
pub(crate) refresh: Refresh,
|
||||||
pub(crate) indexes: Vec<Index>,
|
pub(crate) indexes: Vec<Index>,
|
||||||
|
|
@ -1390,6 +1390,7 @@ impl AddSettings {
|
||||||
script,
|
script,
|
||||||
python,
|
python,
|
||||||
workspace,
|
workspace,
|
||||||
|
no_workspace,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
let dependency_type = if let Some(extra) = optional {
|
let dependency_type = if let Some(extra) = optional {
|
||||||
|
|
@ -1490,7 +1491,7 @@ impl AddSettings {
|
||||||
package,
|
package,
|
||||||
script,
|
script,
|
||||||
python: python.and_then(Maybe::into_option),
|
python: python.and_then(Maybe::into_option),
|
||||||
workspace,
|
workspace: flag(workspace, no_workspace, "workspace"),
|
||||||
editable: flag(editable, no_editable, "editable"),
|
editable: flag(editable, no_editable, "editable"),
|
||||||
extras: extra.unwrap_or_default(),
|
extras: extra.unwrap_or_default(),
|
||||||
refresh: Refresh::from(refresh),
|
refresh: Refresh::from(refresh),
|
||||||
|
|
|
||||||
|
|
@ -2491,9 +2491,9 @@ fn add_workspace_path() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a path dependency.
|
/// Add a path dependency, which should be implicitly added to the workspace.
|
||||||
#[test]
|
#[test]
|
||||||
fn add_path() -> Result<()> {
|
fn add_path_implicit_workspace() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
let workspace = context.temp_dir.child("workspace");
|
let workspace = context.temp_dir.child("workspace");
|
||||||
|
|
@ -2533,6 +2533,7 @@ fn add_path() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
Creating virtual environment at: .venv
|
Creating virtual environment at: .venv
|
||||||
|
Added `packages/child` to workspace members
|
||||||
Resolved 2 packages in [TIME]
|
Resolved 2 packages in [TIME]
|
||||||
Prepared 1 package in [TIME]
|
Prepared 1 package in [TIME]
|
||||||
Installed 1 package in [TIME]
|
Installed 1 package in [TIME]
|
||||||
|
|
@ -2545,7 +2546,134 @@ fn add_path() -> Result<()> {
|
||||||
filters => context.filters(),
|
filters => context.filters(),
|
||||||
}, {
|
}, {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
pyproject_toml, @r###"
|
pyproject_toml, @r#"
|
||||||
|
[project]
|
||||||
|
name = "parent"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"child",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = [
|
||||||
|
"packages/child",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
child = { workspace = true }
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// `uv add` implies a full lock and sync, including development dependencies.
|
||||||
|
let lock = fs_err::read_to_string(workspace.join("uv.lock"))?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
lock, @r#"
|
||||||
|
version = 1
|
||||||
|
revision = 2
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[options]
|
||||||
|
exclude-newer = "2024-03-25T00:00:00Z"
|
||||||
|
|
||||||
|
[manifest]
|
||||||
|
members = [
|
||||||
|
"child",
|
||||||
|
"parent",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "child"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { editable = "packages/child" }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parent"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "child" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [{ name = "child", editable = "packages/child" }]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Install from the lockfile.
|
||||||
|
uv_snapshot!(context.filters(), context.sync().arg("--frozen").current_dir(workspace.path()), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Audited 1 package in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a path dependency with `--no-workspace`, which should not be added to the workspace.
|
||||||
|
#[test]
|
||||||
|
fn add_path_no_workspace() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let workspace = context.temp_dir.child("workspace");
|
||||||
|
workspace.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "parent"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
let child = workspace.child("packages").child("child");
|
||||||
|
child.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "child"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
"#})?;
|
||||||
|
workspace
|
||||||
|
.child("packages")
|
||||||
|
.child("child")
|
||||||
|
.child("src")
|
||||||
|
.child("child")
|
||||||
|
.child("__init__.py")
|
||||||
|
.touch()?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add().arg(Path::new("packages").join("child")).current_dir(workspace.path()).arg("--no-workspace"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Creating virtual environment at: .venv
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ child==0.1.0 (from file://[TEMP_DIR]/workspace/packages/child)
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = fs_err::read_to_string(workspace.join("pyproject.toml"))?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
[project]
|
[project]
|
||||||
name = "parent"
|
name = "parent"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -2556,7 +2684,7 @@ fn add_path() -> Result<()> {
|
||||||
|
|
||||||
[tool.uv.sources]
|
[tool.uv.sources]
|
||||||
child = { path = "packages/child" }
|
child = { path = "packages/child" }
|
||||||
"###
|
"#
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2607,6 +2735,110 @@ fn add_path() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a path dependency in an adjacent directory, which should not be added to the workspace.
|
||||||
|
#[test]
|
||||||
|
fn add_path_adjacent_directory() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let project = context.temp_dir.child("project");
|
||||||
|
project.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
let dependency = context.temp_dir.child("dependency");
|
||||||
|
dependency.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "dependency"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
"#})?;
|
||||||
|
dependency
|
||||||
|
.child("src")
|
||||||
|
.child("dependency")
|
||||||
|
.child("__init__.py")
|
||||||
|
.touch()?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add().arg(dependency.path()).current_dir(project.path()), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Creating virtual environment at: .venv
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ dependency==0.1.0 (from file://[TEMP_DIR]/dependency)
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = fs_err::read_to_string(project.join("pyproject.toml"))?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"dependency",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
dependency = { path = "../dependency" }
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// `uv add` implies a full lock and sync, including development dependencies.
|
||||||
|
let lock = fs_err::read_to_string(project.join("uv.lock"))?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
lock, @r#"
|
||||||
|
version = 1
|
||||||
|
revision = 2
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[options]
|
||||||
|
exclude-newer = "2024-03-25T00:00:00Z"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dependency"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { directory = "../dependency" }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "dependency" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [{ name = "dependency", directory = "../dependency" }]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Update a requirement, modifying the source and extras.
|
/// Update a requirement, modifying the source and extras.
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "git")]
|
#[cfg(feature = "git")]
|
||||||
|
|
@ -7249,7 +7481,7 @@ fn fail_to_add_revert_project() -> Result<()> {
|
||||||
.child("setup.py")
|
.child("setup.py")
|
||||||
.write_str("1/0")?;
|
.write_str("1/0")?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.add().arg("./child"), @r#"
|
uv_snapshot!(context.filters(), context.add().arg("./child").arg("--no-workspace"), @r#"
|
||||||
success: false
|
success: false
|
||||||
exit_code: 1
|
exit_code: 1
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -7351,7 +7583,7 @@ fn fail_to_edit_revert_project() -> Result<()> {
|
||||||
.child("setup.py")
|
.child("setup.py")
|
||||||
.write_str("1/0")?;
|
.write_str("1/0")?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.add().arg("./child"), @r#"
|
uv_snapshot!(context.filters(), context.add().arg("./child").arg("--no-workspace"), @r#"
|
||||||
success: false
|
success: false
|
||||||
exit_code: 1
|
exit_code: 1
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -7460,7 +7692,7 @@ fn fail_to_add_revert_workspace_root() -> Result<()> {
|
||||||
.child("setup.py")
|
.child("setup.py")
|
||||||
.write_str("1/0")?;
|
.write_str("1/0")?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.add().arg("--workspace").arg("./broken"), @r#"
|
uv_snapshot!(context.filters(), context.add().arg("./broken"), @r#"
|
||||||
success: false
|
success: false
|
||||||
exit_code: 1
|
exit_code: 1
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -7575,7 +7807,7 @@ fn fail_to_add_revert_workspace_member() -> Result<()> {
|
||||||
.child("setup.py")
|
.child("setup.py")
|
||||||
.write_str("1/0")?;
|
.write_str("1/0")?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.add().current_dir(&project).arg("--workspace").arg("../broken"), @r#"
|
uv_snapshot!(context.filters(), context.add().current_dir(&project).arg("../broken"), @r#"
|
||||||
success: false
|
success: false
|
||||||
exit_code: 1
|
exit_code: 1
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -12928,12 +13160,12 @@ fn add_path_with_existing_workspace() -> Result<()> {
|
||||||
dependencies = []
|
dependencies = []
|
||||||
"#})?;
|
"#})?;
|
||||||
|
|
||||||
// Add the dependency with `--workspace` flag from the project directory.
|
// Add the dependency from the project directory. It should automatically be added as a
|
||||||
|
// workspace member, since it's in the same directory as the workspace.
|
||||||
uv_snapshot!(context.filters(), context
|
uv_snapshot!(context.filters(), context
|
||||||
.add()
|
.add()
|
||||||
.current_dir(&project_dir)
|
.current_dir(&project_dir)
|
||||||
.arg("../dep")
|
.arg("../dep"), @r"
|
||||||
.arg("--workspace"), @r"
|
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
@ -13044,3 +13276,203 @@ fn add_path_with_workspace() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a path dependency within the workspace directory without --workspace flag.
|
||||||
|
/// It should automatically be added as a workspace member.
|
||||||
|
#[test]
|
||||||
|
fn add_path_within_workspace_defaults_to_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"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
let dep_dir = context.temp_dir.child("dep");
|
||||||
|
dep_dir.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "dep"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Add the dependency without --workspace flag - it should still be added as workspace member
|
||||||
|
// since it's within the workspace directory.
|
||||||
|
uv_snapshot!(context.filters(), context
|
||||||
|
.add()
|
||||||
|
.arg("./dep"), @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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a path dependency within the workspace directory with --no-workspace flag.
|
||||||
|
/// It should be added as a direct path dependency.
|
||||||
|
#[test]
|
||||||
|
fn add_path_with_no_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"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
let dep_dir = context.temp_dir.child("dep");
|
||||||
|
dep_dir.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "dep"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Add the dependency with --no-workspace flag - it should be added as direct path dependency.
|
||||||
|
uv_snapshot!(context.filters(), context
|
||||||
|
.add()
|
||||||
|
.arg("./dep")
|
||||||
|
.arg("--no-workspace"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
dep = { path = "dep" }
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a path dependency outside the workspace directory.
|
||||||
|
/// It should be added as a direct path dependency, not a workspace member.
|
||||||
|
#[test]
|
||||||
|
fn add_path_outside_workspace_no_default() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
// Create a workspace directory
|
||||||
|
let workspace_dir = context.temp_dir.child("workspace");
|
||||||
|
workspace_dir.create_dir_all()?;
|
||||||
|
|
||||||
|
let workspace_toml = workspace_dir.child("pyproject.toml");
|
||||||
|
workspace_toml.write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "parent"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Create a dependency outside the workspace
|
||||||
|
let dep_dir = context.temp_dir.child("external_dep");
|
||||||
|
dep_dir.child("pyproject.toml").write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "dep"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Add the dependency without --workspace flag - it should be a direct path dependency
|
||||||
|
// since it's outside the workspace directory.
|
||||||
|
uv_snapshot!(context.filters(), context
|
||||||
|
.add()
|
||||||
|
.current_dir(&workspace_dir)
|
||||||
|
.arg("../external_dep"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Creating virtual environment at: .venv
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Audited in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = fs_err::read_to_string(workspace_toml)?;
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[project]
|
||||||
|
name = "parent"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"dep",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = []
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
dep = { path = "../external_dep" }
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -535,7 +535,9 @@ uv add [OPTIONS] <PACKAGES|--requirements <REQUIREMENTS>>
|
||||||
<p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p></dd><dt id="uv-add--no-python-downloads"><a href="#uv-add--no-python-downloads"><code>--no-python-downloads</code></a></dt><dd><p>Disable automatic downloads of Python.</p>
|
<p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p></dd><dt id="uv-add--no-python-downloads"><a href="#uv-add--no-python-downloads"><code>--no-python-downloads</code></a></dt><dd><p>Disable automatic downloads of Python.</p>
|
||||||
</dd><dt id="uv-add--no-sources"><a href="#uv-add--no-sources"><code>--no-sources</code></a></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any workspace, Git, URL, or local path sources</p>
|
</dd><dt id="uv-add--no-sources"><a href="#uv-add--no-sources"><code>--no-sources</code></a></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any workspace, Git, URL, or local path sources</p>
|
||||||
</dd><dt id="uv-add--no-sync"><a href="#uv-add--no-sync"><code>--no-sync</code></a></dt><dd><p>Avoid syncing the virtual environment</p>
|
</dd><dt id="uv-add--no-sync"><a href="#uv-add--no-sync"><code>--no-sync</code></a></dt><dd><p>Avoid syncing the virtual environment</p>
|
||||||
<p>May also be set with the <code>UV_NO_SYNC</code> environment variable.</p></dd><dt id="uv-add--offline"><a href="#uv-add--offline"><code>--offline</code></a></dt><dd><p>Disable network access.</p>
|
<p>May also be set with the <code>UV_NO_SYNC</code> environment variable.</p></dd><dt id="uv-add--no-workspace"><a href="#uv-add--no-workspace"><code>--no-workspace</code></a></dt><dd><p>Don't add the dependency as a workspace member.</p>
|
||||||
|
<p>By default, when adding a dependency that's a local path and is within the workspace directory, uv will add it as a workspace member; pass <code>--no-workspace</code> to add the package as direct path dependency instead.</p>
|
||||||
|
</dd><dt id="uv-add--offline"><a href="#uv-add--offline"><code>--offline</code></a></dt><dd><p>Disable network access.</p>
|
||||||
<p>When disabled, uv will only use locally cached data and locally available files.</p>
|
<p>When disabled, uv will only use locally cached data and locally available files.</p>
|
||||||
<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-add--optional"><a href="#uv-add--optional"><code>--optional</code></a> <i>optional</i></dt><dd><p>Add the requirements to the package's optional dependencies for the specified extra.</p>
|
<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p></dd><dt id="uv-add--optional"><a href="#uv-add--optional"><code>--optional</code></a> <i>optional</i></dt><dd><p>Add the requirements to the package's optional dependencies for the specified extra.</p>
|
||||||
<p>The group may then be activated when installing the project with the <code>--extra</code> flag.</p>
|
<p>The group may then be activated when installing the project with the <code>--extra</code> flag.</p>
|
||||||
|
|
@ -583,7 +585,7 @@ uv add [OPTIONS] <PACKAGES|--requirements <REQUIREMENTS>>
|
||||||
</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>
|
</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>
|
<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>
|
</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>
|
<p>By default, uv will add path dependencies that are within the workspace directory as workspace members. 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>
|
</dd></dl>
|
||||||
|
|
||||||
## uv remove
|
## uv remove
|
||||||
|
|
@ -1154,10 +1156,10 @@ environment in the project.</p>
|
||||||
<li><code>macos</code>: An alias for <code>aarch64-apple-darwin</code>, the default target for macOS</li>
|
<li><code>macos</code>: An alias for <code>aarch64-apple-darwin</code>, the default target for macOS</li>
|
||||||
<li><code>x86_64-pc-windows-msvc</code>: A 64-bit x86 Windows target</li>
|
<li><code>x86_64-pc-windows-msvc</code>: A 64-bit x86 Windows target</li>
|
||||||
<li><code>i686-pc-windows-msvc</code>: A 32-bit x86 Windows target</li>
|
<li><code>i686-pc-windows-msvc</code>: A 32-bit x86 Windows target</li>
|
||||||
<li><code>x86_64-unknown-linux-gnu</code>: An x86 Linux target. Equivalent to <code>x86_64-manylinux_2_17</code></li>
|
<li><code>x86_64-unknown-linux-gnu</code>: An x86 Linux target. Equivalent to <code>x86_64-manylinux_2_28</code></li>
|
||||||
<li><code>aarch64-apple-darwin</code>: An ARM-based macOS target, as seen on Apple Silicon devices</li>
|
<li><code>aarch64-apple-darwin</code>: An ARM-based macOS target, as seen on Apple Silicon devices</li>
|
||||||
<li><code>x86_64-apple-darwin</code>: An x86 macOS target</li>
|
<li><code>x86_64-apple-darwin</code>: An x86 macOS target</li>
|
||||||
<li><code>aarch64-unknown-linux-gnu</code>: An ARM64 Linux target. Equivalent to <code>aarch64-manylinux_2_17</code></li>
|
<li><code>aarch64-unknown-linux-gnu</code>: An ARM64 Linux target. Equivalent to <code>aarch64-manylinux_2_28</code></li>
|
||||||
<li><code>aarch64-unknown-linux-musl</code>: An ARM64 Linux target</li>
|
<li><code>aarch64-unknown-linux-musl</code>: An ARM64 Linux target</li>
|
||||||
<li><code>x86_64-unknown-linux-musl</code>: An <code>x86_64</code> Linux target</li>
|
<li><code>x86_64-unknown-linux-musl</code>: An <code>x86_64</code> Linux target</li>
|
||||||
<li><code>x86_64-manylinux2014</code>: An <code>x86_64</code> target for the <code>manylinux2014</code> platform. Equivalent to <code>x86_64-manylinux_2_17</code></li>
|
<li><code>x86_64-manylinux2014</code>: An <code>x86_64</code> target for the <code>manylinux2014</code> platform. Equivalent to <code>x86_64-manylinux_2_17</code></li>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue