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
40 changes: 14 additions & 26 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ pep440_rs = "0.7.3"
pep508_rs = "0.9.2"
percent-encoding = "2.3.1"
pin-project-lite = "0.2.16"
pyproject-toml = "0.13.4"
pyproject-toml = "0.13.6"
rand = { version = "0.9.1", default-features = false }
rayon = "1.10.0"
regex = "1.11.1"
Expand Down
97 changes: 94 additions & 3 deletions crates/pixi/tests/integration_rust/pypi_tests.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,104 @@
use std::io::Write;
use std::{fs::File, io::Write, str::FromStr};

use pep508_rs::Requirement;
use rattler_conda_types::Platform;
use tempfile::tempdir;
use typed_path::Utf8TypedPath;

use crate::common::pypi_index::{Database as PyPIDatabase, PyPIPackage};
use crate::common::{LockFileExt, PixiControl};
use crate::common::{
LockFileExt, PixiControl,
package_database::{Package, PackageDatabase},
};
use crate::setup_tracing;
use std::fs::File;

/// This tests if we can resolve pyproject optional dependencies recursively
/// before when running `pixi list -e all`, this would have not included numpy
/// we are now explicitly testing that this works
#[tokio::test]
async fn pyproject_optional_dependencies_resolve_recursively() {
setup_tracing();

let simple = PyPIDatabase::new()
.with(PyPIPackage::new("numpy", "1.0.0"))
.with(PyPIPackage::new("sphinx", "1.0.0"))
.with(PyPIPackage::new("pytest", "1.0.0"))
.into_simple_index()
.unwrap();

let platform = Platform::current();
let platform_str = platform.to_string();

let mut package_db = PackageDatabase::default();
package_db.add_package(
Package::build("python", "3.11.0")
.with_subdir(platform)
.finish(),
);
let channel = package_db.into_channel().await.unwrap();
let channel_url = channel.url();
let index_url = simple.index_url();

let pyproject = format!(
r#"
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "recursive-optional-groups"

[project.optional-dependencies]
np = ["numpy"]
all = ["recursive-optional-groups[np]"]

[dependency-groups]
docs = ["sphinx"]
test = ["recursive-optional-groups[np]", "pytest", {{include-group = "docs"}}]

[tool.pixi.project]
channels = ["{channel_url}"]
platforms = ["{platform}"]

[tool.pixi.dependencies]
python = "==3.11.0"

[tool.pixi.pypi-options]
index-url = "{index_url}"

[tool.pixi.environments]
np = {{features = ["np"]}}
all = {{features = ["all"]}}
test = {{features = ["test"]}}
"#,
platform = platform_str,
channel_url = channel_url,
index_url = index_url,
);

let pixi = PixiControl::from_pyproject_manifest(&pyproject).unwrap();

let lock = pixi.update_lock_file().await.unwrap();

let numpy_req = Requirement::from_str("numpy").unwrap();
let sphinx_req = Requirement::from_str("sphinx").unwrap();
assert!(
lock.contains_pep508_requirement("np", platform, numpy_req.clone()),
"np environment should include numpy from optional dependencies"
);
assert!(
lock.contains_pep508_requirement("all", platform, numpy_req.clone()),
"all environment should include numpy inherited from recursive optional dependency"
);
assert!(
lock.contains_pep508_requirement("test", platform, numpy_req),
"test environment should include numpy inherited from recursive optional dependency"
);
assert!(
lock.contains_pep508_requirement("test", platform, sphinx_req),
"test environment should include sphinx inherited from recursive dependency group"
);
}

#[tokio::test]
#[cfg_attr(not(feature = "slow_integration_tests"), ignore)]
Expand Down
6 changes: 3 additions & 3 deletions crates/pixi_api/src/workspace/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,10 @@ pub(crate) async fn init<I: Interface>(
}

let (name, pixi_name) = match pyproject.name() {
Some(name) => (name, false),
None => (default_name.as_str(), true),
Some(name) => (name.to_string(), false),
None => (default_name.clone(), true),
};
let environments = pyproject.environments_from_extras().into_diagnostic()?;
let environments = pyproject.environments_from_groups().into_diagnostic()?;
let rv = env
.render_named_str(
consts::PYPROJECT_MANIFEST,
Expand Down
4 changes: 4 additions & 0 deletions crates/pixi_manifest/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use itertools::Itertools;
use miette::{Diagnostic, LabeledSpan, SourceOffset, SourceSpan};
use pixi_pypi_spec::Pep508ToPyPiRequirementError;
use pixi_toml::TomlDiagnostic;
use pyproject_toml::ResolveError;
use rattler_conda_types::{InvalidPackageNameError, version_spec::ParseVersionSpecError};
use thiserror::Error;
use toml_span::{DeserError, Error};
Expand Down Expand Up @@ -113,6 +114,8 @@ pub enum TomlError {
#[error(transparent)]
Conversion(#[from] Box<Pep508ToPyPiRequirementError>),
#[error(transparent)]
ResolveError(#[from] ResolveError),
#[error(transparent)]
InvalidNonPackageDependencies(#[from] InvalidNonPackageDependencies),
}

Expand Down Expand Up @@ -163,6 +166,7 @@ impl Display for TomlError {
write!(f, "Could not convert pep508 to pixi pypi requirement")
}
TomlError::InvalidNonPackageDependencies(err) => write!(f, "{err}"),
TomlError::ResolveError(err) => write!(f, "{err}"),
}
}
}
Expand Down
Loading
Loading