From 2b3312fc06cec053803d236d4906cdb28b901f9e Mon Sep 17 00:00:00 2001 From: Hristo Filaretov Date: Mon, 2 Aug 2021 08:36:58 +0200 Subject: [PATCH 1/4] Add scripts for virtual environments This commit adds two rudimentary scripts for interacting with Conda environments and Python virtual environments created with `venv`. Both scripts follow the same pattern: a table of new environment variables is created via the script and the user must activate it by loading it with `load-env`: `load-env (conda-env name-of-env)` The environments are deactivated by sourcing a script, which uses `unlet-env` to remove the environment variables, and restores the old path. In both cases, nesting virtual environments is not supported. As soon as an environment is activated, all information from the previous one is overwritten. --- virtual_environments/conda.nu | 39 ++++++++++++++++++++++++ virtual_environments/conda_deactivate.nu | 6 ++++ virtual_environments/venv.nu | 19 ++++++++++++ virtual_environments/venv_deactivate.nu | 3 ++ 4 files changed, 67 insertions(+) create mode 100644 virtual_environments/conda.nu create mode 100644 virtual_environments/conda_deactivate.nu create mode 100644 virtual_environments/venv.nu create mode 100644 virtual_environments/venv_deactivate.nu diff --git a/virtual_environments/conda.nu b/virtual_environments/conda.nu new file mode 100644 index 0000000..9f03de0 --- /dev/null +++ b/virtual_environments/conda.nu @@ -0,0 +1,39 @@ +def conda-env [env-name] { + let conda-info = (conda info --envs --json | from json) + let suffix = (if $env-name == "base" {""} {(["envs" $env-name] | path join)}) + let env-dir = ([$conda-info.root_prefix $suffix] | path join) + let old-path = ($nu.path | str collect (path-sep)) + let env-path = (if (windows?) { (conda-create-path-windows $env-dir) } { (conda-create-path-unix $env-dir) }) + let new-path = ([$env-path $nu.path] | flatten | str collect (path-sep)) + [[name, value]; + [CONDA_DEFAULT_ENV $env-name] + [CONDA_PREFIX $env-dir] + [CONDA_PROMPT_MODIFIER $"[($env-name)]"] + [CONDA_SHLVL "1"] + [CONDA_OLD_PATH $old-path] + [PATH $new-path]] +} + +def conda-create-path-windows [env-dir] { + [ + $env-dir + ([$env-dir "Scripts"] | path join) + ([$env-dir "Library" "mingw-w64"] | path join) + ([$env-dir "Library" "bin"] | path join) + ([$env-dir "Library" "usr" "bin"] | path join) + ] +} + +def conda-create-path-unix [env-dir] { + [ + ([$env-dir "bin"] | path join) + ] +} + +def windows? [] { + (sys).host.name == "Windows" +} + +def path-sep [] { + if (windows?) { ";" } { ":" } +} diff --git a/virtual_environments/conda_deactivate.nu b/virtual_environments/conda_deactivate.nu new file mode 100644 index 0000000..c2f9953 --- /dev/null +++ b/virtual_environments/conda_deactivate.nu @@ -0,0 +1,6 @@ +let-env PATH = $nu.env.CONDA_OLD_PATH +unlet-env CONDA_PROMPT_MODIFIER +unlet-env CONDA_PREFIX +unlet-env CONDA_SHLVL +unlet-env CONDA_DEFAULT_ENV +unlet-env CONDA_OLD_PATH diff --git a/virtual_environments/venv.nu b/virtual_environments/venv.nu new file mode 100644 index 0000000..64f26e7 --- /dev/null +++ b/virtual_environments/venv.nu @@ -0,0 +1,19 @@ +def venv [name] { + let venv-path = ($name | path expand) + let venv-bin-path = ([$venv-path "bin"] | path join) + let old-path = ($nu.path | str collect (path-sep)) + let new-path = ($nu.path | prepend $venv-bin-path | str collect (path-sep)) + [[name, value]; + [PATH $new-path] + [VENV_OLD_PATH $old-path] + [VIRTUAL_ENV $venv-path]] +} + +def windows? [] { + (sys).host.name == "Windows" +} + +def path-sep [] { + if (windows?) { ";" } { ":" } +} + diff --git a/virtual_environments/venv_deactivate.nu b/virtual_environments/venv_deactivate.nu new file mode 100644 index 0000000..64cec01 --- /dev/null +++ b/virtual_environments/venv_deactivate.nu @@ -0,0 +1,3 @@ +let-env PATH = $nu.env.VENV_OLD_PATH +unlet-env VIRTUAL_ENV +unlet-env VENV_OLD_PATH From 9c7822aa188bd191b3acfc476200c9971f840dff Mon Sep 17 00:00:00 2001 From: Hristo Filaretov Date: Wed, 4 Aug 2021 09:44:35 +0200 Subject: [PATCH 2/4] Add Windows support The path env var on Windows is Path, not PATH. This commit includes that difference when differentiating between OS. --- virtual_environments/conda.nu | 18 +++++++++++------- virtual_environments/conda_deactivate.nu | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/virtual_environments/conda.nu b/virtual_environments/conda.nu index 9f03de0..d7f9c80 100644 --- a/virtual_environments/conda.nu +++ b/virtual_environments/conda.nu @@ -3,31 +3,35 @@ def conda-env [env-name] { let suffix = (if $env-name == "base" {""} {(["envs" $env-name] | path join)}) let env-dir = ([$conda-info.root_prefix $suffix] | path join) let old-path = ($nu.path | str collect (path-sep)) - let env-path = (if (windows?) { (conda-create-path-windows $env-dir) } { (conda-create-path-unix $env-dir) }) - let new-path = ([$env-path $nu.path] | flatten | str collect (path-sep)) - [[name, value]; + let new-path = (if (windows?) { (conda-create-path-windows $env-dir) } { (conda-create-path-unix $env-dir) }) + let new-env = [[name, value]; [CONDA_DEFAULT_ENV $env-name] [CONDA_PREFIX $env-dir] [CONDA_PROMPT_MODIFIER $"[($env-name)]"] [CONDA_SHLVL "1"] - [CONDA_OLD_PATH $old-path] - [PATH $new-path]] + [CONDA_OLD_PATH $old-path]] + + $new-env | append $new-path } def conda-create-path-windows [env-dir] { - [ + let env-path = [ $env-dir ([$env-dir "Scripts"] | path join) ([$env-dir "Library" "mingw-w64"] | path join) ([$env-dir "Library" "bin"] | path join) ([$env-dir "Library" "usr" "bin"] | path join) ] + let new-path = ([$env-path $nu.path] | flatten | str collect (path-sep)) + [[name, value]; [Path $new-path]] } def conda-create-path-unix [env-dir] { - [ + let env-path = [ ([$env-dir "bin"] | path join) ] + let new-path = ([$env-path $nu.path] | flatten | str collect (path-sep)) + [[name, value]; [PATH $new-path]] } def windows? [] { diff --git a/virtual_environments/conda_deactivate.nu b/virtual_environments/conda_deactivate.nu index c2f9953..69f3dae 100644 --- a/virtual_environments/conda_deactivate.nu +++ b/virtual_environments/conda_deactivate.nu @@ -1,4 +1,5 @@ -let-env PATH = $nu.env.CONDA_OLD_PATH +let path-name = (if ((sys).host.name == "Windows") { "Path" } { "PATH" }) +let-env $path-name = $nu.env.CONDA_OLD_PATH unlet-env CONDA_PROMPT_MODIFIER unlet-env CONDA_PREFIX unlet-env CONDA_SHLVL From 6def5d5517c0272e627d4a98c208475f96828e3c Mon Sep 17 00:00:00 2001 From: Hristo Filaretov Date: Thu, 5 Aug 2021 11:07:46 +0200 Subject: [PATCH 3/4] Add Windows support for venv As with the Conda script, a few differences have to be considered when activating on Windows, namely that the correct env var is Path and the directory containing the executables is "Scripts", not "bin" --- virtual_environments/venv.nu | 28 ++++++++++++++++++------- virtual_environments/venv_deactivate.nu | 3 ++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/virtual_environments/venv.nu b/virtual_environments/venv.nu index 64f26e7..c93ddcc 100644 --- a/virtual_environments/venv.nu +++ b/virtual_environments/venv.nu @@ -1,12 +1,25 @@ -def venv [name] { - let venv-path = ($name | path expand) - let venv-bin-path = ([$venv-path "bin"] | path join) +def venv [venv-dir] { + let venv-abs-dir = ($venv-dir | path expand) + let venv-name = ($venv-abs-dir | path basename) let old-path = ($nu.path | str collect (path-sep)) - let new-path = ($nu.path | prepend $venv-bin-path | str collect (path-sep)) - [[name, value]; - [PATH $new-path] + let new-path = (if (windows?) { (venv-path-windows $venv-abs-dir) } { (venv-path-unix $venv-abs-dir) }) + let new-env = [[name, value]; [VENV_OLD_PATH $old-path] - [VIRTUAL_ENV $venv-path]] + [VIRTUAL_ENV $venv-name]] + + $new-env | append $new-path +} + +def venv-path-unix [venv-dir] { + let venv-path = ([$venv-dir "bin"] | path join) + let new-path = ($nu.path | prepend $venv-path | str collect (path-sep)) + [[name, value]; [PATH $new-path]] +} + +def venv-path-windows [venv-dir] { + let venv-path = ([$venv-dir "Scripts"] | path join) + let new-path = ($nu.path | prepend $venv-path | str collect (path-sep)) + [[name, value]; [Path $new-path]] } def windows? [] { @@ -16,4 +29,3 @@ def windows? [] { def path-sep [] { if (windows?) { ";" } { ":" } } - diff --git a/virtual_environments/venv_deactivate.nu b/virtual_environments/venv_deactivate.nu index 64cec01..95cdd11 100644 --- a/virtual_environments/venv_deactivate.nu +++ b/virtual_environments/venv_deactivate.nu @@ -1,3 +1,4 @@ -let-env PATH = $nu.env.VENV_OLD_PATH +let path-name = (if ((sys).host.name == "Windows") { "Path" } { "PATH" }) +let-env $path-name = $nu.env.VENV_OLD_PATH unlet-env VIRTUAL_ENV unlet-env VENV_OLD_PATH From 9ed5c6063327a7be300d3a53243ee0a304e77e39 Mon Sep 17 00:00:00 2001 From: Hristo Filaretov Date: Thu, 5 Aug 2021 12:56:02 +0200 Subject: [PATCH 4/4] Add documentation and format the source files Add a README with information on how to use the virtual environment commands and the patterns they are based on. Also add the two new commands to the root README table. --- README.md | 2 ++ virtual_environments/README.md | 53 ++++++++++++++++++++++++++++++++++ virtual_environments/conda.nu | 12 ++++---- virtual_environments/venv.nu | 30 ++++++++++--------- 4 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 virtual_environments/README.md diff --git a/README.md b/README.md index b253ae6..fc98b76 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,5 @@ You can run nushell scripts in a few different ways. | prompt | [git_status_prompt.nu](./prompt/git_status_prompt.nu) | 0.28 | Creates a prompt which includes short working directory and current git status. | | stdlib_candidate | [nu_style.nu](./stdlib_candidate/nu_style.nu) | 0.26 | Define colors as names. | | stdlib_candidate | [print.nu](./stdlib_candidate/print.nu) | 0.26 | Examples of a print statement. | +| virtual_envs | [conda.nu](./virtual_environments/conda.nu) | 0.32 | Activates a Conda environment. | +| virtual_envs | [venv.nu](./virtual_environments/venv.nu) | 0.32 | Activates a Python venv. | diff --git a/virtual_environments/README.md b/virtual_environments/README.md new file mode 100644 index 0000000..c578d4c --- /dev/null +++ b/virtual_environments/README.md @@ -0,0 +1,53 @@ +# Virtual environment scripts + +The scripts in this directory activate virtual environments like Python venvs +and 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): + +``` +$ 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. + +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. + +## Expected Usage + +1. Source an activation script in your `config.toml`. 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`. + +Look at the script files to find the exact command name for creating an environment table. + +## `conda.nu` + +Limitations: + +- 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. + +## `venv.nu` + +Limitations: + +- The prompt is not updated by the script. Consider using [Starship](https://starship.rs/) + with the Python prompt element. \ No newline at end of file diff --git a/virtual_environments/conda.nu b/virtual_environments/conda.nu index d7f9c80..7e87402 100644 --- a/virtual_environments/conda.nu +++ b/virtual_environments/conda.nu @@ -5,16 +5,18 @@ def conda-env [env-name] { let old-path = ($nu.path | str collect (path-sep)) let new-path = (if (windows?) { (conda-create-path-windows $env-dir) } { (conda-create-path-unix $env-dir) }) let new-env = [[name, value]; - [CONDA_DEFAULT_ENV $env-name] - [CONDA_PREFIX $env-dir] - [CONDA_PROMPT_MODIFIER $"[($env-name)]"] - [CONDA_SHLVL "1"] - [CONDA_OLD_PATH $old-path]] + [CONDA_DEFAULT_ENV $env-name] + [CONDA_PREFIX $env-dir] + [CONDA_PROMPT_MODIFIER $"[($env-name)]"] + [CONDA_SHLVL "1"] + [CONDA_OLD_PATH $old-path]] $new-env | append $new-path } def conda-create-path-windows [env-dir] { + # 1. Conda on Windows needs a few additional Path elements + # 2. The path env var on Windows is called Path (not PATH) let env-path = [ $env-dir ([$env-dir "Scripts"] | path join) diff --git a/virtual_environments/venv.nu b/virtual_environments/venv.nu index c93ddcc..ba1ab81 100644 --- a/virtual_environments/venv.nu +++ b/virtual_environments/venv.nu @@ -1,25 +1,27 @@ def venv [venv-dir] { - let venv-abs-dir = ($venv-dir | path expand) - let venv-name = ($venv-abs-dir | path basename) - let old-path = ($nu.path | str collect (path-sep)) - let new-path = (if (windows?) { (venv-path-windows $venv-abs-dir) } { (venv-path-unix $venv-abs-dir) }) - let new-env = [[name, value]; - [VENV_OLD_PATH $old-path] - [VIRTUAL_ENV $venv-name]] + let venv-abs-dir = ($venv-dir | path expand) + let venv-name = ($venv-abs-dir | path basename) + let old-path = ($nu.path | str collect (path-sep)) + let new-path = (if (windows?) { (venv-path-windows $venv-abs-dir) } { (venv-path-unix $venv-abs-dir) }) + let new-env = [[name, value]; + [VENV_OLD_PATH $old-path] + [VIRTUAL_ENV $venv-name]] - $new-env | append $new-path + $new-env | append $new-path } def venv-path-unix [venv-dir] { - let venv-path = ([$venv-dir "bin"] | path join) - let new-path = ($nu.path | prepend $venv-path | str collect (path-sep)) - [[name, value]; [PATH $new-path]] + let venv-path = ([$venv-dir "bin"] | path join) + let new-path = ($nu.path | prepend $venv-path | str collect (path-sep)) + [[name, value]; [PATH $new-path]] } def venv-path-windows [venv-dir] { - let venv-path = ([$venv-dir "Scripts"] | path join) - let new-path = ($nu.path | prepend $venv-path | str collect (path-sep)) - [[name, value]; [Path $new-path]] + # 1. Conda on Windows needs a few additional Path elements + # 2. The path env var on Windows is called Path (not PATH) + let venv-path = ([$venv-dir "Scripts"] | path join) + let new-path = ($nu.path | prepend $venv-path | str collect (path-sep)) + [[name, value]; [Path $new-path]] } def windows? [] {