From 1ff98c312134294f15e402d63bff19ae080f3c8b Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Wed, 21 Aug 2024 13:22:51 -0400 Subject: [PATCH 1/7] export conda explicit specification file from project --- docs/reference/cli.md | 12 + src/cli/project/export/conda_explicit_spec.rs | 220 ++++++++++++++++++ src/cli/project/export/mod.rs | 31 +++ src/cli/project/mod.rs | 3 + 4 files changed, 266 insertions(+) create mode 100644 src/cli/project/export/conda_explicit_spec.rs create mode 100644 src/cli/project/export/mod.rs diff --git a/docs/reference/cli.md b/docs/reference/cli.md index beebb5690..7f195db4e 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1178,6 +1178,18 @@ List the environments in the manifest file. pixi project environment list ``` +### `project export conda_explicit_spec` + +Render a platform-specific conda [explicit specification file](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#building-identical-conda-environments) +for an environment. The file can be then used to create a conda environment using conda/mamba: + +```shell +mamba create --name --file +``` + +As the explicit specification file format does not support pypi-dependencies, use either the `--ignore-pypi-errors` option to ignore those dependencies or +`--write-pypi-requirements` to write a `requirements.txt` file that can then be used to install the packages into a conda env using `pip` or `uv`. + ### `project platform add` Adds a platform(s) to the manifest file and updates the lock file. diff --git a/src/cli/project/export/conda_explicit_spec.rs b/src/cli/project/export/conda_explicit_spec.rs new file mode 100644 index 000000000..238547ae1 --- /dev/null +++ b/src/cli/project/export/conda_explicit_spec.rs @@ -0,0 +1,220 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use clap::Parser; + +use crate::cli::cli_config::PrefixUpdateConfig; +use crate::cli::LockFileUsageArgs; +use crate::lock_file::UpdateLockFileOptions; +use crate::Project; +use rattler_conda_types::{ExplicitEnvironmentEntry, ExplicitEnvironmentSpec, Platform}; +use rattler_lock::{CondaPackage, Package, PackageHashes, PypiPackage, PypiPackageData, UrlOrPath}; + +#[derive(Debug, Parser)] +#[clap(arg_required_else_help = false)] +pub struct Args { + /// Environment to render + #[arg(short, long)] + environment: Option, + + /// The platform to render. Defaults to the current platform. + #[arg(long)] + pub platform: Option, + + /// PyPI dependencies are not supported in the conda spec file. + /// This flag allows creating the spec file even if PyPI dependencies are present. + /// Alternatively see --write-pypi-requirements + #[arg(long, default_value = "false")] + ignore_pypi_errors: bool, + + /// Write a requirements file containing all pypi dependencies + #[arg(long, default_value = "false", conflicts_with = "ignore_pypi_errors")] + write_pypi_requirements: bool, + + #[clap(flatten)] + pub lock_file_usage: LockFileUsageArgs, + + #[clap(flatten)] + pub prefix_update_config: PrefixUpdateConfig, +} + +fn cwd() -> PathBuf { + std::env::current_dir().expect("failed to obtain current working directory") +} + +fn build_explicit_spec<'a>( + platform: Platform, + conda_packages: impl IntoIterator, +) -> miette::Result { + let mut packages = Vec::new(); + + for cp in conda_packages { + let prec = cp.package_record(); + let mut url = cp.url().to_owned(); + let hash = prec.md5.ok_or(miette::miette!( + "Package {} does not contain an md5 hash", + prec.name.as_normalized() + ))?; + + url.set_fragment(Some(&format!("{:x}", hash))); + + packages.push(ExplicitEnvironmentEntry { + url: url.to_owned(), + }); + } + + Ok(ExplicitEnvironmentSpec { + platform: Some(platform), + packages, + }) +} + +fn write_explicit_spec( + target: impl AsRef, + exp_env_spec: &ExplicitEnvironmentSpec, +) -> miette::Result<()> { + let mut environment = String::new(); + environment.push_str("# Generated by `pixi project export`\n"); + environment.push_str(exp_env_spec.to_spec_string().as_str()); + + fs::write(target, environment) + .map_err(|e| miette::miette!("Could not write environment file: {}", e))?; + + Ok(()) +} + +fn get_pypi_hash_str(package_data: &PypiPackageData) -> Option { + if let Some(hashes) = &package_data.hash { + let h = match hashes { + PackageHashes::Sha256(h) => format!("--hash=sha256:{:x}", h).to_string(), + PackageHashes::Md5Sha256(_, h) => format!("--hash=sha256:{:x}", h).to_string(), + PackageHashes::Md5(h) => format!("--hash=md5:{:x}", h).to_string(), + }; + Some(h) + } else { + None + } +} + +fn write_pypi_requirements( + target: impl AsRef, + packages: &[PypiPackage], +) -> miette::Result<()> { + let mut reqs = String::new(); + + for p in packages { + // pip --verify-hashes does not accept hashes for local files + let (s, include_hash) = match p.url() { + UrlOrPath::Url(url) => (url.as_str(), true), + UrlOrPath::Path(path) => ( + path.as_os_str() + .to_str() + .unwrap_or_else(|| panic!("Could not convert {:?} to str", path)), + false, + ), + }; + + // remove "direct+ since not valid for pip urls" + let s = s.trim_start_matches("direct+"); + + let hash = match (include_hash, get_pypi_hash_str(p.data().package)) { + (true, Some(h)) => format!(" {}", h), + (false, _) => "".to_string(), + (_, None) => "".to_string(), + }; + + if p.is_editable() { + reqs.push_str(&format!("-e {}{}\n", s, hash)); + } else { + reqs.push_str(&format!("{}{}\n", s, hash)); + } + } + + fs::write(target, reqs) + .map_err(|e| miette::miette!("Could not write requirements file: {}", e))?; + + Ok(()) +} + +pub async fn execute(project: Project, args: Args) -> miette::Result<()> { + let environment = project.environment_from_name_or_env_var(args.environment)?; + // Load the platform + let platform = args.platform.unwrap_or_else(|| environment.best_platform()); + + let lock_file = project + .update_lock_file(UpdateLockFileOptions { + lock_file_usage: args.prefix_update_config.lock_file_usage(), + no_install: args.prefix_update_config.no_install, + ..UpdateLockFileOptions::default() + }) + .await? + .lock_file; + + let env = lock_file + .environment(environment.name().as_str()) + .ok_or(miette::miette!( + "unknown environment '{}' in {}", + environment.name(), + project + .manifest_path() + .to_str() + .expect("expected to have a manifest_path") + ))?; + + let packages = env.packages(platform).ok_or(miette::miette!( + "platform '{platform}' not found in {}", + project + .manifest_path() + .to_str() + .expect("expected to have a manifest_path"), + ))?; + + let mut conda_packages_from_lockfile: Vec = Vec::new(); + let mut pypi_packags_from_lockfile: Vec = Vec::new(); + + for package in packages { + match package { + Package::Conda(p) => conda_packages_from_lockfile.push(p), + Package::Pypi(pyp) => { + if args.ignore_pypi_errors { + tracing::warn!("ignoring PyPI package since PyPI packages are not supported"); + } else if args.write_pypi_requirements { + pypi_packags_from_lockfile.push(pyp); + } else { + miette::bail!( + "PyPI packages are not supported. Specify `--ignore-pypi-errors` to ignore this error\ + or `--write-pypi-requirements` to write pypi requirements to a separate requirements.txt file" + ); + } + } + } + } + + let ees = build_explicit_spec(platform, &conda_packages_from_lockfile)?; + + tracing::info!("Creating conda lock file"); + let target = cwd() + .join(format!( + "conda-{}-{}.lock", + platform, + environment.name().as_str() + )) + .into_os_string(); + + write_explicit_spec(target, &ees)?; + + if args.write_pypi_requirements { + tracing::info!("Creating conda lock file"); + let pypi_target = cwd() + .join(format!( + "requirements-{}-{}.txt", + platform, + environment.name().as_str() + )) + .into_os_string(); + + write_pypi_requirements(pypi_target, &pypi_packags_from_lockfile)?; + } + + Ok(()) +} diff --git a/src/cli/project/export/mod.rs b/src/cli/project/export/mod.rs new file mode 100644 index 000000000..8c135f2a1 --- /dev/null +++ b/src/cli/project/export/mod.rs @@ -0,0 +1,31 @@ +use std::path::PathBuf; +pub mod conda_explicit_spec; + +use crate::Project; +use clap::Parser; + +/// Commands to export projects to other formats +#[derive(Parser, Debug)] +pub struct Args { + /// The path to 'pixi.toml' or 'pyproject.toml' + #[clap(long, global = true)] + pub manifest_path: Option, + + #[clap(subcommand)] + pub command: Command, +} + +#[derive(Parser, Debug)] +pub enum Command { + /// Export project environment to a conda explicit specification file + #[clap(visible_alias = "ces")] + CondaExplicitSpec(conda_explicit_spec::Args), +} + +pub async fn execute(args: Args) -> miette::Result<()> { + let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; + match args.command { + Command::CondaExplicitSpec(args) => conda_explicit_spec::execute(project, args).await?, + }; + Ok(()) +} diff --git a/src/cli/project/mod.rs b/src/cli/project/mod.rs index cca40223e..26dcd4e2e 100644 --- a/src/cli/project/mod.rs +++ b/src/cli/project/mod.rs @@ -3,6 +3,7 @@ use clap::Parser; pub mod channel; pub mod description; +pub mod export; pub mod environment; pub mod platform; pub mod version; @@ -14,6 +15,7 @@ pub enum Command { Platform(platform::Args), Version(version::Args), Environment(environment::Args), + Export(export::Args), } /// Modify the project configuration file through the command line. @@ -33,6 +35,7 @@ pub async fn execute(cmd: Args) -> miette::Result<()> { Command::Platform(args) => platform::execute(args).await?, Command::Version(args) => version::execute(args).await?, Command::Environment(args) => environment::execute(args).await?, + Command::Export(cmd) => export::execute(cmd).await?, }; Ok(()) } From 5a26aedb49e44530eff847d3cd63fa993e809d18 Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Wed, 21 Aug 2024 13:55:42 -0400 Subject: [PATCH 2/7] fix formatting errors --- docs/reference/cli.md | 4 ++-- src/cli/project/export/conda_explicit_spec.rs | 6 +++--- src/cli/project/mod.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 7f195db4e..71e0198a6 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1187,8 +1187,8 @@ for an environment. The file can be then used to create a conda environment usin mamba create --name --file ``` -As the explicit specification file format does not support pypi-dependencies, use either the `--ignore-pypi-errors` option to ignore those dependencies or -`--write-pypi-requirements` to write a `requirements.txt` file that can then be used to install the packages into a conda env using `pip` or `uv`. +As the explicit specification file format does not support pypi-dependencies, use either the `--ignore-pypi-errors` option to ignore those dependencies +or `--write-pypi-requirements` to write a `requirements.txt` file that can then be used to install the packages into a conda env using `pip` or `uv`. ### `project platform add` diff --git a/src/cli/project/export/conda_explicit_spec.rs b/src/cli/project/export/conda_explicit_spec.rs index 238547ae1..48b5fd6ba 100644 --- a/src/cli/project/export/conda_explicit_spec.rs +++ b/src/cli/project/export/conda_explicit_spec.rs @@ -170,7 +170,7 @@ pub async fn execute(project: Project, args: Args) -> miette::Result<()> { ))?; let mut conda_packages_from_lockfile: Vec = Vec::new(); - let mut pypi_packags_from_lockfile: Vec = Vec::new(); + let mut pypi_packages_from_lockfile: Vec = Vec::new(); for package in packages { match package { @@ -179,7 +179,7 @@ pub async fn execute(project: Project, args: Args) -> miette::Result<()> { if args.ignore_pypi_errors { tracing::warn!("ignoring PyPI package since PyPI packages are not supported"); } else if args.write_pypi_requirements { - pypi_packags_from_lockfile.push(pyp); + pypi_packages_from_lockfile.push(pyp); } else { miette::bail!( "PyPI packages are not supported. Specify `--ignore-pypi-errors` to ignore this error\ @@ -213,7 +213,7 @@ pub async fn execute(project: Project, args: Args) -> miette::Result<()> { )) .into_os_string(); - write_pypi_requirements(pypi_target, &pypi_packags_from_lockfile)?; + write_pypi_requirements(pypi_target, &pypi_packages_from_lockfile)?; } Ok(()) diff --git a/src/cli/project/mod.rs b/src/cli/project/mod.rs index 26dcd4e2e..99d098dc4 100644 --- a/src/cli/project/mod.rs +++ b/src/cli/project/mod.rs @@ -3,8 +3,8 @@ use clap::Parser; pub mod channel; pub mod description; -pub mod export; pub mod environment; +pub mod export; pub mod platform; pub mod version; From 63479bb97d3a66ed5e17056368437b684c8c0acc Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Sat, 24 Aug 2024 21:18:05 -0400 Subject: [PATCH 3/7] refactor export conda explicit to dump multiple spec files --- src/cli/project/export/conda_explicit_spec.rs | 185 +++++++++++------- 1 file changed, 117 insertions(+), 68 deletions(-) diff --git a/src/cli/project/export/conda_explicit_spec.rs b/src/cli/project/export/conda_explicit_spec.rs index 48b5fd6ba..ddd677be9 100644 --- a/src/cli/project/export/conda_explicit_spec.rs +++ b/src/cli/project/export/conda_explicit_spec.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::fs; use std::path::{Path, PathBuf}; @@ -8,28 +9,34 @@ use crate::cli::LockFileUsageArgs; use crate::lock_file::UpdateLockFileOptions; use crate::Project; use rattler_conda_types::{ExplicitEnvironmentEntry, ExplicitEnvironmentSpec, Platform}; -use rattler_lock::{CondaPackage, Package, PackageHashes, PypiPackage, PypiPackageData, UrlOrPath}; +use rattler_lock::{ + CondaPackage, Environment, Package, PackageHashes, PypiPackage, PypiPackageData, UrlOrPath, +}; #[derive(Debug, Parser)] #[clap(arg_required_else_help = false)] pub struct Args { - /// Environment to render + /// Output directory for rendered explicit environment spec files + pub output_dir: PathBuf, + + /// Environment to render. Can be repeated for multiple envs. Defaults to all environments #[arg(short, long)] - environment: Option, + pub environment: Option>, - /// The platform to render. Defaults to the current platform. - #[arg(long)] - pub platform: Option, + /// The platform to render. Can be repeated for multiple platforms. + /// Defaults to all platforms available for selected environments. + #[arg(short, long)] + pub platform: Option>, - /// PyPI dependencies are not supported in the conda spec file. + /// PyPI dependencies are not supported in the conda explicit spec file. /// This flag allows creating the spec file even if PyPI dependencies are present. /// Alternatively see --write-pypi-requirements #[arg(long, default_value = "false")] - ignore_pypi_errors: bool, + pub ignore_pypi_errors: bool, /// Write a requirements file containing all pypi dependencies #[arg(long, default_value = "false", conflicts_with = "ignore_pypi_errors")] - write_pypi_requirements: bool, + pub write_pypi_requirements: bool, #[clap(flatten)] pub lock_file_usage: LockFileUsageArgs, @@ -38,12 +45,8 @@ pub struct Args { pub prefix_update_config: PrefixUpdateConfig, } -fn cwd() -> PathBuf { - std::env::current_dir().expect("failed to obtain current working directory") -} - fn build_explicit_spec<'a>( - platform: Platform, + platform: &Platform, conda_packages: impl IntoIterator, ) -> miette::Result { let mut packages = Vec::new(); @@ -64,15 +67,19 @@ fn build_explicit_spec<'a>( } Ok(ExplicitEnvironmentSpec { - platform: Some(platform), + platform: Some(*platform), packages, }) } -fn write_explicit_spec( +fn render_explicit_spec( target: impl AsRef, exp_env_spec: &ExplicitEnvironmentSpec, ) -> miette::Result<()> { + if exp_env_spec.packages.is_empty() { + return Ok(()); + } + let mut environment = String::new(); environment.push_str("# Generated by `pixi project export`\n"); environment.push_str(exp_env_spec.to_spec_string().as_str()); @@ -96,10 +103,14 @@ fn get_pypi_hash_str(package_data: &PypiPackageData) -> Option { } } -fn write_pypi_requirements( +fn render_pypi_requirements( target: impl AsRef, packages: &[PypiPackage], ) -> miette::Result<()> { + if packages.is_empty() { + return Ok(()); + } + let mut reqs = String::new(); for p in packages { @@ -136,37 +147,17 @@ fn write_pypi_requirements( Ok(()) } -pub async fn execute(project: Project, args: Args) -> miette::Result<()> { - let environment = project.environment_from_name_or_env_var(args.environment)?; - // Load the platform - let platform = args.platform.unwrap_or_else(|| environment.best_platform()); - - let lock_file = project - .update_lock_file(UpdateLockFileOptions { - lock_file_usage: args.prefix_update_config.lock_file_usage(), - no_install: args.prefix_update_config.no_install, - ..UpdateLockFileOptions::default() - }) - .await? - .lock_file; - - let env = lock_file - .environment(environment.name().as_str()) - .ok_or(miette::miette!( - "unknown environment '{}' in {}", - environment.name(), - project - .manifest_path() - .to_str() - .expect("expected to have a manifest_path") - ))?; - - let packages = env.packages(platform).ok_or(miette::miette!( - "platform '{platform}' not found in {}", - project - .manifest_path() - .to_str() - .expect("expected to have a manifest_path"), +fn render_env_platform( + output_dir: &Path, + env_name: &str, + env: &Environment, + platform: &Platform, + ignore_pypi_errors: bool, + write_pypi_requirements: bool, +) -> miette::Result<()> { + let packages = env.packages(*platform).ok_or(miette::miette!( + "platform '{platform}' not found for env {}", + env_name, ))?; let mut conda_packages_from_lockfile: Vec = Vec::new(); @@ -176,13 +167,13 @@ pub async fn execute(project: Project, args: Args) -> miette::Result<()> { match package { Package::Conda(p) => conda_packages_from_lockfile.push(p), Package::Pypi(pyp) => { - if args.ignore_pypi_errors { + if ignore_pypi_errors { tracing::warn!("ignoring PyPI package since PyPI packages are not supported"); - } else if args.write_pypi_requirements { + } else if write_pypi_requirements { pypi_packages_from_lockfile.push(pyp); } else { miette::bail!( - "PyPI packages are not supported. Specify `--ignore-pypi-errors` to ignore this error\ + "PyPI packages are not supported. Specify `--ignore-pypi-errors` to ignore this error \ or `--write-pypi-requirements` to write pypi requirements to a separate requirements.txt file" ); } @@ -192,28 +183,86 @@ pub async fn execute(project: Project, args: Args) -> miette::Result<()> { let ees = build_explicit_spec(platform, &conda_packages_from_lockfile)?; - tracing::info!("Creating conda lock file"); - let target = cwd() - .join(format!( - "conda-{}-{}.lock", - platform, - environment.name().as_str() - )) + tracing::info!("Creating conda explicit spec for env: {env_name} platform: {platform}"); + let target = output_dir + .join(format!("{}-{}-conda_spec.txt", env_name, platform)) .into_os_string(); - write_explicit_spec(target, &ees)?; + render_explicit_spec(target, &ees)?; - if args.write_pypi_requirements { - tracing::info!("Creating conda lock file"); - let pypi_target = cwd() - .join(format!( - "requirements-{}-{}.txt", - platform, - environment.name().as_str() - )) + if write_pypi_requirements { + tracing::info!("Creating pypi requirements file for env: {env_name} platform: {platform}"); + let pypi_target = output_dir + .join(format!("{}-{}-requirements.txt", env_name, platform)) .into_os_string(); - write_pypi_requirements(pypi_target, &pypi_packages_from_lockfile)?; + render_pypi_requirements(pypi_target, &pypi_packages_from_lockfile)?; + } + + Ok(()) +} + +pub async fn execute(project: Project, args: Args) -> miette::Result<()> { + let lockfile = project + .update_lock_file(UpdateLockFileOptions { + lock_file_usage: args.prefix_update_config.lock_file_usage(), + no_install: args.prefix_update_config.no_install, + ..UpdateLockFileOptions::default() + }) + .await? + .lock_file; + + let mut environments = Vec::new(); + if let Some(env_names) = args.environment { + for env_name in &env_names { + environments.push(( + env_name.to_string(), + lockfile + .environment(env_name) + .ok_or(miette::miette!("unknown environment {}", env_name))?, + )); + } + } else { + for (env_name, env) in lockfile.environments() { + environments.push((env_name.to_string(), env)); + } + }; + + let mut env_platform = Vec::new(); + + for (env_name, env) in environments { + let available_platforms: HashSet = HashSet::from_iter(env.platforms()); + + if let Some(ref platforms) = args.platform { + for plat in platforms { + if available_platforms.contains(plat) { + env_platform.push((env_name.clone(), env.clone(), *plat)); + } else { + tracing::warn!( + "Platform {} not available for environment {}. Skipping...", + plat, + env_name, + ); + } + } + } else { + for plat in available_platforms { + env_platform.push((env_name.clone(), env.clone(), plat)); + } + } + } + + fs::create_dir_all(&args.output_dir).ok(); + + for (env_name, env, plat) in env_platform { + render_env_platform( + &args.output_dir, + &env_name, + &env, + &plat, + args.ignore_pypi_errors, + args.write_pypi_requirements, + )?; } Ok(()) From c1a4e30fe19057a1a1d2d052a5fb15c39c4b6993 Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Sat, 24 Aug 2024 21:36:45 -0400 Subject: [PATCH 4/7] update export conda_explicit_spec cli docs --- docs/reference/cli.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 71e0198a6..d66c12fd7 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1190,6 +1190,23 @@ mamba create --name --file As the explicit specification file format does not support pypi-dependencies, use either the `--ignore-pypi-errors` option to ignore those dependencies or `--write-pypi-requirements` to write a `requirements.txt` file that can then be used to install the packages into a conda env using `pip` or `uv`. +##### Arguments + +1. ``: Output directory for rendered explicit environment spec files. + +##### Options + +- `--environment (-e)`: Environment to render. Can be repeated for multiple envs. Defaults to all environments. +- `--platform (-p)`: The platform to render. Can be repeated for multiple platforms. Defaults to all platforms available for selected environments. +- `--ignore-pypi-errors`: PyPI dependencies are not supported in the conda explicit spec file. This flag allows creating the spec file even if PyPI dependencies are present. Alternatively see `--write-pypi-requirements`. +- `--write-pypi-requirements`: Write a requirements file containing all pypi dependencies. + +```sh +pixi project export conda_explicit_spec output +pixi project export conda_explicit_spec --write-pypi-requirements -e default -e test -p linux-64 output +``` + + ### `project platform add` Adds a platform(s) to the manifest file and updates the lock file. From 00007d4f9b7c6722343d695038f66a0ebfb59638 Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Thu, 5 Sep 2024 15:59:11 -0400 Subject: [PATCH 5/7] Remove pypi functionality from export conda explicit spec Additionally added snapshot testing --- docs/reference/cli.md | 8 +- src/cli/project/export/conda_explicit_spec.rs | 125 +- ..._conda_explicit_spec_default_linux-64.snap | 46 + ...er_conda_explicit_spec_default_osx-64.snap | 23 + ...der_conda_explicit_spec_test_linux-64.snap | 53 + ...ender_conda_explicit_spec_test_osx-64.snap | 30 + .../export/test-data/testenv/pixi.lock | 1268 +++++++++++++++++ .../export/test-data/testenv/pixi.toml | 27 + 8 files changed, 1494 insertions(+), 86 deletions(-) create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_linux-64.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_osx-64.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_linux-64.snap create mode 100644 src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_osx-64.snap create mode 100644 src/cli/project/export/test-data/testenv/pixi.lock create mode 100644 src/cli/project/export/test-data/testenv/pixi.toml diff --git a/docs/reference/cli.md b/docs/reference/cli.md index d66c12fd7..bc5a8ee51 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1187,8 +1187,7 @@ for an environment. The file can be then used to create a conda environment usin mamba create --name --file ``` -As the explicit specification file format does not support pypi-dependencies, use either the `--ignore-pypi-errors` option to ignore those dependencies -or `--write-pypi-requirements` to write a `requirements.txt` file that can then be used to install the packages into a conda env using `pip` or `uv`. +As the explicit specification file format does not support pypi-dependencies, use the `--ignore-pypi-errors` option to ignore those dependencies. ##### Arguments @@ -1198,12 +1197,11 @@ or `--write-pypi-requirements` to write a `requirements.txt` file that can then - `--environment (-e)`: Environment to render. Can be repeated for multiple envs. Defaults to all environments. - `--platform (-p)`: The platform to render. Can be repeated for multiple platforms. Defaults to all platforms available for selected environments. -- `--ignore-pypi-errors`: PyPI dependencies are not supported in the conda explicit spec file. This flag allows creating the spec file even if PyPI dependencies are present. Alternatively see `--write-pypi-requirements`. -- `--write-pypi-requirements`: Write a requirements file containing all pypi dependencies. +- `--ignore-pypi-errors`: PyPI dependencies are not supported in the conda explicit spec file. This flag allows creating the spec file even if PyPI dependencies are present. ```sh pixi project export conda_explicit_spec output -pixi project export conda_explicit_spec --write-pypi-requirements -e default -e test -p linux-64 output +pixi project export conda_explicit_spec -e default -e test -p linux-64 output ``` diff --git a/src/cli/project/export/conda_explicit_spec.rs b/src/cli/project/export/conda_explicit_spec.rs index ddd677be9..1d31b4ed7 100644 --- a/src/cli/project/export/conda_explicit_spec.rs +++ b/src/cli/project/export/conda_explicit_spec.rs @@ -9,9 +9,7 @@ use crate::cli::LockFileUsageArgs; use crate::lock_file::UpdateLockFileOptions; use crate::Project; use rattler_conda_types::{ExplicitEnvironmentEntry, ExplicitEnvironmentSpec, Platform}; -use rattler_lock::{ - CondaPackage, Environment, Package, PackageHashes, PypiPackage, PypiPackageData, UrlOrPath, -}; +use rattler_lock::{CondaPackage, Environment, Package}; #[derive(Debug, Parser)] #[clap(arg_required_else_help = false)] @@ -30,14 +28,9 @@ pub struct Args { /// PyPI dependencies are not supported in the conda explicit spec file. /// This flag allows creating the spec file even if PyPI dependencies are present. - /// Alternatively see --write-pypi-requirements #[arg(long, default_value = "false")] pub ignore_pypi_errors: bool, - /// Write a requirements file containing all pypi dependencies - #[arg(long, default_value = "false", conflicts_with = "ignore_pypi_errors")] - pub write_pypi_requirements: bool, - #[clap(flatten)] pub lock_file_usage: LockFileUsageArgs, @@ -90,70 +83,12 @@ fn render_explicit_spec( Ok(()) } -fn get_pypi_hash_str(package_data: &PypiPackageData) -> Option { - if let Some(hashes) = &package_data.hash { - let h = match hashes { - PackageHashes::Sha256(h) => format!("--hash=sha256:{:x}", h).to_string(), - PackageHashes::Md5Sha256(_, h) => format!("--hash=sha256:{:x}", h).to_string(), - PackageHashes::Md5(h) => format!("--hash=md5:{:x}", h).to_string(), - }; - Some(h) - } else { - None - } -} - -fn render_pypi_requirements( - target: impl AsRef, - packages: &[PypiPackage], -) -> miette::Result<()> { - if packages.is_empty() { - return Ok(()); - } - - let mut reqs = String::new(); - - for p in packages { - // pip --verify-hashes does not accept hashes for local files - let (s, include_hash) = match p.url() { - UrlOrPath::Url(url) => (url.as_str(), true), - UrlOrPath::Path(path) => ( - path.as_os_str() - .to_str() - .unwrap_or_else(|| panic!("Could not convert {:?} to str", path)), - false, - ), - }; - - // remove "direct+ since not valid for pip urls" - let s = s.trim_start_matches("direct+"); - - let hash = match (include_hash, get_pypi_hash_str(p.data().package)) { - (true, Some(h)) => format!(" {}", h), - (false, _) => "".to_string(), - (_, None) => "".to_string(), - }; - - if p.is_editable() { - reqs.push_str(&format!("-e {}{}\n", s, hash)); - } else { - reqs.push_str(&format!("{}{}\n", s, hash)); - } - } - - fs::write(target, reqs) - .map_err(|e| miette::miette!("Could not write requirements file: {}", e))?; - - Ok(()) -} - fn render_env_platform( output_dir: &Path, env_name: &str, env: &Environment, platform: &Platform, ignore_pypi_errors: bool, - write_pypi_requirements: bool, ) -> miette::Result<()> { let packages = env.packages(*platform).ok_or(miette::miette!( "platform '{platform}' not found for env {}", @@ -161,16 +96,16 @@ fn render_env_platform( ))?; let mut conda_packages_from_lockfile: Vec = Vec::new(); - let mut pypi_packages_from_lockfile: Vec = Vec::new(); for package in packages { match package { Package::Conda(p) => conda_packages_from_lockfile.push(p), Package::Pypi(pyp) => { if ignore_pypi_errors { - tracing::warn!("ignoring PyPI package since PyPI packages are not supported"); - } else if write_pypi_requirements { - pypi_packages_from_lockfile.push(pyp); + tracing::warn!( + "ignoring PyPI package {} since PyPI packages are not supported", + pyp.data().package.name + ); } else { miette::bail!( "PyPI packages are not supported. Specify `--ignore-pypi-errors` to ignore this error \ @@ -185,20 +120,11 @@ fn render_env_platform( tracing::info!("Creating conda explicit spec for env: {env_name} platform: {platform}"); let target = output_dir - .join(format!("{}-{}-conda_spec.txt", env_name, platform)) + .join(format!("{}_{}_conda_spec.txt", env_name, platform)) .into_os_string(); render_explicit_spec(target, &ees)?; - if write_pypi_requirements { - tracing::info!("Creating pypi requirements file for env: {env_name} platform: {platform}"); - let pypi_target = output_dir - .join(format!("{}-{}-requirements.txt", env_name, platform)) - .into_os_string(); - - render_pypi_requirements(pypi_target, &pypi_packages_from_lockfile)?; - } - Ok(()) } @@ -261,9 +187,46 @@ pub async fn execute(project: Project, args: Args) -> miette::Result<()> { &env, &plat, args.ignore_pypi_errors, - args.write_pypi_requirements, )?; } Ok(()) } + +#[cfg(test)] +mod tests { + use std::path::Path; + + use super::*; + use rattler_lock::LockFile; + use tempfile::tempdir; + + #[test] + fn test_render_conda_explicit_spec() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("src/cli/project/export/test-data/testenv/pixi.lock"); + let lockfile = LockFile::from_path(&path).unwrap(); + + let output_dir = tempdir().unwrap(); + + for (env_name, env) in lockfile.environments() { + for platform in env.platforms() { + // example contains pypi dependencies so should fail if `ignore_pypi_errors` is + // false. + assert!( + render_env_platform(output_dir.path(), env_name, &env, &platform, false) + .is_err() + ); + render_env_platform(output_dir.path(), env_name, &env, &platform, true).unwrap(); + + let file_path = output_dir + .path() + .join(format!("{}_{}_conda_spec.txt", env_name, platform)); + insta::assert_snapshot!( + format!("test_render_conda_explicit_spec_{}_{}", env_name, platform), + fs::read_to_string(file_path).unwrap() + ); + } + } + } +} diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_linux-64.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_linux-64.snap new file mode 100644 index 000000000..d9185f076 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_linux-64.snap @@ -0,0 +1,46 @@ +--- +source: src/cli/project/export/conda_explicit_spec.rs +expression: "fs::read_to_string(file_path).unwrap()" +--- +# Generated by `pixi project export` +# platform: linux-64 +@EXPLICIT +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda#b0b867af6fc74b2a0aa206da29c0f3cf +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda#c27d1c142233b5bc9ca570c6e2e0c244 +https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 +https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h06ac9bb_1.conda#db9bdbaee0f524ead0471689f002781e +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a +https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097 +https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71 +https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4 +https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda#e7ba12deb7020dd080c6c70e7b6f6a3d +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89 +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088 +https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025 +https://conda.anaconda.org/conda-forge/linux-64/python-3.12.5-h2ad013b_0_cpython.conda#9c56c4df45f6571b13111d8df2448692 +https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda#0424ae29b104430108f5218a66db7260 +https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc +https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8 +https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda#8b7069e9792ee4e5b4919a7a306d2e67 +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45 diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_osx-64.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_osx-64.snap new file mode 100644 index 000000000..891d98a1c --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_default_osx-64.snap @@ -0,0 +1,23 @@ +--- +source: src/cli/project/export/conda_explicit_spec.rs +expression: "fs::read_to_string(file_path).unwrap()" +--- +# Generated by `pixi project export` +# platform: osx-64 +@EXPLICIT +https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda#7ed4301d437b59045be7e051a0308211 +https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda#b7e5424e7f06547a903d28e4651dbb21 +https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.2-h73e2aa4_0.conda#3d1d51c8f716d97c864d12f7af329526 +https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2#ccb34fb14960ad8b125962d3d79b31a9 +https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda#84de0078b58f899fc164303b0603ff0e +https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda#b7575b5aa92108dcc9aaab0f05f2dbce +https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda#e102bbf8a6ceeaf429deab8032fc8977 +https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda#2ff47134c8e292868a4609519b1ea3b6 +https://conda.anaconda.org/conda-forge/osx-64/python-3.12.5-h37a9e06_0_cpython.conda#517cb4e16466f8d96ba2a72897d14c48 +https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda#c34dd4920e0addf7cfcc725809f25d8e +https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hb553811_1.conda#66514594817d51c78db7109a23ad322f +https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda#f17f77f2acf4d344734bda76829ce14e +https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda#bf830ba5afc507c6232d4ef0fb1a882d +https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd +https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2#a72f9d4ea13d55d745ff1ed594747f10 +https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h0d85af4_2.tar.bz2#d7e08fcf8259d742156188e8762b4d20 diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_linux-64.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_linux-64.snap new file mode 100644 index 000000000..31dd3cf3e --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_linux-64.snap @@ -0,0 +1,53 @@ +--- +source: src/cli/project/export/conda_explicit_spec.rs +expression: "fs::read_to_string(file_path).unwrap()" +--- +# Generated by `pixi project export` +# platform: linux-64 +@EXPLICIT +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda#b0b867af6fc74b2a0aa206da29c0f3cf +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda#c27d1c142233b5bc9ca570c6e2e0c244 +https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 +https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h06ac9bb_1.conda#db9bdbaee0f524ead0471689f002781e +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0 +https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097 +https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71 +https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4 +https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda#99e164522f6bdf23c177c8d9ae63f975 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda#b80f2f396ca2c28b8c14c437a4ed1e74 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda#e7ba12deb7020dd080c6c70e7b6f6a3d +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89 +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda#57d7dc60e9325e3de37ff8dffd18e814 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e +https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088 +https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda#e010a224b90f1f623a917c35addbb924 +https://conda.anaconda.org/conda-forge/linux-64/python-3.12.5-h2ad013b_0_cpython.conda#9c56c4df45f6571b13111d8df2448692 +https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda#0424ae29b104430108f5218a66db7260 +https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc +https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda#e804c43f58255e977093a2298e442bb8 +https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda#8b7069e9792ee4e5b4919a7a306d2e67 +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45 diff --git a/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_osx-64.snap b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_osx-64.snap new file mode 100644 index 000000000..c638cef69 --- /dev/null +++ b/src/cli/project/export/snapshots/pixi__cli__project__export__conda_explicit_spec__tests__test_render_conda_explicit_spec_test_osx-64.snap @@ -0,0 +1,30 @@ +--- +source: src/cli/project/export/conda_explicit_spec.rs +expression: "fs::read_to_string(file_path).unwrap()" +--- +# Generated by `pixi project export` +# platform: osx-64 +@EXPLICIT +https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda#7ed4301d437b59045be7e051a0308211 +https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda#b7e5424e7f06547a903d28e4651dbb21 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 +https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.2-h73e2aa4_0.conda#3d1d51c8f716d97c864d12f7af329526 +https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2#ccb34fb14960ad8b125962d3d79b31a9 +https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda#84de0078b58f899fc164303b0603ff0e +https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda#b7575b5aa92108dcc9aaab0f05f2dbce +https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda#e102bbf8a6ceeaf429deab8032fc8977 +https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda#2ff47134c8e292868a4609519b1ea3b6 +https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf +https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda#e010a224b90f1f623a917c35addbb924 +https://conda.anaconda.org/conda-forge/osx-64/python-3.12.5-h37a9e06_0_cpython.conda#517cb4e16466f8d96ba2a72897d14c48 +https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda#c34dd4920e0addf7cfcc725809f25d8e +https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hb553811_1.conda#66514594817d51c78db7109a23ad322f +https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda#f17f77f2acf4d344734bda76829ce14e +https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda#bf830ba5afc507c6232d4ef0fb1a882d +https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda#8bfdead4e0fff0383ae4c9c50d0531bd +https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2#a72f9d4ea13d55d745ff1ed594747f10 +https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h0d85af4_2.tar.bz2#d7e08fcf8259d742156188e8762b4d20 diff --git a/src/cli/project/export/test-data/testenv/pixi.lock b/src/cli/project/export/test-data/testenv/pixi.lock new file mode 100644 index 000000000..ae2f5570f --- /dev/null +++ b/src/cli/project/export/test-data/testenv/pixi.lock @@ -0,0 +1,1268 @@ +version: 5 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h06ac9bb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.5-h2ad013b_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + - pypi: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl + osx-64: + - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.2-h73e2aa4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.5-h37a9e06_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hb553811_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h0d85af4_2.tar.bz2 + - pypi: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl + test: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h06ac9bb_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.5-h2ad013b_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + - pypi: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl + osx-64: + - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.2-h73e2aa4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.5-h37a9e06_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hb553811_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h0d85af4_2.tar.bz2 + - pypi: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl +packages: +- kind: conda + name: _libgcc_mutex + version: '0.1' + build: conda_forge + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + purls: [] + size: 2562 + timestamp: 1578324546067 +- kind: conda + name: _openmp_mutex + version: '4.5' + build: 2_gnu + build_number: 16 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 23621 + timestamp: 1650670423406 +- kind: conda + name: brotli-python + version: 1.1.0 + build: py312h2ec8cdc_2 + build_number: 2 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py312h2ec8cdc_2.conda + sha256: f2a59ccd20b4816dea9a2a5cb917eb69728271dbf1aeab4e1b7e609330a50b6f + md5: b0b867af6fc74b2a0aa206da29c0f3cf + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.1.0 hb9d3cd8_2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/brotli?source=hash-mapping + size: 349867 + timestamp: 1725267732089 +- kind: conda + name: bzip2 + version: 1.0.8 + build: h4bc722e_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + sha256: 5ced96500d945fb286c9c838e54fa759aa04a7129c59800f0846b4335cee770d + md5: 62ee74e96c5ebb0af99386de58cf9553 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 252783 + timestamp: 1720974456583 +- kind: conda + name: bzip2 + version: 1.0.8 + build: hfdf4475_7 + build_number: 7 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda + sha256: cad153608b81fb24fc8c509357daa9ae4e49dfc535b2cb49b91e23dbd68fc3c5 + md5: 7ed4301d437b59045be7e051a0308211 + depends: + - __osx >=10.13 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 134188 + timestamp: 1720974491916 +- kind: conda + name: ca-certificates + version: 2024.8.30 + build: h8857fd0_0 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.8.30-h8857fd0_0.conda + sha256: 593f302d0f44c2c771e1614ee6d56fffdc7d616e6f187669c8b0e34ffce3e1ae + md5: b7e5424e7f06547a903d28e4651dbb21 + license: ISC + purls: [] + size: 158665 + timestamp: 1725019059295 +- kind: conda + name: ca-certificates + version: 2024.8.30 + build: hbcca054_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.8.30-hbcca054_0.conda + sha256: afee721baa6d988e27fef1832f68d6f32ac8cc99cdf6015732224c2841a09cea + md5: c27d1c142233b5bc9ca570c6e2e0c244 + license: ISC + purls: [] + size: 159003 + timestamp: 1725018903918 +- kind: conda + name: certifi + version: 2024.8.30 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda + sha256: 7020770df338c45ac6b560185956c32f0a5abf4b76179c037f115fc7d687819f + md5: 12f7d00853807b0531775e9be891cb11 + depends: + - python >=3.7 + license: ISC + purls: + - pkg:pypi/certifi?source=hash-mapping + size: 163752 + timestamp: 1725278204397 +- kind: conda + name: cffi + version: 1.17.0 + build: py312h06ac9bb_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.0-py312h06ac9bb_1.conda + sha256: 397f588c30dd1a30236d289d8dc7f3c34cd71a498dc66d20450393014594cf4d + md5: db9bdbaee0f524ead0471689f002781e + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - pycparser + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 294242 + timestamp: 1724956485789 +- kind: conda + name: charset-normalizer + version: 3.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda + sha256: 20cae47d31fdd58d99c4d2e65fbdcefa0b0de0c84e455ba9d6356a4bdbc4b5b9 + md5: 7f4a9e3fcff3f6356ae99244a014da6a + depends: + - python >=3.7 + license: MIT + license_family: MIT + purls: + - pkg:pypi/charset-normalizer?source=hash-mapping + size: 46597 + timestamp: 1698833765762 +- kind: conda + name: colorama + version: 0.4.6 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 + sha256: 2c1b2e9755ce3102bca8d69e8f26e4f087ece73f50418186aee7c74bef8e1698 + md5: 3faab06a954c2a04039983f2c4a50d99 + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping + size: 25170 + timestamp: 1666700778190 +- kind: conda + name: exceptiongroup + version: 1.2.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda + sha256: e0edd30c4b7144406bb4da975e6bb97d6bc9c0e999aa4efe66ae108cada5d5b5 + md5: d02ae936e42063ca46af6cdad2dbd1e0 + depends: + - python >=3.7 + license: MIT and PSF-2.0 + purls: + - pkg:pypi/exceptiongroup?source=hash-mapping + size: 20418 + timestamp: 1720869435725 +- kind: conda + name: h2 + version: 4.1.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2 + sha256: bfc6a23849953647f4e255c782e74a0e18fe16f7e25c7bb0bc57b83bb6762c7a + md5: b748fbf7060927a6e82df7cb5ee8f097 + depends: + - hpack >=4.0,<5 + - hyperframe >=6.0,<7 + - python >=3.6.1 + license: MIT + license_family: MIT + purls: + - pkg:pypi/h2?source=hash-mapping + size: 46754 + timestamp: 1634280590080 +- kind: conda + name: hpack + version: 4.0.0 + build: pyh9f0ad1d_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2 + sha256: 5dec948932c4f740674b1afb551223ada0c55103f4c7bf86a110454da3d27cb8 + md5: 914d6646c4dbb1fd3ff539830a12fd71 + depends: + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/hpack?source=hash-mapping + size: 25341 + timestamp: 1598856368685 +- kind: conda + name: hyperframe + version: 6.0.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2 + sha256: e374a9d0f53149328134a8d86f5d72bca4c6dcebed3c0ecfa968c02996289330 + md5: 9f765cbfab6870c8435b9eefecd7a1f4 + depends: + - python >=3.6 + license: MIT + license_family: MIT + purls: + - pkg:pypi/hyperframe?source=hash-mapping + size: 14646 + timestamp: 1619110249723 +- kind: conda + name: idna + version: '3.8' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/idna-3.8-pyhd8ed1ab_0.conda + sha256: 8660d38b272d3713ec8ac5ae918bc3bc80e1b81e1a7d61df554bded71ada6110 + md5: 99e164522f6bdf23c177c8d9ae63f975 + depends: + - python >=3.6 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/idna?source=hash-mapping + size: 49275 + timestamp: 1724450633325 +- kind: conda + name: iniconfig + version: 2.0.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda + sha256: 38740c939b668b36a50ef455b077e8015b8c9cf89860d421b3fff86048f49666 + md5: f800d2da156d08e289b14e87e43c1ae5 + depends: + - python >=3.7 + license: MIT + license_family: MIT + purls: + - pkg:pypi/iniconfig?source=hash-mapping + size: 11101 + timestamp: 1673103208955 +- kind: conda + name: ld_impl_linux-64 + version: '2.40' + build: hf3520f5_7 + build_number: 7 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-hf3520f5_7.conda + sha256: 764b6950aceaaad0c67ef925417594dd14cd2e22fff864aeef455ac259263d15 + md5: b80f2f396ca2c28b8c14c437a4ed1e74 + constrains: + - binutils_impl_linux-64 2.40 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 707602 + timestamp: 1718625640445 +- kind: conda + name: libexpat + version: 2.6.2 + build: h59595ed_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.2-h59595ed_0.conda + sha256: 331bb7c7c05025343ebd79f86ae612b9e1e74d2687b8f3179faec234f986ce19 + md5: e7ba12deb7020dd080c6c70e7b6f6a3d + depends: + - libgcc-ng >=12 + constrains: + - expat 2.6.2.* + license: MIT + license_family: MIT + purls: [] + size: 73730 + timestamp: 1710362120304 +- kind: conda + name: libexpat + version: 2.6.2 + build: h73e2aa4_0 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.2-h73e2aa4_0.conda + sha256: a188a77b275d61159a32ab547f7d17892226e7dac4518d2c6ac3ac8fc8dfde92 + md5: 3d1d51c8f716d97c864d12f7af329526 + constrains: + - expat 2.6.2.* + license: MIT + license_family: MIT + purls: [] + size: 69246 + timestamp: 1710362566073 +- kind: conda + name: libffi + version: 3.4.2 + build: h0d85af4_5 + build_number: 5 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2 + sha256: 7a2d27a936ceee6942ea4d397f9c7d136f12549d86f7617e8b6bad51e01a941f + md5: ccb34fb14960ad8b125962d3d79b31a9 + license: MIT + license_family: MIT + purls: [] + size: 51348 + timestamp: 1636488394370 +- kind: conda + name: libffi + version: 3.4.2 + build: h7f98852_5 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2 + sha256: ab6e9856c21709b7b517e940ae7028ae0737546122f83c2aa5d692860c3b149e + md5: d645c6d2ac96843a2bfaccd2d62b3ac3 + depends: + - libgcc-ng >=9.4.0 + license: MIT + license_family: MIT + purls: [] + size: 58292 + timestamp: 1636488182923 +- kind: conda + name: libgcc + version: 14.1.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda + sha256: 10fa74b69266a2be7b96db881e18fa62cfa03082b65231e8d652e897c4b335a3 + md5: 002ef4463dd1e2b44a94a4ace468f5d2 + depends: + - _libgcc_mutex 0.1 conda_forge + - _openmp_mutex >=4.5 + constrains: + - libgomp 14.1.0 h77fa898_1 + - libgcc-ng ==14.1.0=*_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 846380 + timestamp: 1724801836552 +- kind: conda + name: libgcc-ng + version: 14.1.0 + build: h69a702a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda + sha256: b91f7021e14c3d5c840fbf0dc75370d6e1f7c7ff4482220940eaafb9c64613b7 + md5: 1efc0ad219877a73ef977af7dbb51f17 + depends: + - libgcc 14.1.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 52170 + timestamp: 1724801842101 +- kind: conda + name: libgomp + version: 14.1.0 + build: h77fa898_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda + sha256: c96724c8ae4ee61af7674c5d9e5a3fbcf6cd887a40ad5a52c99aa36f1d4f9680 + md5: 23c255b008c4f2ae008f81edcabaca89 + depends: + - _libgcc_mutex 0.1 conda_forge + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 460218 + timestamp: 1724801743478 +- kind: conda + name: libnsl + version: 2.0.1 + build: hd590300_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 + md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 + depends: + - libgcc-ng >=12 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33408 + timestamp: 1697359010159 +- kind: conda + name: libsqlite + version: 3.46.1 + build: h4b8f8c9_0 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda + sha256: 1d075cb823f0cad7e196871b7c57961d669cbbb6cd0e798bf50cbf520dda65fb + md5: 84de0078b58f899fc164303b0603ff0e + depends: + - __osx >=10.13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + purls: [] + size: 908317 + timestamp: 1725353652135 +- kind: conda + name: libsqlite + version: 3.46.1 + build: hadc24fc_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda + sha256: 9851c049abafed3ee329d6c7c2033407e2fc269d33a75c071110ab52300002b0 + md5: 36f79405ab16bf271edb55b213836dac + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + license: Unlicense + purls: [] + size: 865214 + timestamp: 1725353659783 +- kind: conda + name: libstdcxx + version: 14.1.0 + build: hc0a3c3a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda + sha256: 44decb3d23abacf1c6dd59f3c152a7101b7ca565b4ef8872804ceaedcc53a9cd + md5: 9dbb9699ea467983ba8a4ba89b08b066 + depends: + - libgcc 14.1.0 h77fa898_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 3892781 + timestamp: 1724801863728 +- kind: conda + name: libstdcxx-ng + version: 14.1.0 + build: h4852527_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda + sha256: a2dc44f97290740cc187bfe94ce543e6eb3c2ea8964d99f189a1d8c97b419b8c + md5: bd2598399a70bb86d8218e95548d735e + depends: + - libstdcxx 14.1.0 hc0a3c3a_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 52219 + timestamp: 1724801897766 +- kind: conda + name: libuuid + version: 2.38.1 + build: h0b41bf4_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 + md5: 40b61aab5c7ba9ff276c41cfffe6b80b + depends: + - libgcc-ng >=12 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 33601 + timestamp: 1680112270483 +- kind: conda + name: libxcrypt + version: 4.4.36 + build: hd590300_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- kind: conda + name: libzlib + version: 1.3.1 + build: h4ab18f5_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-h4ab18f5_1.conda + sha256: adf6096f98b537a11ae3729eaa642b0811478f0ea0402ca67b5108fe2cb0010d + md5: 57d7dc60e9325e3de37ff8dffd18e814 + depends: + - libgcc-ng >=12 + constrains: + - zlib 1.3.1 *_1 + license: Zlib + license_family: Other + purls: [] + size: 61574 + timestamp: 1716874187109 +- kind: conda + name: libzlib + version: 1.3.1 + build: h87427d6_1 + build_number: 1 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-h87427d6_1.conda + sha256: 80a62db652b1da0ccc100812a1d86e94f75028968991bfb17f9536f3aa72d91d + md5: b7575b5aa92108dcc9aaab0f05f2dbce + depends: + - __osx >=10.13 + constrains: + - zlib 1.3.1 *_1 + license: Zlib + license_family: Other + purls: [] + size: 57372 + timestamp: 1716874211519 +- kind: pypi + name: markdown-it-py + version: 3.0.0 + url: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl + sha256: 355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 + requires_dist: + - mdurl~=0.1 + - psutil ; extra == 'benchmarking' + - pytest ; extra == 'benchmarking' + - pytest-benchmark ; extra == 'benchmarking' + - pre-commit~=3.0 ; extra == 'code-style' + - commonmark~=0.9 ; extra == 'compare' + - markdown~=3.4 ; extra == 'compare' + - mistletoe~=1.0 ; extra == 'compare' + - mistune~=2.0 ; extra == 'compare' + - panflute~=2.3 ; extra == 'compare' + - linkify-it-py>=1,<3 ; extra == 'linkify' + - mdit-py-plugins ; extra == 'plugins' + - gprof2dot ; extra == 'profiling' + - mdit-py-plugins ; extra == 'rtd' + - myst-parser ; extra == 'rtd' + - pyyaml ; extra == 'rtd' + - sphinx ; extra == 'rtd' + - sphinx-copybutton ; extra == 'rtd' + - sphinx-design ; extra == 'rtd' + - sphinx-book-theme ; extra == 'rtd' + - jupyter-sphinx ; extra == 'rtd' + - coverage ; extra == 'testing' + - pytest ; extra == 'testing' + - pytest-cov ; extra == 'testing' + - pytest-regressions ; extra == 'testing' + requires_python: '>=3.8' +- kind: pypi + name: mdurl + version: 0.1.2 + url: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl + sha256: 84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 + requires_python: '>=3.7' +- kind: conda + name: ncurses + version: '6.5' + build: he02047a_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda + sha256: 6a1d5d8634c1a07913f1c525db6455918cbc589d745fac46d9d6e30340c8731a + md5: 70caf8bb6cf39a0b6b7efc885f51c0fe + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + license: X11 AND BSD-3-Clause + purls: [] + size: 889086 + timestamp: 1724658547447 +- kind: conda + name: ncurses + version: '6.5' + build: hf036a51_1 + build_number: 1 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda + sha256: b0b3180039ef19502525a2abd5833c00f9624af830fd391f851934d57bffb9af + md5: e102bbf8a6ceeaf429deab8032fc8977 + depends: + - __osx >=10.13 + license: X11 AND BSD-3-Clause + purls: [] + size: 822066 + timestamp: 1724658603042 +- kind: conda + name: openssl + version: 3.3.2 + build: hb9d3cd8_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda + sha256: cee91036686419f6dd6086902acf7142b4916e1c4ba042e9ca23e151da012b6d + md5: 4d638782050ab6faa27275bed57e9b4e + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=13 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 2891789 + timestamp: 1725410790053 +- kind: conda + name: openssl + version: 3.3.2 + build: hd23fc13_0 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda + sha256: 2b75d4b56e45992adf172b158143742daeb316c35274b36f385ccb6644e93268 + md5: 2ff47134c8e292868a4609519b1ea3b6 + depends: + - __osx >=10.13 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 2544654 + timestamp: 1725410973572 +- kind: conda + name: packaging + version: '24.1' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda + sha256: 36aca948219e2c9fdd6d80728bcc657519e02f06c2703d8db3446aec67f51d81 + md5: cbe1bb1f21567018ce595d9c2be0f0db + depends: + - python >=3.8 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=hash-mapping + size: 50290 + timestamp: 1718189540074 +- kind: conda + name: pluggy + version: 1.5.0 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda + sha256: 33eaa3359948a260ebccf9cdc2fd862cea5a6029783289e13602d8e634cd9a26 + md5: d3483c8fc2dc2cc3f5cf43e26d60cabf + depends: + - python >=3.8 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pluggy?source=hash-mapping + size: 23815 + timestamp: 1713667175451 +- kind: conda + name: pycparser + version: '2.22' + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda + sha256: 406001ebf017688b1a1554b49127ca3a4ac4626ec0fd51dc75ffa4415b720b64 + md5: 844d9eb3b43095b031874477f7d70088 + depends: + - python >=3.8 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pycparser?source=hash-mapping + size: 105098 + timestamp: 1711811634025 +- kind: pypi + name: pygments + version: 2.18.0 + url: https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl + sha256: b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a + requires_dist: + - colorama>=0.4.6 ; extra == 'windows-terminal' + requires_python: '>=3.8' +- kind: conda + name: pysocks + version: 1.7.1 + build: pyha2e5f31_6 + build_number: 6 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2 + sha256: a42f826e958a8d22e65b3394f437af7332610e43ee313393d1cf143f0a2d274b + md5: 2a7de29fb590ca14b5243c4c812c8025 + depends: + - __unix + - python >=3.8 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pysocks?source=hash-mapping + size: 18981 + timestamp: 1661604969727 +- kind: conda + name: pytest + version: 8.3.2 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.2-pyhd8ed1ab_0.conda + sha256: 72c84a3cd9fe82835a88e975fd2a0dbf2071d1c423ea4f79e7930578c1014873 + md5: e010a224b90f1f623a917c35addbb924 + depends: + - colorama + - exceptiongroup >=1.0.0rc8 + - iniconfig + - packaging + - pluggy <2,>=1.5 + - python >=3.8 + - tomli >=1 + constrains: + - pytest-faulthandler >=2 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pytest?source=hash-mapping + size: 257671 + timestamp: 1721923749407 +- kind: conda + name: python + version: 3.12.5 + build: h2ad013b_0_cpython + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.5-h2ad013b_0_cpython.conda + sha256: e2aad83838988725d4ffba4e9717b9328054fd18a668cff3377e0c50f109e8bd + md5: 9c56c4df45f6571b13111d8df2448692 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.6.2,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc-ng >=12 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.46.0,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.3.1,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - xz >=5.2.6,<6.0a0 + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 31663253 + timestamp: 1723143721353 +- kind: conda + name: python + version: 3.12.5 + build: h37a9e06_0_cpython + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.5-h37a9e06_0_cpython.conda + sha256: c0f39e625b2fd65f70a9cc086fe4b25cc72228453dbbcd92cd5d140d080e38c5 + md5: 517cb4e16466f8d96ba2a72897d14c48 + depends: + - __osx >=10.13 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.6.2,<3.0a0 + - libffi >=3.4,<4.0a0 + - libsqlite >=3.46.0,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.3.1,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - xz >=5.2.6,<6.0a0 + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + purls: [] + size: 12173272 + timestamp: 1723142761765 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 + md5: 0424ae29b104430108f5218a66db7260 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6238 + timestamp: 1723823388266 +- kind: conda + name: python_abi + version: '3.12' + build: 5_cp312 + build_number: 5 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda + sha256: 4da26c7508d5bc5d8621e84dc510284402239df56aab3587a7d217de9d3c806d + md5: c34dd4920e0addf7cfcc725809f25d8e + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6312 + timestamp: 1723823137004 +- kind: conda + name: pyyaml + version: 6.0.2 + build: py312hb553811_1 + build_number: 1 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.2-py312hb553811_1.conda + sha256: 455ce40588b35df654cb089d29cc3f0d3c78365924ffdfc6ee93dba80cea5f33 + md5: 66514594817d51c78db7109a23ad322f + depends: + - __osx >=10.13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 189347 + timestamp: 1725456465705 +- kind: conda + name: readline + version: '8.2' + build: h8228510_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda + sha256: 5435cf39d039387fbdc977b0a762357ea909a7694d9528ab40f005e9208744d7 + md5: 47d31b792659ce70f470b5c82fdfb7a4 + depends: + - libgcc-ng >=12 + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 281456 + timestamp: 1679532220005 +- kind: conda + name: readline + version: '8.2' + build: h9e318b2_1 + build_number: 1 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda + sha256: 41e7d30a097d9b060037f0c6a2b1d4c4ae7e942c06c943d23f9d481548478568 + md5: f17f77f2acf4d344734bda76829ce14e + depends: + - ncurses >=6.3,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 255870 + timestamp: 1679532707590 +- kind: conda + name: requests + version: 2.32.3 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda + sha256: 5845ffe82a6fa4d437a2eae1e32a1ad308d7ad349f61e337c0a890fe04c513cc + md5: 5ede4753180c7a550a443c430dc8ab52 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.8 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/requests?source=hash-mapping + size: 58810 + timestamp: 1717057174842 +- kind: pypi + name: rich + version: 13.8.0 + url: https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl + sha256: 2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc + requires_dist: + - ipywidgets>=7.5.1,<9 ; extra == 'jupyter' + - markdown-it-py>=2.2.0 + - pygments>=2.13.0,<3.0.0 + - typing-extensions>=4.0.0,<5.0 ; python_full_version < '3.9' + requires_python: '>=3.7.0' +- kind: conda + name: tk + version: 8.6.13 + build: h1abcd95_1 + build_number: 1 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda + sha256: 30412b2e9de4ff82d8c2a7e5d06a15f4f4fef1809a72138b6ccb53a33b26faf5 + md5: bf830ba5afc507c6232d4ef0fb1a882d + depends: + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3270220 + timestamp: 1699202389792 +- kind: conda + name: tk + version: 8.6.13 + build: noxft_h4845f30_101 + build_number: 101 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda + sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e + md5: d453b98d9c83e71da0741bb0ff4d76bc + depends: + - libgcc-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3318875 + timestamp: 1699202167581 +- kind: conda + name: tomli + version: 2.0.1 + build: pyhd8ed1ab_0 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2 + sha256: 4cd48aba7cd026d17e86886af48d0d2ebc67ed36f87f6534f4b67138f5a5a58f + md5: 5844808ffab9ebdb694585b50ba02a96 + depends: + - python >=3.7 + license: MIT + license_family: MIT + purls: + - pkg:pypi/tomli?source=hash-mapping + size: 15940 + timestamp: 1644342331069 +- kind: conda + name: tzdata + version: 2024a + build: h8827d51_1 + build_number: 1 + subdir: noarch + noarch: generic + url: https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h8827d51_1.conda + sha256: 7d21c95f61319dba9209ca17d1935e6128af4235a67ee4e57a00908a1450081e + md5: 8bfdead4e0fff0383ae4c9c50d0531bd + license: LicenseRef-Public-Domain + purls: [] + size: 124164 + timestamp: 1724736371498 +- kind: conda + name: urllib3 + version: 2.2.2 + build: pyhd8ed1ab_1 + build_number: 1 + subdir: noarch + noarch: python + url: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.2-pyhd8ed1ab_1.conda + sha256: 00c47c602c03137e7396f904eccede8cc64cc6bad63ce1fc355125df8882a748 + md5: e804c43f58255e977093a2298e442bb8 + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.8 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/urllib3?source=hash-mapping + size: 95048 + timestamp: 1719391384778 +- kind: conda + name: xz + version: 5.2.6 + build: h166bdaf_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2 + sha256: 03a6d28ded42af8a347345f82f3eebdd6807a08526d47899a42d62d319609162 + md5: 2161070d867d1b1204ea749c8eec4ef0 + depends: + - libgcc-ng >=12 + license: LGPL-2.1 and GPL-2.0 + purls: [] + size: 418368 + timestamp: 1660346797927 +- kind: conda + name: xz + version: 5.2.6 + build: h775f41a_0 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2 + sha256: eb09823f34cc2dd663c0ec4ab13f246f45dcd52e5b8c47b9864361de5204a1c8 + md5: a72f9d4ea13d55d745ff1ed594747f10 + license: LGPL-2.1 and GPL-2.0 + purls: [] + size: 238119 + timestamp: 1660346964847 +- kind: conda + name: yaml + version: 0.2.5 + build: h0d85af4_2 + build_number: 2 + subdir: osx-64 + url: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h0d85af4_2.tar.bz2 + sha256: 5301417e2c8dea45b401ffee8df3957d2447d4ce80c83c5ff151fc6bfe1c4148 + md5: d7e08fcf8259d742156188e8762b4d20 + license: MIT + license_family: MIT + purls: [] + size: 84237 + timestamp: 1641347062780 +- kind: conda + name: zstandard + version: 0.23.0 + build: py312hef9b889_1 + build_number: 1 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py312hef9b889_1.conda + sha256: b97015e146437283f2213ff0e95abdc8e2480150634d81fbae6b96ee09f5e50b + md5: 8b7069e9792ee4e5b4919a7a306d2e67 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.11 + - libgcc >=13 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.6,<1.5.7.0a0 + - zstd >=1.5.6,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/zstandard?source=hash-mapping + size: 419552 + timestamp: 1725305670210 +- kind: conda + name: zstd + version: 1.5.6 + build: ha6fb4c9_0 + subdir: linux-64 + url: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda + sha256: c558b9cc01d9c1444031bd1ce4b9cff86f9085765f17627a6cd85fc623c8a02b + md5: 4d056880988120e29d75bfff282e0f45 + depends: + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - libzlib >=1.2.13,<2.0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 554846 + timestamp: 1714722996770 diff --git a/src/cli/project/export/test-data/testenv/pixi.toml b/src/cli/project/export/test-data/testenv/pixi.toml new file mode 100644 index 000000000..5db6c50b8 --- /dev/null +++ b/src/cli/project/export/test-data/testenv/pixi.toml @@ -0,0 +1,27 @@ +[project] +authors = [] +channels = ["conda-forge"] +description = "test environments" +name = "testenv" +platforms = ["osx-64", "linux-64"] +version = "0.1.0" + +[tasks] + +[dependencies] +python = ">=3.12.5,<4" + +[pypi-dependencies] +rich = ">=13.8.0, <14" + +[target.linux-64.dependencies] +requests = ">=2.32.3,<3" + +[target.osx-64.dependencies] +pyyaml = ">=6.0.2,<7" + +[feature.test.dependencies] +pytest = "*" + +[environments] +test = ["test"] From 272e8b3d46f875074195a782d8be9f4468d09c0a Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Thu, 5 Sep 2024 17:27:06 -0400 Subject: [PATCH 6/7] fix whitespace issue --- docs/reference/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index bc5a8ee51..00dbad0e2 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1197,7 +1197,7 @@ As the explicit specification file format does not support pypi-dependencies, us - `--environment (-e)`: Environment to render. Can be repeated for multiple envs. Defaults to all environments. - `--platform (-p)`: The platform to render. Can be repeated for multiple platforms. Defaults to all platforms available for selected environments. -- `--ignore-pypi-errors`: PyPI dependencies are not supported in the conda explicit spec file. This flag allows creating the spec file even if PyPI dependencies are present. +- `--ignore-pypi-errors`: PyPI dependencies are not supported in the conda explicit spec file. This flag allows creating the spec file even if PyPI dependencies are present. ```sh pixi project export conda_explicit_spec output From c74c69a0e88eb1cb039fe67245e93713ed36ad8f Mon Sep 17 00:00:00 2001 From: "Joshua L. Adelman" Date: Sat, 7 Sep 2024 21:24:47 -0400 Subject: [PATCH 7/7] fix error/diagnostics output in conda export explicit --- src/cli/project/export/conda_explicit_spec.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cli/project/export/conda_explicit_spec.rs b/src/cli/project/export/conda_explicit_spec.rs index 1d31b4ed7..d34521608 100644 --- a/src/cli/project/export/conda_explicit_spec.rs +++ b/src/cli/project/export/conda_explicit_spec.rs @@ -3,6 +3,7 @@ use std::fs; use std::path::{Path, PathBuf}; use clap::Parser; +use miette::{Context, IntoDiagnostic}; use crate::cli::cli_config::PrefixUpdateConfig; use crate::cli::LockFileUsageArgs; @@ -73,12 +74,15 @@ fn render_explicit_spec( return Ok(()); } + let target = target.as_ref(); + let mut environment = String::new(); environment.push_str("# Generated by `pixi project export`\n"); environment.push_str(exp_env_spec.to_spec_string().as_str()); fs::write(target, environment) - .map_err(|e| miette::miette!("Could not write environment file: {}", e))?; + .into_diagnostic() + .with_context(|| format!("failed to write environment file: {}", target.display()))?; Ok(()) }