Skip to content

Commit

Permalink
Change conda activation scripts to be a module (nushell#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
kubouch committed Apr 14, 2022
1 parent af33063 commit 34c2411
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 58 deletions.
43 changes: 13 additions & 30 deletions virtual_environments/README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,28 @@
# Virtual environment scripts

The scripts in this directory activate virtual environments for Conda environments. They follow the pattern described in the [Nushell 0.32
changelog](https://www.nushell.sh/blog/2021-06-01-nushell_0_32.html#environment-loading-lily-mara):
The scripts in this directory activate virtual environments for Conda environments.

```
$ load-env (activate some-env)
```

A custom command (`activate`) creates a table with environment variables and
`load-env` is used to load it into the shell's scope.
## Usage

In most cases, deactivation is a matter of restoring the PATH variable to the
state before activating the env and removing any additional variables. There are
no environment-specific elements to this, which is why the same deactivation
script can be used for deactivating any environment of a specific type. For
example, `source conda_deactivate.nu` will deactivate any Conda env, there are
no input parameters.
The activation and deactivation commands are exported from the `conda` module.

## Expected Usage

1. Source an activation script in your `config.nu`. For example, `conda.nu`.
You'll then have the `conda-env` command available.
2. Create an alias for sourcing the appropriate deactivation script:
`alias conda-deactivate = source /path/to/conda_deactivate.nu`
3. Activate with `load-env (conda-env env-name)`. You might want to define some
shorter aliases for both commands, if typing that every time seems like a
hassle.
4. If you're using [Starship](https://starship.rs/), your prompt should reflect the activated env.
5. When done, deactivate with your alias: `conda-deactivate`.
```
> use conda.nu
> conda activate foo
[foo] > conda deactivate
>
```

Look at the script files to find the exact command name for creating an environment table.
Do disable the prompt changes (e.g., to let [Starship](https://starship.rs) include its own), pass a `--no-prompt` flag to `activate`.
The `activate` command includes custom completions for the environment names.

## `conda.nu`
## Limitations

Limitations:
_(old text, not tested)_

- The "root_prefix" might not actually correspond to the correct path to the Conda envs. You can fix
this for your setup by changing how the root prefix is found in the `conda-env` command.
- Nested envs are not well supported. If you activate a Conda env while another one is
activated, new elements will be appended to the PATH, but the other environment
variables will be overwritten. There's no way to then restore the PATH to the state
it was in before activating the *first* env (at least not with this script directly).
- The prompt is not updated by the script. Consider using [Starship](https://starship.rs/)
with the Python prompt element.
122 changes: 101 additions & 21 deletions virtual_environments/conda.nu
Original file line number Diff line number Diff line change
@@ -1,20 +1,97 @@
def conda-env [env-name] {
# Activate conda environment
export def-env activate [
env-name: string@'nu-complete conda envs' # name of the environment
--no-prompt # do not update the prompt
] {
let conda-info = (conda info --envs --json | from json)
let suffix = (if $env-name == "base" {""} else {(["envs" $env-name] | path join)})

let suffix = if $env-name == "base" {
""
} else {
["envs" $env-name] | path join
}

let env-dir = ([$conda-info.root_prefix $suffix] | path join)
let old-path = ((system-path) | str collect (path-sep))
let new-path = (if (windows?) { (conda-create-path-windows $env-dir) } else { (conda-create-path-unix $env-dir) })
let new-env = {
let old-path = (system-path | str collect (char esep))

let new-path = if windows? {
conda-create-path-windows $env-dir
} else {
conda-create-path-unix $env-dir
}

let virtual-prompt = $'[($env-name)] '

let new-env = ({
CONDA_DEFAULT_ENV: $env-name
CONDA_PREFIX: $env-dir
CONDA_PROMPT_MODIFIER: $"[($env-name)]"
CONDA_PROMPT_MODIFIER: $virtual-prompt
CONDA_SHLVL: "1"
CONDA_OLD_PATH: $old-path
} | merge { $new-path })

let new-env = if not $no-prompt {
let old_prompt_command = if (has-env CONDA_OLD_PROMPT_COMMAND) {
$env.CONDA_OLD_PROMPT_COMMAND
} else {
if (has-env 'PROMPT_COMMAND') {
$env.PROMPT_COMMAND
} else {
''
}
}


let new-prompt = if (has-env 'PROMPT_COMMAND') {
if ($old_prompt_command | describe) == 'block' {
{ $'($virtual-prompt)(do $old_prompt_command)' }
} else {
{ $'($virtual-prompt)($old_prompt_command)' }
}
} else {
{ $'($virtual-prompt)' }
}

$new-env
| insert CONDA_OLD_PROMPT_COMMAND $old_prompt_command
| insert PROMPT_COMMAND $new-prompt
} else {
$new-env
}
$new-env | merge { $new-path }

load-env $new-env
}

def conda-create-path-windows [env-dir] {
# Deactivate currently active conda environment
export def-env deactivate [] {
let path-name = if "PATH" in (env).name { "PATH" } else { "Path" }
let-env $path-name = $env.CONDA_OLD_PATH
let-env PROMPT_COMMAND = $env.CONDA_OLD_PROMPT_COMMAND

hide CONDA_PROMPT_MODIFIER
hide CONDA_PREFIX
hide CONDA_SHLVL
hide CONDA_DEFAULT_ENV
hide CONDA_OLD_PATH

let-env PROMPT_COMMAND = if 'CONDA_OLD_PROMPT_COMMAND' in (env).name {
$env.CONDA_OLD_PROMPT_COMMAND
} else {
$env.PROMPT_COMMAND
}

hide CONDA_OLD_PROMPT_COMMAND
}

def 'nu-complete conda envs' [] {
conda info --envs
| lines
| where not ($it | str starts-with '#')
| where not ($it | empty?)
| each {|entry| $entry | split row ' ' | get 0 }
}

def conda-create-path-windows [env-dir: path] {
# Conda on Windows needs a few additional Path elements
let env-path = [
$env-dir
Expand All @@ -23,31 +100,34 @@ def conda-create-path-windows [env-dir] {
([$env-dir "Library" "bin"] | path join)
([$env-dir "Library" "usr" "bin"] | path join)
]
let new-path = ([$env-path (system-path)] | flatten | str collect (path-sep))
{
PATH: $new-path
}

let new-path = ([$env-path (system-path)]
| flatten
| str collect (char esep))

{ PATH: $new-path }
}

def conda-create-path-unix [env-dir] {
def conda-create-path-unix [env-dir: path] {
let env-path = [
([$env-dir "bin"] | path join)
]
let new-path = ([$env-path $env.PATH] | flatten | str collect (path-sep))
{
PATH: $new-path
}
}

let new-path = ([$env-path $env.PATH]
| flatten
| str collect (char esep))

{ PATH: $new-path }
}

def windows? [] {
(sys).host.name == "Windows"
((sys).host.name | str downcase) == "windows"
}

def system-path [] {
if "PATH" in (env).name { $env.PATH } else { $env.Path }
}

def path-sep [] {
if (windows?) { ";" } else { ":" }
def has-env [name: string] {
$name in (env).name
}
7 changes: 0 additions & 7 deletions virtual_environments/conda_deactivate.nu

This file was deleted.

0 comments on commit 34c2411

Please sign in to comment.