mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Automatically detect workspace packages in uv add
(#4557)
## Summary If the package _isn't_ marked as `workspace = true`, locking will fail given: ```rust let workspace_package_declared = // We require that when you use a package that's part of the workspace, ... !workspace.packages().contains_key(&requirement.name) // ... it must be declared as a workspace dependency (`workspace = true`), ... || matches!( source, Some(Source::Workspace { // By using toml, we technically support `workspace = false`. workspace: true, .. }) ) // ... except for recursive self-inclusion (extras that activate other extras), e.g. // `framework[machine_learning]` depends on `framework[cuda]`. || &requirement.name == project_name; if !workspace_package_declared { return Err(LoweringError::UndeclaredWorkspacePackage); } ``` Closes https://github.com/astral-sh/uv/issues/4552.
This commit is contained in:
parent
a328c7b995
commit
45c271d15d
6 changed files with 46 additions and 23 deletions
|
@ -1749,10 +1749,6 @@ pub struct AddArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub dev: bool,
|
pub dev: bool,
|
||||||
|
|
||||||
/// Add the requirements as workspace dependencies.
|
|
||||||
#[arg(long)]
|
|
||||||
pub workspace: bool,
|
|
||||||
|
|
||||||
/// Add the requirements as editables.
|
/// Add the requirements as editables.
|
||||||
#[arg(long, default_missing_value = "true", num_args(0..=1))]
|
#[arg(long, default_missing_value = "true", num_args(0..=1))]
|
||||||
pub editable: Option<bool>,
|
pub editable: Option<bool>,
|
||||||
|
|
|
@ -194,14 +194,19 @@ pub enum Source {
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SourceError {
|
pub enum SourceError {
|
||||||
#[error("Cannot resolve git reference `{0}`.")]
|
#[error("Cannot resolve git reference `{0}`")]
|
||||||
UnresolvedReference(String),
|
UnresolvedReference(String),
|
||||||
#[error("Workspace dependency must be a local path.")]
|
#[error("Workspace dependency `{0}` must refer to local directory, not a Git repository")]
|
||||||
InvalidWorkspaceRequirement,
|
WorkspacePackageGit(String),
|
||||||
|
#[error("Workspace dependency `{0}` must refer to local directory, not a URL")]
|
||||||
|
WorkspacePackageUrl(String),
|
||||||
|
#[error("Workspace dependency `{0}` must refer to local directory, not a file")]
|
||||||
|
WorkspacePackageFile(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
pub fn from_requirement(
|
pub fn from_requirement(
|
||||||
|
name: &PackageName,
|
||||||
source: RequirementSource,
|
source: RequirementSource,
|
||||||
workspace: bool,
|
workspace: bool,
|
||||||
editable: Option<bool>,
|
editable: Option<bool>,
|
||||||
|
@ -210,15 +215,23 @@ impl Source {
|
||||||
branch: Option<String>,
|
branch: Option<String>,
|
||||||
) -> Result<Option<Source>, SourceError> {
|
) -> Result<Option<Source>, SourceError> {
|
||||||
if workspace {
|
if workspace {
|
||||||
match source {
|
return match source {
|
||||||
RequirementSource::Registry { .. } | RequirementSource::Directory { .. } => {}
|
RequirementSource::Registry { .. } | RequirementSource::Directory { .. } => {
|
||||||
_ => return Err(SourceError::InvalidWorkspaceRequirement),
|
Ok(Some(Source::Workspace {
|
||||||
}
|
editable,
|
||||||
|
workspace: true,
|
||||||
return Ok(Some(Source::Workspace {
|
}))
|
||||||
editable,
|
}
|
||||||
workspace: true,
|
RequirementSource::Url { .. } => {
|
||||||
}));
|
Err(SourceError::WorkspacePackageUrl(name.to_string()))
|
||||||
|
}
|
||||||
|
RequirementSource::Git { .. } => {
|
||||||
|
Err(SourceError::WorkspacePackageGit(name.to_string()))
|
||||||
|
}
|
||||||
|
RequirementSource::Path { .. } => {
|
||||||
|
Err(SourceError::WorkspacePackageFile(name.to_string()))
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = match source {
|
let source = match source {
|
||||||
|
|
|
@ -26,7 +26,6 @@ use crate::settings::ResolverInstallerSettings;
|
||||||
#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
|
#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
|
||||||
pub(crate) async fn add(
|
pub(crate) async fn add(
|
||||||
requirements: Vec<RequirementsSource>,
|
requirements: Vec<RequirementsSource>,
|
||||||
workspace: bool,
|
|
||||||
dev: bool,
|
dev: bool,
|
||||||
editable: Option<bool>,
|
editable: Option<bool>,
|
||||||
raw: bool,
|
raw: bool,
|
||||||
|
@ -154,7 +153,9 @@ pub(crate) async fn add(
|
||||||
(pep508_rs::Requirement::from(req), None)
|
(pep508_rs::Requirement::from(req), None)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, try to construct the source.
|
// Otherwise, try to construct the source.
|
||||||
|
let workspace = project.workspace().packages().contains_key(&req.name);
|
||||||
let result = Source::from_requirement(
|
let result = Source::from_requirement(
|
||||||
|
&req.name,
|
||||||
req.source.clone(),
|
req.source.clone(),
|
||||||
workspace,
|
workspace,
|
||||||
editable,
|
editable,
|
||||||
|
|
|
@ -719,7 +719,6 @@ async fn run() -> Result<ExitStatus> {
|
||||||
|
|
||||||
commands::add(
|
commands::add(
|
||||||
args.requirements,
|
args.requirements,
|
||||||
args.workspace,
|
|
||||||
args.dev,
|
args.dev,
|
||||||
args.editable,
|
args.editable,
|
||||||
args.raw,
|
args.raw,
|
||||||
|
|
|
@ -432,7 +432,6 @@ impl LockSettings {
|
||||||
pub(crate) struct AddSettings {
|
pub(crate) struct AddSettings {
|
||||||
pub(crate) requirements: Vec<RequirementsSource>,
|
pub(crate) requirements: Vec<RequirementsSource>,
|
||||||
pub(crate) dev: bool,
|
pub(crate) dev: bool,
|
||||||
pub(crate) workspace: bool,
|
|
||||||
pub(crate) editable: Option<bool>,
|
pub(crate) editable: Option<bool>,
|
||||||
pub(crate) raw: bool,
|
pub(crate) raw: bool,
|
||||||
pub(crate) rev: Option<String>,
|
pub(crate) rev: Option<String>,
|
||||||
|
@ -451,7 +450,6 @@ impl AddSettings {
|
||||||
let AddArgs {
|
let AddArgs {
|
||||||
requirements,
|
requirements,
|
||||||
dev,
|
dev,
|
||||||
workspace,
|
|
||||||
editable,
|
editable,
|
||||||
raw,
|
raw,
|
||||||
rev,
|
rev,
|
||||||
|
@ -471,7 +469,6 @@ impl AddSettings {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
requirements,
|
requirements,
|
||||||
workspace,
|
|
||||||
dev,
|
dev,
|
||||||
editable,
|
editable,
|
||||||
raw,
|
raw,
|
||||||
|
|
|
@ -735,11 +735,29 @@ fn add_remove_workspace() -> Result<()> {
|
||||||
dependencies = []
|
dependencies = []
|
||||||
"#})?;
|
"#})?;
|
||||||
|
|
||||||
|
// Adding a workspace package with a mismatched source should error.
|
||||||
|
let mut add_cmd =
|
||||||
|
context.add(&["child2 @ git+https://github.com/astral-test/uv-public-pypackage"]);
|
||||||
|
add_cmd
|
||||||
|
.arg("--preview")
|
||||||
|
.arg("--package")
|
||||||
|
.arg("child1")
|
||||||
|
.current_dir(&context.temp_dir);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), add_cmd, @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Workspace dependency `child2` must refer to local directory, not a Git repository
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Workspace packages should be detected automatically.
|
||||||
let child1 = context.temp_dir.join("child1");
|
let child1 = context.temp_dir.join("child1");
|
||||||
let mut add_cmd = context.add(&["child2"]);
|
let mut add_cmd = context.add(&["child2"]);
|
||||||
add_cmd
|
add_cmd
|
||||||
.arg("--preview")
|
.arg("--preview")
|
||||||
.arg("--workspace")
|
|
||||||
.arg("--package")
|
.arg("--package")
|
||||||
.arg("child1")
|
.arg("child1")
|
||||||
.current_dir(&context.temp_dir);
|
.current_dir(&context.temp_dir);
|
||||||
|
@ -921,7 +939,6 @@ fn add_workspace_editable() -> Result<()> {
|
||||||
let mut add_cmd = context.add(&["child2"]);
|
let mut add_cmd = context.add(&["child2"]);
|
||||||
add_cmd
|
add_cmd
|
||||||
.arg("--editable")
|
.arg("--editable")
|
||||||
.arg("--workspace")
|
|
||||||
.arg("--preview")
|
.arg("--preview")
|
||||||
.current_dir(&child1);
|
.current_dir(&child1);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue