Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions modules/virtual_environments/nu_conda/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.


Expand Down
97 changes: 65 additions & 32 deletions modules/virtual_environments/nu_conda/nu_conda.nu
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand All @@ -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] {
Expand Down