Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions crates/pixi_manifest/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ impl DiscoveryStart {
}

impl WorkspaceDiscoverer {
/// Required sections. At least one of them must be present.
pub const REQUIRED_SECTIONS: [&'static str; 3] = ["workspace", "project", "package"];

/// Constructs a new instance from the current path.
pub fn new(start: DiscoveryStart) -> Self {
Self {
Expand Down Expand Up @@ -329,9 +332,31 @@ impl WorkspaceDiscoverer {
// Cheap check to see if the manifest contains a pixi section.
if let ManifestSource::PyProjectToml(source) = &contents {
if !source.contains("[tool.pixi")
&& !matches!(search_path.clone(), SearchPath::Explicit(_))
&& matches!(search_path.clone(), SearchPath::Explicit(_))
{
return Err(WorkspaceDiscoveryError::Toml(Box::new(WithSourceCode {
error: TomlError::NoPixiTable(ManifestKind::Pyproject, None),
source: contents.into_named(provenance.absolute_path().to_string_lossy()),
})));
}
} else if let ManifestSource::PixiToml(source) = &contents {
// check if at least one of the required sections is present
if !Self::REQUIRED_SECTIONS
.iter()
.any(|section| source.contains(&format!("[{}]", section)))
{
continue;
return Err(WorkspaceDiscoveryError::Toml(Box::new(WithSourceCode {
error: TomlError::NoPixiTable(
ManifestKind::Pixi,
Some(format!(
"Any of the following sections is required:\n{}",
Self::REQUIRED_SECTIONS
.map(|s| format!("* {}", s))
.join("\n")
)),
),
source: contents.into_named(provenance.absolute_path().to_string_lossy()),
})));
}
}

Expand Down Expand Up @@ -601,6 +626,8 @@ mod test {
#[case::pixi("pixi.toml")]
#[case::empty("empty")]
#[case::package_specific("package_a/pixi.toml")]
#[case::missing_table_pixi_manifest("missing-tables/pixi.toml")]
#[case::missing_table_pyproject_manifest("missing-tables/pyproject.toml")]
fn test_explicit_workspace_discoverer(#[case] subdir: &str) {
let test_data_root = dunce::canonicalize(
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/data/workspace-discovery"),
Expand Down
25 changes: 19 additions & 6 deletions crates/pixi_manifest/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use thiserror::Error;
use toml_span::{DeserError, Error};

use super::pypi::pypi_requirement::Pep508ToPyPiRequirementError;
use crate::{KnownPreviewFeature, WorkspaceManifest};
use crate::{KnownPreviewFeature, ManifestKind, WorkspaceManifest};

#[derive(Error, Debug, Clone, Diagnostic)]
pub enum DependencyError {
Expand Down Expand Up @@ -97,7 +97,7 @@ impl GenericError {
pub enum TomlError {
Error(toml_edit::TomlError),
TomlError(toml_span::Error),
NoPixiTable,
NoPixiTable(ManifestKind, Option<String>),
MissingField(Cow<'static, str>, Option<Range<usize>>),
Generic(GenericError),
#[error(transparent)]
Expand Down Expand Up @@ -153,7 +153,17 @@ impl Display for TomlError {
}
_ => write!(f, "{}", err),
},
TomlError::NoPixiTable => write!(f, "Missing table `[tool.pixi.project]`"),
TomlError::NoPixiTable(manifest_kind, detail) => {
let filename = match manifest_kind {
ManifestKind::Pyproject => "pyproject.toml",
ManifestKind::Pixi => "pixi.toml",
};
if let Some(detail) = detail {
write!(f, "Missing table in manifest {filename}:\n{detail}")
} else {
write!(f, "Missing table in manifest {filename}")
}
}
TomlError::MissingField(key, _) => write!(f, "Missing field `{key}`"),
TomlError::Generic(err) => write!(f, "{}", &err.message),
TomlError::FeatureNotEnabled(err) => write!(f, "{err}"),
Expand Down Expand Up @@ -269,7 +279,7 @@ impl Diagnostic for TomlError {
}
_ => Some(SourceSpan::new(span.start.into(), span.end - span.start)),
},
TomlError::NoPixiTable => Some(SourceSpan::new(SourceOffset::from(0), 1)),
TomlError::NoPixiTable(_, _) => Some(SourceSpan::new(SourceOffset::from(0), 1)),
TomlError::MissingField(_, span) => span.clone().map(SourceSpan::from),
TomlError::FeatureNotEnabled(err) => return err.labels(),
TomlError::InvalidNonPackageDependencies(err) => return err.labels(),
Expand Down Expand Up @@ -303,8 +313,11 @@ impl Diagnostic for TomlError {

fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self {
TomlError::NoPixiTable => {
Some(Box::new("Run `pixi init` to create a new project manifest"))
TomlError::NoPixiTable(ManifestKind::Pixi, _) => {
Some(Box::new("More information about pixi manifest files can be found at https://pixi.sh/latest/reference/pixi_manifest/"))
}
TomlError::NoPixiTable(ManifestKind::Pyproject, _) => {
Some(Box::new("Check your manifest for a [tool.pixi.*] table. See https://pixi.sh/latest/python/pyproject_toml for more information."))
}
TomlError::TomlError(toml_span::Error { kind, .. }) => match kind {
toml_span::ErrorKind::UnexpectedValue { expected, value } => {
Expand Down
4 changes: 2 additions & 2 deletions crates/pixi_manifest/src/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
ExternalPackageProperties, ExternalWorkspaceProperties, FromTomlStr, PyProjectToml,
TomlManifest,
},
FeatureName, Warning,
FeatureName, ManifestKind, Warning,
};

#[derive(Debug)]
Expand Down Expand Up @@ -63,7 +63,7 @@ impl PyProjectManifest {
pub fn ensure_pixi(self) -> Result<Self, TomlError> {
// Make sure the `[tool.pixi]` table exist
if !self.has_pixi_table() {
return Err(TomlError::NoPixiTable);
return Err(TomlError::NoPixiTable(ManifestKind::Pyproject, None));
}

// Make sure a 'name' is defined
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/pixi_manifest/src/discovery.rs
expression: snapshot
---
× Missing table in manifest pixi.toml:
│ Any of the following sections is required:
│ * workspace
│ * project
│ * package
╭─[<CARGO_ROOT>/tests/data/workspace-discovery/missing-tables/pixi.toml:1:1]
1 │ # Empty on purpose.
· ─
2 │ # Explicit discovery should fail with a diagnostic.
╰────
help: More information about pixi manifest files can be found at https://pixi.sh/latest/reference/pixi_manifest/
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/pixi_manifest/src/discovery.rs
expression: snapshot
---
× Missing table in manifest pyproject.toml
╭─[<CARGO_ROOT>/tests/data/workspace-discovery/missing-tables/pyproject.toml:1:1]
1 │ [project]
· ─
2 │ name = "malformed"
╰────
help: Check your manifest for a [tool.pixi.*] table. See https://pixi.sh/latest/python/pyproject_toml for more information.
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
source: crates/pixi_manifest/src/discovery.rs
expression: snapshot
---
Discovered workspace at: nested-pyproject-workspace/pyproject.toml
- Name: nested-pyproject-workspace
× Missing field `tool.pixi`
2 changes: 2 additions & 0 deletions tests/data/workspace-discovery/missing-tables/pixi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Empty on purpose.
# Explicit discovery should fail with a diagnostic.
3 changes: 3 additions & 0 deletions tests/data/workspace-discovery/missing-tables/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[project]
name = "malformed"
version = "0.1.0"
Loading