Skip to content

Commit

Permalink
Merge pull request #1833 from prefix-dev/feature/pixi-global
Browse files Browse the repository at this point in the history
feat: new `pixi global`
  • Loading branch information
Hofer-Julian authored Oct 16, 2024
2 parents 5fa78cf + 88af4dc commit 1fdc406
Show file tree
Hide file tree
Showing 136 changed files with 24,449 additions and 1,237 deletions.
23 changes: 21 additions & 2 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ distribution-types = { git = "https://github.com/astral-sh/uv", tag = "0.4.0" }
dunce = "1.0.4"
fd-lock = "4.0.2"
flate2 = "1.0.28"
fs-err = { version = "2.11.0" }
fs_extra = "1.3.0"
futures = "0.3.30"
http = "1.1.0"
Expand Down Expand Up @@ -229,6 +230,7 @@ rattler_repodata_gateway = { workspace = true, features = [
rattler_shell = { workspace = true, features = ["sysinfo"] }
rattler_solve = { workspace = true, features = ["resolvo", "serde"] }

fs-err = { workspace = true, features = ["tokio"] }
pixi_config = { workspace = true, features = ["rattler_repodata_gateway"] }
pixi_consts = { workspace = true }
pixi_default_versions = { workspace = true }
Expand Down Expand Up @@ -302,6 +304,7 @@ strip = false

[dev-dependencies]
async-trait = { workspace = true }
fake = "2.9.2"
http = { workspace = true }
insta = { workspace = true, features = ["yaml", "glob"] }
rstest = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions crates/pixi_consts/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub const TASK_CACHE_DIR: &str = "task-cache-v0";
pub const PIXI_UV_INSTALLER: &str = "uv-pixi";
pub const CONDA_PACKAGE_CACHE_DIR: &str = rattler_cache::PACKAGE_CACHE_DIR;
pub const CONDA_REPODATA_CACHE_DIR: &str = rattler_cache::REPODATA_CACHE_DIR;
// TODO: move to rattler
pub const CONDA_META_DIR: &str = "conda-meta";
pub const PYPI_CACHE_DIR: &str = "uv-cache";
pub const CONDA_PYPI_MAPPING_CACHE_DIR: &str = "conda-pypi-mapping";
pub const CACHED_ENVS_DIR: &str = "cached-envs-v0";
Expand All @@ -39,6 +41,7 @@ lazy_static! {
pub static ref TASK_STYLE: Style = Style::new().blue();
pub static ref PLATFORM_STYLE: Style = Style::new().yellow();
pub static ref ENVIRONMENT_STYLE: Style = Style::new().magenta();
pub static ref EXPOSED_NAME_STYLE: Style = Style::new().yellow();
pub static ref FEATURE_STYLE: Style = Style::new().cyan();
pub static ref SOLVE_GROUP_STYLE: Style = Style::new().cyan();
pub static ref DEFAULT_PYPI_INDEX_URL: Url = Url::parse("https://pypi.org/simple").unwrap();
Expand Down
65 changes: 62 additions & 3 deletions crates/pixi_manifest/src/channel.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
use std::str::FromStr;

use itertools::Itertools;
use rattler_conda_types::NamedChannelOrUrl;
use serde::{de::Error, Deserialize, Deserializer};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use serde_with::serde_as;
use toml_edit::{Table, Value};

/// A channel with an optional priority.
/// If the priority is not specified, it is assumed to be 0.
/// The higher the priority, the more important the channel is.
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct PrioritizedChannel {
pub channel: NamedChannelOrUrl,
pub priority: Option<i32>,
}

impl PrioritizedChannel {
/// The prioritized channels contain a priority, sort on this priority.
/// Higher priority comes first. [-10, 1, 0 ,2] -> [2, 1, 0, -10]
pub fn sort_channels_by_priority<'a, I>(
channels: I,
) -> impl Iterator<Item = &'a NamedChannelOrUrl>
where
I: IntoIterator<Item = &'a crate::PrioritizedChannel>,
{
channels
.into_iter()
.sorted_by(|a, b| {
let a = a.priority.unwrap_or(0);
let b = b.priority.unwrap_or(0);
b.cmp(&a)
})
.map(|prioritized_channel| &prioritized_channel.channel)
}
}

impl From<NamedChannelOrUrl> for PrioritizedChannel {
fn from(value: NamedChannelOrUrl) -> Self {
Self {
Expand Down Expand Up @@ -64,6 +85,19 @@ impl TomlPrioritizedChannelStrOrMap {
}
}

impl From<PrioritizedChannel> for TomlPrioritizedChannelStrOrMap {
fn from(channel: PrioritizedChannel) -> Self {
if let Some(priority) = channel.priority {
TomlPrioritizedChannelStrOrMap::Map(PrioritizedChannel {
channel: channel.channel,
priority: Some(priority),
})
} else {
TomlPrioritizedChannelStrOrMap::Str(channel.channel)
}
}
}

impl<'de> Deserialize<'de> for TomlPrioritizedChannelStrOrMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand All @@ -81,8 +115,20 @@ impl<'de> Deserialize<'de> for TomlPrioritizedChannelStrOrMap {
}
}

impl Serialize for TomlPrioritizedChannelStrOrMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
TomlPrioritizedChannelStrOrMap::Map(map) => map.serialize(serializer),
TomlPrioritizedChannelStrOrMap::Str(str) => str.serialize(serializer),
}
}
}

/// Helper so that we can deserialize
/// [`crate::project::manifest::serde::PrioritizedChannel`] from a string or a
/// [`crate::channel::PrioritizedChannel`] from a string or a
/// map.
impl<'de> serde_with::DeserializeAs<'de, PrioritizedChannel> for TomlPrioritizedChannelStrOrMap {
fn deserialize_as<D>(deserializer: D) -> Result<PrioritizedChannel, D::Error>
Expand All @@ -93,3 +139,16 @@ impl<'de> serde_with::DeserializeAs<'de, PrioritizedChannel> for TomlPrioritized
Ok(prioritized_channel.into_prioritized_channel())
}
}

/// Helper so that we can serialize
/// [`crate::channel::PrioritizedChannel`] to a string or a
/// map.
impl serde_with::SerializeAs<PrioritizedChannel> for TomlPrioritizedChannelStrOrMap {
fn serialize_as<S>(source: &PrioritizedChannel, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let toml_prioritized_channel: TomlPrioritizedChannelStrOrMap = source.clone().into();
toml_prioritized_channel.serialize(serializer)
}
}
24 changes: 6 additions & 18 deletions crates/pixi_manifest/src/features_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use indexmap::IndexSet;
use rattler_conda_types::{NamedChannelOrUrl, Platform};
use rattler_solve::ChannelPriority;

use crate::{HasManifestRef, SpecType};
use crate::{HasManifestRef, PrioritizedChannel, SpecType};

use crate::has_features_iter::HasFeaturesIter;
use crate::{pypi::pypi_options::PypiOptions, SystemRequirements};
Expand Down Expand Up @@ -35,24 +35,12 @@ pub trait FeaturesExt<'source>: HasManifestRef<'source> + HasFeaturesIter<'sourc
fn channels(&self) -> IndexSet<&'source NamedChannelOrUrl> {
// Collect all the channels from the features in one set,
// deduplicate them and sort them on feature index, default feature comes last.
let channels: IndexSet<_> = self
.features()
.flat_map(|feature| match &feature.channels {
Some(channels) => channels,
None => &self.manifest().parsed.project.channels,
})
.collect();
let channels = self.features().flat_map(|feature| match &feature.channels {
Some(channels) => channels,
None => &self.manifest().parsed.project.channels,
});

// The prioritized channels contain a priority, sort on this priority.
// Higher priority comes first. [-10, 1, 0 ,2] -> [2, 1, 0, -10]
channels
.sorted_by(|a, b| {
let a = a.priority.unwrap_or(0);
let b = b.priority.unwrap_or(0);
b.cmp(&a)
})
.map(|prioritized_channel| &prioritized_channel.channel)
.collect()
PrioritizedChannel::sort_channels_by_priority(channels).collect()
}

/// Returns the channel priority, error on multiple values, return None if no value is set.
Expand Down
6 changes: 4 additions & 2 deletions crates/pixi_manifest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ mod validation;
pub use dependencies::{CondaDependencies, Dependencies, PyPiDependencies};

pub use manifests::manifest::{Manifest, ManifestKind};
pub use manifests::TomlManifest;

pub use crate::environments::Environments;
pub use crate::parsed_manifest::ParsedManifest;
pub use crate::parsed_manifest::{deserialize_package_map, ParsedManifest};
pub use crate::solve_group::{SolveGroup, SolveGroups};
pub use activation::Activation;
pub use channel::PrioritizedChannel;
pub use channel::{PrioritizedChannel, TomlPrioritizedChannelStrOrMap};
pub use environment::{Environment, EnvironmentName};
pub use error::TomlError;
pub use feature::{Feature, FeatureName};
use itertools::Itertools;
pub use metadata::ProjectMetadata;
Expand Down
58 changes: 57 additions & 1 deletion crates/pixi_manifest/src/manifests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt;

use toml_edit::{self, Array, Item, Table, TableLike, Value};

pub mod project;
Expand All @@ -20,10 +22,15 @@ impl TomlManifest {
Self(document)
}

/// Get or insert a top-level item
pub fn get_or_insert<'a>(&'a mut self, key: &str, item: Item) -> &'a Item {
self.0.entry(key).or_insert(item)
}

/// Retrieve a mutable reference to a target table `table_name`
/// in dotted form (e.g. `table1.table2`) from the root of the document.
/// If the table is not found, it is inserted into the document.
fn get_or_insert_nested_table<'a>(
pub fn get_or_insert_nested_table<'a>(
&'a mut self,
table_name: &str,
) -> Result<&'a mut dyn TableLike, TomlError> {
Expand All @@ -45,6 +52,49 @@ impl TomlManifest {
Ok(current_table)
}

/// Inserts a value into a certain table
/// If the most inner table doesn't exist, an inline table will be created.
/// If it already exists, the formatting of the table will be preserved
pub fn insert_into_inline_table<'a>(
&'a mut self,
table_name: &str,
key: &str,
value: Value,
) -> Result<&'a mut dyn TableLike, TomlError> {
let mut parts: Vec<&str> = table_name.split('.').collect();

let last = parts.pop();

let mut current_table = self.0.as_table_mut() as &mut dyn TableLike;

for part in parts {
let entry = current_table.entry(part);
let item = entry.or_insert(Item::Table(Table::new()));
if let Some(table) = item.as_table_mut() {
// Avoid creating empty tables
table.set_implicit(true);
}
current_table = item
.as_table_like_mut()
.ok_or_else(|| TomlError::table_error(part, table_name))?;
}

// Add dependency as inline table if it doesn't exist
if let Some(last) = last {
if let Some(dependency) = current_table.get_mut(last) {
dependency
.as_table_like_mut()
.map(|table| table.insert(key, Item::Value(value)));
} else {
let mut dependency = toml_edit::InlineTable::new();
dependency.insert(key, value);
current_table.insert(last, toml_edit::value(dependency));
}
}

Ok(current_table)
}

/// Retrieves a mutable reference to a target array `array_name`
/// in table `table_name` in dotted form (e.g. `table1.table2.array`).
///
Expand Down Expand Up @@ -78,6 +128,12 @@ impl TomlManifest {
}
}

impl fmt::Display for TomlManifest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
Expand Down
1 change: 1 addition & 0 deletions crates/pixi_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ url = { workspace = true }

[dev-dependencies]
insta = { workspace = true }
rstest = { workspace = true }
Loading

0 comments on commit 1fdc406

Please sign in to comment.