From 5eb7436a80a0bad19af6b42a543a9ee8c7eb909e Mon Sep 17 00:00:00 2001 From: neur1n Date: Wed, 13 Aug 2025 11:29:49 +0800 Subject: [PATCH] refactor(nu_conda): improve performance and compatibility 1. improve the startup efficiency by using job and std-rfc/kv 2. update info parsing to work with mamba v2.* --- .../virtual_environments/nu_conda/README.md | 37 +++---- .../virtual_environments/nu_conda/nu_conda.nu | 97 +++++++++++++------ 2 files changed, 85 insertions(+), 49 deletions(-) diff --git a/modules/virtual_environments/nu_conda/README.md b/modules/virtual_environments/nu_conda/README.md index 75d74faf3..c35a6031e 100644 --- a/modules/virtual_environments/nu_conda/README.md +++ b/modules/virtual_environments/nu_conda/README.md @@ -1,9 +1,11 @@ # Conda Module for Nushell -A simple module for activating and deactivating Conda environments. +A simple module for finding, listing, activating, and deactivating Conda environments. ## Prerequisites -- [nushell](https://github.com/nushell/nushell) >= 0.94.0 +The following commands must be available in your version of Nushell: +- [job](https://www.nushell.sh/commands/docs/job.html) +- [std-rfc/kv](https://github.com/nushell/nushell/blob/main/crates/nu-std/std-rfc/kv/mod.nu) ## Installation @@ -12,16 +14,18 @@ Put `nu_conda.nu` into the module folder of your nushell configuration workspace ## Usage ```nu -use nu_conda.nu # activate module -nu_conda activate py36 # activate a Conda environment, e.g. py36 -nu_conda deactivate # deactivate the activated Conda environment -nu_conda list # list available environments, same as `$env.CONDA_ENVS` +use nu_conda.nu # import module, Conda environments will be searched +nu_conda activate py310 # activate a Conda environment, e.g. py310 +nu_conda deactivate # deactivate the activated Conda environment +nu_conda find # find/update available environments +nu_conda list # list available environments, same as `$env.CONDA_ENVS` ``` ## How It Works -This module re-implements the activation and deactivation functionalities of -the [conda.nu](https://github.com/Neur1n/nu_scripts/blob/main/virtual_environments/conda.nu) -module while providing a better performance, but not fully replacing it. +> [!NOTE] +> Originally, this module cached Conda environment information during import, +> which increased startup time. It now performs the caching in a background +> job, significantly reducing startup latency. This module adds paths of a target Conda environment to `PATH`/`Path` while activating the environment, and recover the original `PATH`/`Path` while @@ -30,13 +34,13 @@ deactivating an environment. Several environment variables are exported: - `CONDA_BASE_PATH`: The original `PATH`/`Path` before any activation/deactivation. - `CONDA_ROOT`: Root directory of Conda installation. - `CONDA_ENVS`: Available Conda environments for activation. -- `CONDA_CURR`: Current activated Conda environments. +- `CONDA_CURR`: Current activated Conda environment. ## FAQ **Q**: How better is the performance?\ -**A**: Activating a Conda environment costs ~20ms while conda.nu costs ~1500ms on -a PC with Windows 10 Enterprise OS and Intel i7-8700 3.20GHz CPU. +**A**: Activating a Conda environment costs ~20ms while conda.nu costs ~1500ms +on a PC with Windows 10 Enterprise OS and Intel i7-8700 3.20GHz CPU. **Q**: How to show the current Conda environment in the prompt?\ **A**: This module does not automatically change the prompt when a Conda @@ -46,15 +50,14 @@ prompt. **Q**: Does it support Mamba/Micromamba?\ -**A**: As [Mamba's documentation](https://mamba.readthedocs.io/en/latest/) said, -`mamba` is drop-in replacement for `conda`, and `micromamba` seems to be +**A**: As [Mamba's documentation](https://mamba.readthedocs.io/en/latest/) +said, `mamba` is drop-in replacement for `conda`, and `micromamba` seems to be another thing. This module only uses results of `conda/mamba info --envs --json`. -Therefore, I would say Mamba is (partially?) supported but I'm not sure about -Micromamba. +Therefore, I would say Mamba is supported, but I'm not sure about Micromamba. **Q**: How does it choose between Conda and Mamba?\ -**A**: This module prefers calling `mamba` than `conda`, but it should be very +**A**: This module prefers calling `mamba` over `conda`, but it should be very easy to change the preference by modifying the source code. diff --git a/modules/virtual_environments/nu_conda/nu_conda.nu b/modules/virtual_environments/nu_conda/nu_conda.nu index fd63f9733..b824230e8 100644 --- a/modules/virtual_environments/nu_conda/nu_conda.nu +++ b/modules/virtual_environments/nu_conda/nu_conda.nu @@ -1,30 +1,15 @@ +use std-rfc/kv * + export-env { - if not ("CONDA_CURR" in $env) { - $env.CONDA_BASE_PATH = (if $nu.os-info.name == 'windows' {$env.Path} else {$env.PATH}) - - let info = ( - if not (which mamba | is-empty) { - (mamba info --envs --json | from json) - } else if not (which conda | is-empty) { - (conda info --envs --json | from json) - } else { - ('{"root_prefix": "", "envs": ""}' | from json) - }) - - $env.CONDA_ROOT = $info.root_prefix - - $env.CONDA_ENVS = ($info.envs | reduce -f {} {|it, acc| - if $it == $info.root_prefix { - $acc | upsert "base" $it - } else { - $acc | upsert ($it | path basename) $it - }}) - - $env.CONDA_CURR = null - } + job spawn {find} } export def --env activate [name: string] { + if ($env.CONDA_ENVS? | is-empty) { + find + kv get "conda envars" | load-env + } + if ($env.CONDA_ROOT | is-empty) { print "Neither Conda nor Mamba is available." return @@ -37,7 +22,7 @@ export def --env activate [name: string] { } let new_path = ( - if $nu.os-info.name == 'windows' { + if ($nu.os-info.name == "windows") { update-path-windows ($env.CONDA_ENVS | get $name) } else { update-path-linux ($env.CONDA_ENVS | get $name) @@ -47,23 +32,71 @@ export def --env activate [name: string] { } export def --env deactivate [] { + if ($env.CONDA_ENVS? | is-empty) { + find + kv get "conda envars" | load-env + } + if ($env.CONDA_ROOT | is-empty) { print "Neither Conda nor Mamba is available." return } - $env.CONDA_CURR = null + $env.CONDA_CURR = "" load-env {Path: $env.CONDA_BASE_PATH, PATH: $env.CONDA_BASE_PATH} } -export def --env list [] { - $env.CONDA_ENVS | - flatten | - transpose | - rename name path | - insert active { |it| $it.name == $env.CONDA_CURR } | - move path --after active +export def find [] { + let $base_path = (if ($nu.os-info.name == "windows") {$env.Path} else {$env.PATH}) + + let info = ( + if not (which mamba | is-empty) { + (mamba info --envs --json | from json) + } else if not (which conda | is-empty) { + (conda info --envs --json | from json) + } else { + ('{"root": "", "envs": []}' | from json) + }) + + let info = ( + if not ("root" in $info) { + $info | insert root $info.envs.0 + } else { + $info + }) + + let $root = $info.root + + let $envs = ($info.envs | reduce -f {} {|it, acc| + if $it == $info.root { + $acc | upsert "base" $it + } else { + $acc | upsert ($it | path basename) $it + }}) + + let $vars = { + CONDA_BASE_PATH: $base_path, + CONDA_CURR: "", + CONDA_ROOT: $root, + CONDA_ENVS: $envs + } + + kv set "conda envars" $vars +} + +export def list [] { + if ($env.CONDA_ENVS? | is-empty) { + find + kv get "conda envars" | load-env + } + + $env.CONDA_ENVS + | flatten + | transpose + | rename name path + | insert active {|it| $it.name == $env.CONDA_CURR} + | move path --after active } def update-path-linux [env_path: path] {