diff --git a/Cargo.toml b/Cargo.toml index bedbcbb15..7a75a62e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,6 +119,7 @@ serde_json = { version = "1.0.116" } serde_repr = "0.1" serde_with = "3.7.0" serde_yaml = "0.9.34" +serde-untagged = "0.1.6" sha2 = "0.10.8" shlex = "1.3.0" similar-asserts = "1.5.0" diff --git a/crates/rattler_conda_types/Cargo.toml b/crates/rattler_conda_types/Cargo.toml index a81fa52b9..25f54775a 100644 --- a/crates/rattler_conda_types/Cargo.toml +++ b/crates/rattler_conda_types/Cargo.toml @@ -28,16 +28,19 @@ serde = { workspace = true, features = ["derive", "rc"] } serde_json = { workspace = true } serde_repr = { workspace = true } serde_with = { workspace = true, features = ["indexmap_2"] } +serde-untagged = { workspace = true } +serde_yaml = { workspace = true } smallvec = { workspace = true, features = ["serde", "const_new", "const_generics", "union"] } strum = { workspace = true, features = ["derive"] } thiserror = { workspace = true } tracing = { workspace = true } typed-path = { workspace = true } url = { workspace = true, features = ["serde"] } +indexmap = { workspace = true } [dev-dependencies] rand = { workspace = true } -insta = { workspace = true, features = ["yaml", "redactions", "toml"] } +insta = { workspace = true, features = ["yaml", "redactions", "toml", "glob"] } rattler_package_streaming = { path = "../rattler_package_streaming", default-features = false, features = ["rustls-tls"] } tempfile = { workspace = true } rstest = { workspace = true } diff --git a/crates/rattler_conda_types/src/channel/mod.rs b/crates/rattler_conda_types/src/channel/mod.rs index 4d9318992..7e6e2b2da 100644 --- a/crates/rattler_conda_types/src/channel/mod.rs +++ b/crates/rattler_conda_types/src/channel/mod.rs @@ -1,40 +1,43 @@ -use std::borrow::Cow; -use std::fmt::{Display, Formatter}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; +use std::{ + borrow::Cow, + fmt::{Display, Formatter}, + path::{Path, PathBuf}, + str::FromStr, +}; -use crate::utils::path::is_path; -use crate::utils::url::parse_scheme; use file_url::directory_path_to_url; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; use thiserror::Error; use typed_path::{Utf8NativePathBuf, Utf8TypedPath, Utf8TypedPathBuf}; use url::Url; use super::{ParsePlatformError, Platform}; +use crate::utils::{path::is_path, url::parse_scheme}; const DEFAULT_CHANNEL_ALIAS: &str = "https://conda.anaconda.org"; -/// The `ChannelConfig` describes properties that are required to resolve "simple" channel names to -/// channel URLs. +/// The `ChannelConfig` describes properties that are required to resolve +/// "simple" channel names to channel URLs. /// -/// When working with [`Channel`]s you want to resolve them to a Url. The Url describes where to -/// find the data in the channel. Working with URLs is less user friendly since most of the time -/// users only use channels from one particular server. Conda solves this by allowing users not to -/// specify a full Url but instead only specify the name of the channel and reading the primary +/// When working with [`Channel`]s you want to resolve them to a Url. The Url +/// describes where to find the data in the channel. Working with URLs is less +/// user friendly since most of the time users only use channels from one +/// particular server. Conda solves this by allowing users not to specify a full +/// Url but instead only specify the name of the channel and reading the primary /// server address from a configuration file (e.g. `.condarc`). #[derive(Debug, Clone, Serialize, Deserialize, Hash)] pub struct ChannelConfig { - /// A url to prefix to channel names that don't start with a Url. Usually this Url refers to - /// the `https://conda.anaconda.org` server but users are free to change this. This allows - /// naming channels just by their name instead of their entire Url (e.g. "conda-forge" actually - /// refers to ``). + /// A url to prefix to channel names that don't start with a Url. Usually + /// this Url refers to the `https://conda.anaconda.org` server but users are free to change this. This allows + /// naming channels just by their name instead of their entire Url (e.g. + /// "conda-forge" actually refers to ``). /// /// The default value is: pub channel_alias: Url, - /// For local channels, the root directory from which to resolve relative paths. - /// Most of the time you would initialize this with the current working directory. + /// For local channels, the root directory from which to resolve relative + /// paths. Most of the time you would initialize this with the current + /// working directory. pub root_dir: PathBuf, } @@ -58,7 +61,8 @@ impl ChannelConfig { } } -/// Represents a channel description as either a name (e.g. `conda-forge`) or a base url. +/// Represents a channel description as either a name (e.g. `conda-forge`) or a +/// base url. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum NamedChannelOrUrl { /// A named channel @@ -71,7 +75,8 @@ pub enum NamedChannelOrUrl { impl NamedChannelOrUrl { /// Returns the string representation of the channel. /// - /// This method ensures that if the channel is a url, it does not end with a `/`. + /// This method ensures that if the channel is a url, it does not end with a + /// `/`. pub fn as_str(&self) -> &str { match self { NamedChannelOrUrl::Name(name) => name, @@ -117,11 +122,42 @@ impl Display for NamedChannelOrUrl { } } +impl FromStr for NamedChannelOrUrl { + type Err = url::ParseError; + + fn from_str(s: &str) -> Result { + if parse_scheme(s).is_some() { + Ok(NamedChannelOrUrl::Url(Url::from_str(s)?)) + } else { + Ok(NamedChannelOrUrl::Name(s.to_string())) + } + } +} + +impl<'de> serde::Deserialize<'de> for NamedChannelOrUrl { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + NamedChannelOrUrl::from_str(&s).map_err(serde::de::Error::custom) + } +} + +impl serde::Serialize for NamedChannelOrUrl { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_str().serialize(serializer) + } +} + /// `Channel`s are the primary source of package information. #[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash)] pub struct Channel { - /// The platforms supported by this channel, or None if no explicit platforms have been - /// specified. + /// The platforms supported by this channel, or None if no explicit + /// platforms have been specified. #[serde(skip_serializing_if = "Option::is_none")] pub platforms: Option>, @@ -247,7 +283,8 @@ impl Channel { /// /// # Panics /// - /// Panics if the path is not an absolute path or could not be canonicalized. + /// Panics if the path is not an absolute path or could not be + /// canonicalized. pub fn from_directory(path: &Path) -> Self { let path = if path.is_absolute() { Cow::Borrowed(path) @@ -279,7 +316,8 @@ impl Channel { } } - /// Returns the base Url of the channel. This does not include the platform part. + /// Returns the base Url of the channel. This does not include the platform + /// part. pub fn base_url(&self) -> &Url { &self.base_url } @@ -299,8 +337,8 @@ impl Channel { .collect() } - /// Returns the platforms explicitly mentioned in the channel or the default platforms of the - /// current system. + /// Returns the platforms explicitly mentioned in the channel or the default + /// platforms of the current system. pub fn platforms_or_default(&self) -> &[Platform] { if let Some(platforms) = &self.platforms { platforms.as_slice() @@ -375,8 +413,8 @@ fn parse_platforms(channel: &str) -> Result<(Option>, &str), Parse Ok((None, channel)) } -/// Returns the default platforms. These are based on the platform this binary was build for as well -/// as platform agnostic platforms. +/// Returns the default platforms. These are based on the platform this binary +/// was build for as well as platform agnostic platforms. pub(crate) const fn default_platforms() -> &'static [Platform] { const CURRENT_PLATFORMS: [Platform; 2] = [Platform::current(), Platform::NoArch]; &CURRENT_PLATFORMS @@ -405,10 +443,12 @@ fn absolute_path(path: &str, root_dir: &Path) -> Result, + + /// The preferred path to the environment. + #[serde(skip_serializing_if = "Option::is_none")] + pub prefix: Option, + + /// A list of channels that are used to resolve dependencies. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub channels: Vec, + + /// A list of matchspecs that are required for the environment. Or a + /// subsection of specs for another package manager. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub dependencies: Vec, + + /// An optional list of variables. + /// These variables should be dumped into the `conda-meta/state` file of the + /// target environment. + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub variables: IndexMap, +} + +/// A matchspec or a subsection, as part of the `dependencies` section of an +/// `environment.yaml` file. +#[derive(Debug, Clone, PartialEq)] +pub enum MatchSpecOrSubSection { + MatchSpec(MatchSpec), + SubSection(String, Vec), +} + +impl MatchSpecOrSubSection { + /// Returns the matchspec if this is a matchspec, or `None` otherwise. + pub fn as_match_spec(&self) -> Option<&MatchSpec> { + match self { + MatchSpecOrSubSection::MatchSpec(s) => Some(s), + MatchSpecOrSubSection::SubSection(_, _) => None, + } + } + + /// Returns the subsection if this is a subsection, or `None` otherwise. + pub fn as_sub_section(&self) -> Option<(&String, &Vec)> { + match self { + MatchSpecOrSubSection::MatchSpec(_) => None, + MatchSpecOrSubSection::SubSection(key, specs) => Some((key, specs)), + } + } +} + +impl EnvironmentYaml { + /// Returns all the matchspecs in the `dependencies` section of the file. + pub fn match_specs(&self) -> impl Iterator + DoubleEndedIterator + '_ { + self.dependencies + .iter() + .filter_map(MatchSpecOrSubSection::as_match_spec) + } + + /// Returns the subsection with the given name or `None` if no such + /// subsection exists. + pub fn find_sub_section(&self, name: &str) -> Option<&[String]> { + self.dependencies + .iter() + .filter_map(MatchSpecOrSubSection::as_sub_section) + .find_map(|(subsection_name, specs)| { + (subsection_name == name).then_some(specs.as_slice()) + }) + } + + /// Returns the `pip` subsection + pub fn pip_specs(&self) -> Option<&[String]> { + self.find_sub_section("pip") + } + + /// Reads the contents of a file at the given path and parses it as an + /// `environment.yaml` file. + pub fn from_path(path: &Path) -> std::io::Result { + let contents = std::fs::read_to_string(path)?; + Self::from_yaml_str(&contents) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e)) + } + + /// Reads the contents of a string and parses it as an `environment.yaml` + pub fn from_yaml_str(contents: &str) -> Result { + serde_yaml::from_str(contents) + } + + /// Write the contents of this `environment.yaml` file to the given path. + pub fn to_path(&self, path: &Path) -> std::io::Result<()> { + std::fs::write(path, self.to_yaml_string()) + } + + /// Converts the contents of this `environment.yaml` file to a string. + pub fn to_yaml_string(&self) -> String { + serde_yaml::to_string(&self).expect("failed to serialize to a string") + } +} + +impl<'a> serde::Deserialize<'a> for MatchSpecOrSubSection { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + serde_untagged::UntaggedEnumVisitor::new() + .string(|v| { + Ok(MatchSpecOrSubSection::MatchSpec( + MatchSpec::from_str(v, ParseStrictness::Lenient) + .map_err(serde_untagged::de::Error::custom)?, + )) + }) + .map(|v| { + struct SubSectionVisitor; + + impl<'a> Visitor<'a> for SubSectionVisitor { + type Value = MatchSpecOrSubSection; + + fn expecting( + &self, + formatter: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + formatter.write_str("a list of strings") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'a>, + { + let (key, value) = map + .next_entry()? + .ok_or_else(|| serde::de::Error::custom("expected a map entry"))?; + if map.next_key::()?.is_some() { + return Err(serde::de::Error::custom( + "expected a map with a single entry", + )); + } + Ok(MatchSpecOrSubSection::SubSection(key, value)) + } + } + + SubSectionVisitor.visit_map(v) + }) + .deserialize(deserializer) + } +} + +impl serde::Serialize for MatchSpecOrSubSection { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + MatchSpecOrSubSection::MatchSpec(spec) => spec.to_string().serialize(serializer), + MatchSpecOrSubSection::SubSection(key, value) => { + let mut map = serializer.serialize_map(Some(1))?; + map.serialize_entry(key, value)?; + map.end() + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::get_test_data_dir; + + #[test] + fn test_deserialize_environment_yaml() { + insta::glob!( + "../../../test-data/environments", + "*.environment.yaml", + |path| { + insta::assert_yaml_snapshot!(EnvironmentYaml::from_path(path).unwrap()); + } + ); + } + + #[test] + fn test_pip_section() { + let environment_yaml = EnvironmentYaml::from_path( + &get_test_data_dir().join("environments/asymmetric_vqgan.environment.yaml"), + ) + .unwrap(); + insta::assert_debug_snapshot!(environment_yaml.pip_specs()); + } +} diff --git a/crates/rattler_conda_types/src/lib.rs b/crates/rattler_conda_types/src/lib.rs index 2b1b0ebf4..96502fbef 100644 --- a/crates/rattler_conda_types/src/lib.rs +++ b/crates/rattler_conda_types/src/lib.rs @@ -1,6 +1,7 @@ #![deny(missing_docs)] -//! `rattler-conda-types` contains data models for types commonly found within the Conda ecosystem. -//! The library itself doesnt provide any functionality besides parsing the data types. +//! `rattler-conda-types` contains data models for types commonly found within +//! the Conda ecosystem. The library itself doesnt provide any functionality +//! besides parsing the data types. mod build_spec; mod channel; @@ -17,31 +18,39 @@ mod utils; mod version; pub mod version_spec; +mod environment_yaml; mod generic_virtual_package; pub mod package; mod package_name; pub mod prefix_record; +#[cfg(test)] +use std::path::{Path, PathBuf}; + pub use build_spec::{BuildNumber, BuildNumberSpec, ParseBuildNumberSpecError}; pub use channel::{Channel, ChannelConfig, NamedChannelOrUrl, ParseChannelError}; pub use channel_data::{ChannelData, ChannelDataPackage}; +pub use environment_yaml::EnvironmentYaml; pub use explicit_environment_spec::{ ExplicitEnvironmentEntry, ExplicitEnvironmentSpec, PackageArchiveHash, ParseExplicitEnvironmentSpecError, ParsePackageArchiveHashError, }; pub use generic_virtual_package::GenericVirtualPackage; -pub use match_spec::matcher::{StringMatcher, StringMatcherParseError}; -pub use match_spec::parse::ParseMatchSpecError; -pub use match_spec::{MatchSpec, Matches, NamelessMatchSpec}; +pub use match_spec::{ + matcher::{StringMatcher, StringMatcherParseError}, + parse::ParseMatchSpecError, + MatchSpec, Matches, NamelessMatchSpec, +}; pub use no_arch_type::{NoArchKind, NoArchType}; pub use package_name::{InvalidPackageNameError, PackageName}; pub use parse_mode::ParseStrictness; pub use platform::{Arch, ParseArchError, ParsePlatformError, Platform}; pub use prefix_record::PrefixRecord; -pub use repo_data::patches::{PackageRecordPatch, PatchInstructions, RepoDataPatch}; -pub use repo_data::sharded::{Shard, ShardedRepodata, ShardedSubdirInfo}; pub use repo_data::{ - compute_package_url, ChannelInfo, ConvertSubdirError, PackageRecord, RepoData, + compute_package_url, + patches::{PackageRecordPatch, PatchInstructions, RepoDataPatch}, + sharded::{Shard, ShardedRepodata, ShardedSubdirInfo}, + ChannelInfo, ConvertSubdirError, PackageRecord, RepoData, }; pub use repo_data_record::RepoDataRecord; pub use run_export::RunExportKind; @@ -51,10 +60,8 @@ pub use version::{ }; pub use version_spec::VersionSpec; -#[cfg(test)] -use std::path::{Path, PathBuf}; - -/// An package identifier that can be used to identify packages across package ecosystems. +/// An package identifier that can be used to identify packages across package +/// ecosystems. pub type PackageUrl = purl::GenericPurl; #[cfg(test)] diff --git a/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@asymmetric_vqgan.environment.yaml.snap b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@asymmetric_vqgan.environment.yaml.snap new file mode 100644 index 000000000..59358ca9d --- /dev/null +++ b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@asymmetric_vqgan.environment.yaml.snap @@ -0,0 +1,36 @@ +--- +source: crates/rattler_conda_types/src/environment_yaml.rs +assertion_line: 187 +expression: "EnvironmentYaml::from_path(path).unwrap()" +input_file: test-data/environments/asymmetric_vqgan.environment.yaml +--- +channels: + - pytorch + - defaults +dependencies: + - python 3.8.5.* + - pip 20.3.* + - cudatoolkit 11.0.* + - pytorch 1.7.0.* + - torchvision 0.8.1.* + - numpy 1.19.2.* + - pip: + - albumentations==0.4.3 + - easydict==1.10 + - scikit-learn==1.2.0 + - opencv-python==4.1.2.30 + - pudb==2019.2 + - imageio==2.9.0 + - imageio-ffmpeg==0.4.2 + - pytorch-lightning==1.4.2 + - omegaconf==2.1.1 + - test-tube>=0.7.5 + - streamlit>=0.73.1 + - einops==0.3.0 + - torch-fidelity==0.3.0 + - transformers==4.6.0 + - torchmetrics==0.6 + - academictorrents==2.3.3 + - "-e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers" + - "-e git+https://github.com/openai/CLIP.git@main#egg=clip" + - "-e ." diff --git a/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@channel_inversion.environment.yaml.snap b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@channel_inversion.environment.yaml.snap new file mode 100644 index 000000000..f2ec5268c --- /dev/null +++ b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@channel_inversion.environment.yaml.snap @@ -0,0 +1,13 @@ +--- +source: crates/rattler_conda_types/src/environment_yaml.rs +assertion_line: 186 +expression: "EnvironmentYaml::from_path(&path).unwrap()" +input_file: test-data/environments/channel_inversion.environment.yaml +--- +channels: + - rapidsai + - nvidia + - conda-forge +dependencies: + - cudf + - "conda-forge::cuda-python" diff --git a/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@conda_lock_dev.environment.yaml.snap b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@conda_lock_dev.environment.yaml.snap new file mode 100644 index 000000000..14e51c7aa --- /dev/null +++ b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@conda_lock_dev.environment.yaml.snap @@ -0,0 +1,45 @@ +--- +source: crates/rattler_conda_types/src/environment_yaml.rs +assertion_line: 186 +expression: "EnvironmentYaml::from_path(&path).unwrap()" +input_file: test-data/environments/conda_lock_dev.environment.yaml +--- +name: conda-lock-dev +channels: + - conda-forge + - nodefaults +dependencies: + - check-manifest + - docker-py + - doctr + - filelock + - flaky + - freezegun + - gitpython + - mamba + - mkdocs + - mkdocs-click + - mkdocs-material + - mkdocs-include-markdown-plugin + - mypy + - pip + - pre-commit + - pytest + - pytest-cov + - pytest-timeout + - pytest-xdist + - python-build + - requests-mock + - ruff + - tomli + - twine + - types-click + - types-filelock + - types-requests + - types-toml + - types-pyyaml + - types-freezegun + - types-setuptools + - wheel + - pip: + - types-click-default-group diff --git a/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@mamba_dev_extra.environment.yaml.snap b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@mamba_dev_extra.environment.yaml.snap new file mode 100644 index 000000000..a9f1b1e68 --- /dev/null +++ b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@mamba_dev_extra.environment.yaml.snap @@ -0,0 +1,23 @@ +--- +source: crates/rattler_conda_types/src/environment_yaml.rs +assertion_line: 186 +expression: "EnvironmentYaml::from_path(&path).unwrap()" +input_file: test-data/environments/mamba_dev_extra.environment.yaml +--- +channels: + - conda-forge +dependencies: + - ccache + - clang-tools + - clang + - clangxx + - lld + - cmake-format + - ruff + - python-lsp-server-base + - ruff-lsp + - jupyterlab + - ipython + - ipdb + - gh + - go-task diff --git a/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@variables.environment.yaml.snap b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@variables.environment.yaml.snap new file mode 100644 index 000000000..fb979c44c --- /dev/null +++ b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__deserialize_environment_yaml@variables.environment.yaml.snap @@ -0,0 +1,13 @@ +--- +source: crates/rattler_conda_types/src/environment_yaml.rs +assertion_line: 187 +expression: "EnvironmentYaml::from_path(&path).unwrap()" +input_file: test-data/environments/variables.environment.yaml +--- +name: test +channels: + - conda-forge +dependencies: + - numpy +variables: + MY_ENV_VAR: My Value diff --git a/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__pip_section.snap b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__pip_section.snap new file mode 100644 index 000000000..7225febbe --- /dev/null +++ b/crates/rattler_conda_types/src/snapshots/rattler_conda_types__environment_yaml__tests__pip_section.snap @@ -0,0 +1,28 @@ +--- +source: crates/rattler_conda_types/src/environment_yaml.rs +assertion_line: 198 +expression: environment_yaml.pip_specs() +--- +Some( + [ + "albumentations==0.4.3", + "easydict==1.10", + "scikit-learn==1.2.0", + "opencv-python==4.1.2.30", + "pudb==2019.2", + "imageio==2.9.0", + "imageio-ffmpeg==0.4.2", + "pytorch-lightning==1.4.2", + "omegaconf==2.1.1", + "test-tube>=0.7.5", + "streamlit>=0.73.1", + "einops==0.3.0", + "torch-fidelity==0.3.0", + "transformers==4.6.0", + "torchmetrics==0.6", + "academictorrents==2.3.3", + "-e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers", + "-e git+https://github.com/openai/CLIP.git@main#egg=clip", + "-e .", + ], +) diff --git a/py-rattler/Cargo.lock b/py-rattler/Cargo.lock index 304ff936a..afde4805f 100644 --- a/py-rattler/Cargo.lock +++ b/py-rattler/Cargo.lock @@ -765,6 +765,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.9" @@ -830,7 +840,7 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "file_url" -version = "0.1.2" +version = "0.1.3" dependencies = [ "itertools 0.13.0", "percent-encoding", @@ -2671,7 +2681,7 @@ dependencies = [ [[package]] name = "rattler" -version = "0.26.5" +version = "0.27.0" dependencies = [ "anyhow", "console", @@ -2709,7 +2719,7 @@ dependencies = [ [[package]] name = "rattler_cache" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "digest", @@ -2731,13 +2741,14 @@ dependencies = [ [[package]] name = "rattler_conda_types" -version = "0.26.0" +version = "0.26.1" dependencies = [ "chrono", "file_url", "fxhash", "glob", "hex", + "indexmap 2.2.6", "itertools 0.13.0", "lazy-regex", "nom", @@ -2746,9 +2757,11 @@ dependencies = [ "rattler_macros", "regex", "serde", + "serde-untagged", "serde_json", "serde_repr", "serde_with", + "serde_yaml", "simd-json", "smallvec", "strum", @@ -2760,7 +2773,7 @@ dependencies = [ [[package]] name = "rattler_digest" -version = "0.19.4" +version = "0.19.5" dependencies = [ "blake2", "digest", @@ -2775,7 +2788,7 @@ dependencies = [ [[package]] name = "rattler_index" -version = "0.19.18" +version = "0.19.19" dependencies = [ "fs-err", "rattler_conda_types", @@ -2788,7 +2801,7 @@ dependencies = [ [[package]] name = "rattler_lock" -version = "0.22.13" +version = "0.22.14" dependencies = [ "chrono", "file_url", @@ -2817,7 +2830,7 @@ dependencies = [ [[package]] name = "rattler_networking" -version = "0.20.9" +version = "0.20.10" dependencies = [ "anyhow", "async-trait", @@ -2843,7 +2856,7 @@ dependencies = [ [[package]] name = "rattler_package_streaming" -version = "0.21.4" +version = "0.21.5" dependencies = [ "bzip2", "chrono", @@ -2867,7 +2880,7 @@ dependencies = [ [[package]] name = "rattler_repodata_gateway" -version = "0.21.0" +version = "0.21.1" dependencies = [ "anyhow", "async-compression", @@ -2917,7 +2930,7 @@ dependencies = [ [[package]] name = "rattler_shell" -version = "0.21.0" +version = "0.21.1" dependencies = [ "enum_dispatch", "indexmap 2.2.6", @@ -2932,7 +2945,7 @@ dependencies = [ [[package]] name = "rattler_solve" -version = "0.25.0" +version = "0.25.1" dependencies = [ "chrono", "futures", @@ -2948,7 +2961,7 @@ dependencies = [ [[package]] name = "rattler_virtual_packages" -version = "0.19.16" +version = "0.19.18" dependencies = [ "archspec", "libloading", @@ -3422,6 +3435,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + [[package]] name = "serde_derive" version = "1.0.203" @@ -4032,6 +4056,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a3023f4683cd1a846dbd2666e8c34f54338ee5cebae578cda981a87cecd7aa" +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" diff --git a/test-data/environments/asymmetric_vqgan.environment.yaml b/test-data/environments/asymmetric_vqgan.environment.yaml new file mode 100644 index 000000000..0485c9cd9 --- /dev/null +++ b/test-data/environments/asymmetric_vqgan.environment.yaml @@ -0,0 +1,32 @@ +# https://github.com/buxiangzhiren/Asymmetric_VQGAN/blob/406c5cd2e86b3e9565795cc5cd1e3aab304b1a44/environment.yaml +#name: ldm +channels: + - pytorch + - defaults +dependencies: + - python=3.8.5 + - pip=20.3 + - cudatoolkit=11.0 + - pytorch=1.7.0 + - torchvision=0.8.1 + - numpy=1.19.2 + - pip: + - albumentations==0.4.3 + - easydict==1.10 + - scikit-learn==1.2.0 + - opencv-python==4.1.2.30 + - pudb==2019.2 + - imageio==2.9.0 + - imageio-ffmpeg==0.4.2 + - pytorch-lightning==1.4.2 + - omegaconf==2.1.1 + - test-tube>=0.7.5 + - streamlit>=0.73.1 + - einops==0.3.0 + - torch-fidelity==0.3.0 + - transformers==4.6.0 + - torchmetrics==0.6 + - academictorrents==2.3.3 + - "-e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers" + - "-e git+https://github.com/openai/CLIP.git@main#egg=clip" + - "-e ." diff --git a/test-data/environments/channel_inversion.environment.yaml b/test-data/environments/channel_inversion.environment.yaml new file mode 100644 index 000000000..fb86caf20 --- /dev/null +++ b/test-data/environments/channel_inversion.environment.yaml @@ -0,0 +1,12 @@ +channels: + # This example is constructed in this way to mirror a real life packaging + # issue that was experienced in 2022-07. + # In this case we have the channel order as recommended by rapids ai for + # installing cudf, but we want to force getting cuda-python from conda-forge + # instead of from the nvidia channel which would normally have higher priority + - rapidsai + - nvidia + - conda-forge +dependencies: + - cudf + - conda-forge::cuda-python diff --git a/test-data/environments/conda_lock_dev.environment.yaml b/test-data/environments/conda_lock_dev.environment.yaml new file mode 100644 index 000000000..66d10f9f4 --- /dev/null +++ b/test-data/environments/conda_lock_dev.environment.yaml @@ -0,0 +1,40 @@ +name: conda-lock-dev +category: dev +channels: + - conda-forge + - nodefaults +dependencies: + - check-manifest + - docker-py + - doctr + - filelock + - flaky + - freezegun + - gitpython + - mamba + - mkdocs + - mkdocs-click + - mkdocs-material + - mkdocs-include-markdown-plugin + - mypy + - pip + - pre-commit + - pytest + - pytest-cov + - pytest-timeout + - pytest-xdist + - python-build + - requests-mock + - ruff + - tomli + - twine + - types-click + - types-filelock + - types-requests + - types-toml + - types-pyyaml + - types-freezegun + - types-setuptools + - wheel + - pip: + - types-click-default-group diff --git a/test-data/environments/mamba_dev_extra.environment.yaml b/test-data/environments/mamba_dev_extra.environment.yaml new file mode 100644 index 000000000..0a526bba6 --- /dev/null +++ b/test-data/environments/mamba_dev_extra.environment.yaml @@ -0,0 +1,25 @@ +# https://github.com/mamba-org/mamba/blob/main/dev/environment-dev-extra.yml +channels: + - conda-forge +dependencies: + # Compiler cache + - ccache + # C++ LSP tools and related packages + - clang-tools + - clang + - clangxx + - lld + - cmake-format + # Python LSP support + - ruff + - python-lsp-server-base + - ruff-lsp + # Interactive Python tools + - jupyterlab + - ipython + # Python Debugging + - ipdb + # Github CLI tool + - gh + # Taskfile for running scripts + - go-task diff --git a/test-data/environments/variables.environment.yaml b/test-data/environments/variables.environment.yaml new file mode 100644 index 000000000..5041db506 --- /dev/null +++ b/test-data/environments/variables.environment.yaml @@ -0,0 +1,7 @@ +name: test +channels: + - conda-forge +dependencies: + - numpy +variables: + MY_ENV_VAR: "My Value"