mirror of https://github.com/astral-sh/uv
Surface dedicated `project.name` error for workspaces (#7399)
## Summary An extension of https://github.com/astral-sh/uv/pull/6803 to cover `uv run`.
This commit is contained in:
parent
4aad89cf06
commit
e07281deb3
|
|
@ -47,7 +47,7 @@ pub enum MetadataError {
|
||||||
MailParse(#[from] MailParseError),
|
MailParse(#[from] MailParseError),
|
||||||
#[error("Invalid `pyproject.toml`")]
|
#[error("Invalid `pyproject.toml`")]
|
||||||
InvalidPyprojectTomlSyntax(#[source] toml_edit::TomlError),
|
InvalidPyprojectTomlSyntax(#[source] toml_edit::TomlError),
|
||||||
#[error("`pyproject.toml` is using the `[project]` table, but the required `project.name` is not set.")]
|
#[error("`pyproject.toml` is using the `[project]` table, but the required `project.name` field is not set.")]
|
||||||
InvalidPyprojectTomlMissingName(#[source] toml_edit::de::Error),
|
InvalidPyprojectTomlMissingName(#[source] toml_edit::de::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
InvalidPyprojectTomlSchema(toml_edit::de::Error),
|
InvalidPyprojectTomlSchema(toml_edit::de::Error),
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
//!
|
//!
|
||||||
//! Then lowers them into a dependency specification.
|
//! Then lowers them into a dependency specification.
|
||||||
|
|
||||||
|
use glob::Pattern;
|
||||||
|
use serde::{de::IntoDeserializer, Deserialize, Serialize};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::str::FromStr;
|
||||||
use std::{collections::BTreeMap, mem};
|
use std::{collections::BTreeMap, mem};
|
||||||
|
|
||||||
use glob::Pattern;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -22,6 +22,16 @@ use uv_git::GitReference;
|
||||||
use uv_macros::OptionsMetadata;
|
use uv_macros::OptionsMetadata;
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PyprojectTomlError {
|
||||||
|
#[error(transparent)]
|
||||||
|
TomlSyntax(#[from] toml_edit::TomlError),
|
||||||
|
#[error(transparent)]
|
||||||
|
TomlSchema(#[from] toml_edit::de::Error),
|
||||||
|
#[error("`pyproject.toml` is using the `[project]` table, but the required `project.name` field is not set")]
|
||||||
|
MissingName(#[source] toml_edit::de::Error),
|
||||||
|
}
|
||||||
|
|
||||||
/// A `pyproject.toml` as specified in PEP 517.
|
/// A `pyproject.toml` as specified in PEP 517.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
|
@ -41,8 +51,18 @@ pub struct PyProjectToml {
|
||||||
|
|
||||||
impl PyProjectToml {
|
impl PyProjectToml {
|
||||||
/// Parse a `PyProjectToml` from a raw TOML string.
|
/// Parse a `PyProjectToml` from a raw TOML string.
|
||||||
pub fn from_string(raw: String) -> Result<Self, toml::de::Error> {
|
pub fn from_string(raw: String) -> Result<Self, PyprojectTomlError> {
|
||||||
let pyproject = toml::from_str(&raw)?;
|
let pyproject: toml_edit::ImDocument<_> =
|
||||||
|
toml_edit::ImDocument::from_str(&raw).map_err(PyprojectTomlError::TomlSyntax)?;
|
||||||
|
let pyproject =
|
||||||
|
PyProjectToml::deserialize(pyproject.into_deserializer()).map_err(|err| {
|
||||||
|
// TODO(konsti): A typed error would be nicer, this can break on toml upgrades.
|
||||||
|
if err.message().contains("missing field `name`") {
|
||||||
|
PyprojectTomlError::MissingName(err)
|
||||||
|
} else {
|
||||||
|
PyprojectTomlError::TomlSchema(err)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
Ok(PyProjectToml { raw, ..pyproject })
|
Ok(PyProjectToml { raw, ..pyproject })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
//! Resolve the current [`ProjectWorkspace`] or [`Workspace`].
|
//! Resolve the current [`ProjectWorkspace`] or [`Workspace`].
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use glob::{glob, GlobError, PatternError};
|
use glob::{glob, GlobError, PatternError};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use pep508_rs::{MarkerTree, RequirementOrigin, VerbatimUrl};
|
use pep508_rs::{MarkerTree, RequirementOrigin, VerbatimUrl};
|
||||||
|
|
@ -14,7 +13,9 @@ use uv_fs::{Simplified, CWD};
|
||||||
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
|
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
|
||||||
use uv_warnings::{warn_user, warn_user_once};
|
use uv_warnings::{warn_user, warn_user_once};
|
||||||
|
|
||||||
use crate::pyproject::{Project, PyProjectToml, Source, ToolUvSources, ToolUvWorkspace};
|
use crate::pyproject::{
|
||||||
|
Project, PyProjectToml, PyprojectTomlError, Source, ToolUvSources, ToolUvWorkspace,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum WorkspaceError {
|
pub enum WorkspaceError {
|
||||||
|
|
@ -39,7 +40,7 @@ pub enum WorkspaceError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
#[error("Failed to parse: `{}`", _0.user_display())]
|
#[error("Failed to parse: `{}`", _0.user_display())]
|
||||||
Toml(PathBuf, #[source] Box<toml::de::Error>),
|
Toml(PathBuf, #[source] Box<PyprojectTomlError>),
|
||||||
#[error("Failed to normalize workspace member path")]
|
#[error("Failed to normalize workspace member path")]
|
||||||
Normalize(#[source] std::io::Error),
|
Normalize(#[source] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +121,7 @@ impl Workspace {
|
||||||
|
|
||||||
let pyproject_path = project_path.join("pyproject.toml");
|
let pyproject_path = project_path.join("pyproject.toml");
|
||||||
let contents = fs_err::tokio::read_to_string(&pyproject_path).await?;
|
let contents = fs_err::tokio::read_to_string(&pyproject_path).await?;
|
||||||
let pyproject_toml = PyProjectToml::from_string(contents.clone())
|
let pyproject_toml = PyProjectToml::from_string(contents)
|
||||||
.map_err(|err| WorkspaceError::Toml(pyproject_path.clone(), Box::new(err)))?;
|
.map_err(|err| WorkspaceError::Toml(pyproject_path.clone(), Box::new(err)))?;
|
||||||
|
|
||||||
// Check if the project is explicitly marked as unmanaged.
|
// Check if the project is explicitly marked as unmanaged.
|
||||||
|
|
@ -588,7 +589,7 @@ impl Workspace {
|
||||||
let pyproject_path = workspace_root.join("pyproject.toml");
|
let pyproject_path = workspace_root.join("pyproject.toml");
|
||||||
let contents = fs_err::read_to_string(&pyproject_path)?;
|
let contents = fs_err::read_to_string(&pyproject_path)?;
|
||||||
let pyproject_toml = PyProjectToml::from_string(contents)
|
let pyproject_toml = PyProjectToml::from_string(contents)
|
||||||
.map_err(|err| WorkspaceError::Toml(pyproject_path, Box::new(err)))?;
|
.map_err(|err| WorkspaceError::Toml(pyproject_path.clone(), Box::new(err)))?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Adding root workspace member: `{}`",
|
"Adding root workspace member: `{}`",
|
||||||
|
|
@ -699,7 +700,6 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pyproject_toml = PyProjectToml::from_string(contents)
|
let pyproject_toml = PyProjectToml::from_string(contents)
|
||||||
.map_err(|err| WorkspaceError::Toml(pyproject_path.clone(), Box::new(err)))?;
|
.map_err(|err| WorkspaceError::Toml(pyproject_path.clone(), Box::new(err)))?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12729,7 +12729,7 @@ fn lock_invalid_project_table() -> Result<()> {
|
||||||
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
error: Failed to build: `b @ file://[TEMP_DIR]/b`
|
error: Failed to build: `b @ file://[TEMP_DIR]/b`
|
||||||
Caused by: Failed to extract static metadata from `pyproject.toml`
|
Caused by: Failed to extract static metadata from `pyproject.toml`
|
||||||
Caused by: `pyproject.toml` is using the `[project]` table, but the required `project.name` is not set.
|
Caused by: `pyproject.toml` is using the `[project]` table, but the required `project.name` field is not set.
|
||||||
Caused by: TOML parse error at line 2, column 10
|
Caused by: TOML parse error at line 2, column 10
|
||||||
|
|
|
|
||||||
2 | [project.urls]
|
2 | [project.urls]
|
||||||
|
|
|
||||||
|
|
@ -1912,7 +1912,7 @@ fn run_exit_code() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_lock_invalid_project_table() -> Result<()> {
|
fn run_invalid_project_table() -> Result<()> {
|
||||||
let context = TestContext::new_with_versions(&["3.12", "3.11", "3.8"]);
|
let context = TestContext::new_with_versions(&["3.12", "3.11", "3.8"]);
|
||||||
|
|
||||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
|
@ -1939,6 +1939,7 @@ fn run_lock_invalid_project_table() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Failed to parse: `pyproject.toml`
|
error: Failed to parse: `pyproject.toml`
|
||||||
|
Caused by: `pyproject.toml` is using the `[project]` table, but the required `project.name` field is not set
|
||||||
Caused by: TOML parse error at line 1, column 2
|
Caused by: TOML parse error at line 1, column 2
|
||||||
|
|
|
|
||||||
1 | [project.urls]
|
1 | [project.urls]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue