Skip to content

Commit

Permalink
feat: Get tool.pixi.project.name from project.name (#1112)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim de Jager <tdejager89@gmail.com>
Co-authored-by: Ruben Arts <ruben.arts@hotmail.com>
  • Loading branch information
3 people committed Apr 4, 2024
1 parent e89256e commit 2d70705
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 34 deletions.
5 changes: 0 additions & 5 deletions docs/advanced/pyproject_toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ We don't advise to use the `pyproject.toml` file for anything else than python p
When you already have a `pyproject.toml` file in your project, you can add the following section to it:
```toml
[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
```
Expand All @@ -31,7 +30,6 @@ name = "my_project"
requires-python = ">=3.9"

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
```
Expand Down Expand Up @@ -62,7 +60,6 @@ dependencies = [
]

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
```
Expand Down Expand Up @@ -95,7 +92,6 @@ dependencies = [
]

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]

Expand All @@ -121,7 +117,6 @@ dependencies = [
]

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]

Expand Down
1 change: 0 additions & 1 deletion examples/flask-hello-world-pyproject/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

[tool.pixi.project]
name = "flask-hello-world-pyproject"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]

Expand Down
1 change: 0 additions & 1 deletion src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ platforms = ["{{ platforms|join("\", \"") }}"]
/// The pyproject.toml template
const PYROJECT_TEMPLATE: &str = r#"
[tool.pixi.project]
name = "{{ name }}"
channels = [{%- if channels %}"{{ channels|join("\", \"") }}"{%- endif %}]
platforms = ["{{ platforms|join("\", \"") }}"]
Expand Down
46 changes: 45 additions & 1 deletion src/project/manifest/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::project::manifest::{FeatureName, TargetSelector};
use crate::project::SpecType;
use miette::Diagnostic;
use miette::{Diagnostic, IntoDiagnostic, LabeledSpan, NamedSource, Report};
use rattler_conda_types::{InvalidPackageNameError, ParseMatchSpecError};
use thiserror::Error;

Expand Down Expand Up @@ -71,3 +71,47 @@ pub enum RequirementConversionError {
#[error("Error converting requirement from pypi to conda")]
Unimplemented,
}

#[derive(Error, Debug, Clone)]
pub enum TomlError {
#[error("{0}")]
Error(#[from] toml_edit::TomlError),
#[error("Missing field `project`")]
NoProjectTable,
#[error("Missing field `name`")]
NoProjectName(Option<std::ops::Range<usize>>),
}

impl TomlError {
pub fn to_fancy<T>(&self, file_name: &str, contents: impl Into<String>) -> Result<T, Report> {
if let Some(span) = self.span() {
Err(miette::miette!(
labels = vec![LabeledSpan::at(span, self.message())],
"failed to parse project manifest"
)
.with_source_code(NamedSource::new(file_name, contents.into())))
} else {
Err(self.clone()).into_diagnostic()
}
}

fn span(&self) -> Option<std::ops::Range<usize>> {
match self {
TomlError::Error(e) => e.span(),
TomlError::NoProjectTable => Some(0..1),
TomlError::NoProjectName(span) => span.clone(),
}
}
fn message(&self) -> &str {
match self {
TomlError::Error(e) => e.message(),
TomlError::NoProjectTable => "Missing field `project`",
TomlError::NoProjectName(_) => "Missing field `name`",
}
}
}
impl From<toml_edit::de::Error> for TomlError {
fn from(e: toml_edit::de::Error) -> Self {
TomlError::Error(e.into())
}
}
2 changes: 1 addition & 1 deletion src/project/manifest/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use url::Url;
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct ProjectMetadata {
/// The name of the project
pub name: String,
pub name: Option<String>, // set as optional to handle conversion from pyproject.toml

/// The version of the project
#[serde_as(as = "Option<DisplayFromStr>")]
Expand Down
39 changes: 21 additions & 18 deletions src/project/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use indexmap::map::Entry;
use indexmap::{Equivalent, IndexMap, IndexSet};
use itertools::Itertools;
pub use metadata::ProjectMetadata;
use miette::{miette, Diagnostic, IntoDiagnostic, LabeledSpan, NamedSource, WrapErr};
use miette::{miette, Diagnostic, IntoDiagnostic, NamedSource, WrapErr};
use pyproject::PyProjectManifest;
pub use python::PyPiRequirement;
use rattler_conda_types::{
Expand All @@ -47,7 +47,9 @@ use std::{
pub use system_requirements::{LibCSystemRequirement, SystemRequirements};
pub use target::{Target, TargetSelector, Targets};
use thiserror::Error;
use toml_edit::{DocumentMut, TomlError};
use toml_edit::DocumentMut;

use self::error::TomlError;

/// Errors that can occur when getting a feature.
#[derive(Debug, Clone, Error, Diagnostic)]
Expand Down Expand Up @@ -127,21 +129,14 @@ impl Manifest {
),
};

let (manifest, document) = match parsed
.and_then(|manifest| contents.parse::<DocumentMut>().map(|doc| (manifest, doc)))
{
let (manifest, document) = match parsed.and_then(|manifest| {
contents
.parse::<DocumentMut>()
.map(|doc| (manifest, doc))
.map_err(TomlError::from)
}) {
Ok(result) => result,
Err(e) => {
if let Some(span) = e.span() {
return Err(miette::miette!(
labels = vec![LabeledSpan::at(span, e.message())],
"failed to parse project manifest"
)
.with_source_code(NamedSource::new(file_name, contents)));
} else {
return Err(e).into_diagnostic();
}
}
Err(e) => e.to_fancy(file_name, &contents)?,
};

// Validate the contents of the manifest
Expand Down Expand Up @@ -777,7 +772,15 @@ pub struct ProjectManifest {
impl ProjectManifest {
/// Parses a toml string into a project manifest.
pub fn from_toml_str(source: &str) -> Result<Self, TomlError> {
toml_edit::de::from_str(source).map_err(TomlError::from)
let manifest: ProjectManifest = toml_edit::de::from_str(source).map_err(TomlError::from)?;

// Make sure project.name is defined
if manifest.project.name.is_none() {
let span = source.parse::<DocumentMut>().map_err(TomlError::from)?["project"].span();
return Err(TomlError::NoProjectName(span));
}

Ok(manifest)
}

/// Returns the default feature.
Expand Down Expand Up @@ -1118,7 +1121,7 @@ mod tests {
// From PathBuf
let manifest = Manifest::from_path(path).unwrap();

assert_eq!(manifest.parsed.project.name, "foo");
assert_eq!(manifest.parsed.project.name.unwrap(), "foo");
assert_eq!(
manifest.parsed.project.version,
Some(Version::from_str("0.1.0").unwrap())
Expand Down
22 changes: 16 additions & 6 deletions src/project/manifest/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use rattler_conda_types::{NamelessMatchSpec, PackageName, ParseStrictness::Lenie
use serde::Deserialize;
use std::str::FromStr;
use toml_edit;
use toml_edit::TomlError;

use super::{
error::RequirementConversionError, python::PyPiPackageName, ProjectManifest, PyPiRequirement,
SpecType,
error::{RequirementConversionError, TomlError},
python::PyPiPackageName,
ProjectManifest, PyPiRequirement, SpecType,
};

#[derive(Deserialize, Debug, Clone)]
Expand All @@ -33,7 +33,17 @@ impl std::ops::Deref for PyProjectManifest {
impl PyProjectManifest {
/// Parses a toml string into a pyproject manifest.
pub fn from_toml_str(source: &str) -> Result<Self, TomlError> {
toml_edit::de::from_str(source).map_err(TomlError::from)
let manifest: PyProjectManifest =
toml_edit::de::from_str(source).map_err(TomlError::from)?;

// Make sure [project] exists in pyproject.toml,
// This will ensure project.name is defined
// TODO: do we want to Err if tool.pixi.name is defined?
if manifest.project.is_none() {
return Err(TomlError::NoProjectTable);
}

Ok(manifest)
}
}

Expand All @@ -47,8 +57,9 @@ impl From<PyProjectManifest> for ProjectManifest {
.as_ref()
.expect("the [project] table should exist");

// TODO: tool.pixi.project.name should be made optional or read from project.name
// Get tool.pixi.project.name from project.name
// TODO: could copy across / convert some other optional fields if relevant
manifest.project.name = item.project.as_ref().map(|p| p.name.clone());

// Add python as dependency based on the project.requires_python property (if any)
let pythonspec = pyproject
Expand Down Expand Up @@ -118,7 +129,6 @@ mod tests {
name = "project"
[tool.pixi.project]
name = "project"
version = "0.1.0"
description = "A project"
authors = ["Author <author@bla.com>"]
Expand Down
7 changes: 6 additions & 1 deletion src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,12 @@ impl Project {

/// Returns the name of the project
pub fn name(&self) -> &str {
&self.manifest.parsed.project.name
self.manifest
.parsed
.project
.name
.as_ref()
.expect("name should always be defined.")
}

/// Returns the version of the project
Expand Down

0 comments on commit 2d70705

Please sign in to comment.