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 new file mode 100644 index 0000000..7e87402 --- /dev/null +++ b/virtual_environments/conda.nu @@ -0,0 +1,45 @@ +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 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]] + + $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) + ([$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? [] { + (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..69f3dae --- /dev/null +++ b/virtual_environments/conda_deactivate.nu @@ -0,0 +1,7 @@ +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 +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..ba1ab81 --- /dev/null +++ b/virtual_environments/venv.nu @@ -0,0 +1,33 @@ +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]] + + $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] { + # 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? [] { + (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..95cdd11 --- /dev/null +++ b/virtual_environments/venv_deactivate.nu @@ -0,0 +1,4 @@ +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