Subject: bug#59774: Activating conda environments breaks the prompt because bash eats PS1

From: Hugo Buddelmeijer <[email-masked]>
Date: Fri, 2 Dec 2022 13:47:52 +0100

Hi all,

### Summary ####

Activating a conda environment on guix will break your bash prompt, because
guix' binary-replacing-bash-script eats PS1.

### Reproduce ####

E.g.:

# Start a container with conda
hugo@alex ~/t $ guix shell -C conda sudo bash

# Add conda functions to ~/.bashrc. This raises errors but does work.
hugo@alex ~/t [env]$ conda init bash
# ignore the errors

# Add the functions to bash. This activates the conda environment called
"base".
hugo@alex ~/t [env]$ source ~/.bashrc
sh: dirname: command not found
sh: dirname: command not found
(base)

# Deactivate the environment.
(base) conda deactivate

### Actual and expected behaviour ###

The prompt is now empty. The expected result is that the "(base)" string is
prefixed to the prompt when activating an environment (or replaced when
switching environments). "base" is the default environment. The string
should be removed when the environment is deactivated.

However, instead, upon activating an environment, the entire prompt is
replaced with "(base)". Deactivating the environment removes the string,
resulting in an empty prompt.

### Cause ###

The cause of the disappearing prompt is due to an interplay between conda
and guix and bash. Normally, "conda" is either a binary executable, or a
bash function, but in guix it can also be a bash script.

What normally happens after installing conda is:
- The user calls "conda init bash". This is the binary executable "conda",
which adds the "conda" bash function to ~/.bashrc. This is only done once
for each user, after which the shell is restarted.

What normally happens in a user session is:
- The shell implicitly calls "conda activate base" through ~/.bashrc, or
the user calls it explicitly (usually not with "base", but with their own
environment). This "conda" is the conda bash function.
- The conda bash function calls the conda binary, passing PS1 (and other
variables).
- The conda binary uses PS1 to construct a new string to be used as a
prompt, and returns that to the conda bash function.
- The conda bash function uses the output from the conda binary to set PS1.
It also makes a backup of PS1 in CONDA_PS1_BACKUP.

But in guix, the conda binary is renamed to conda_real, and is replaced
with [by?] the conda bash script. Therefore this happens:
- The conda shell function is called through "conda activate base".
- The conda shell function calls the conda bash script. The conda bash
script eats PS1 because it is non-interactive.
- The conda bash script calls the conda_real binary, specifying an empty
PS1.
- The conda_real binary adds the "(base)" prefix to the empty string and
returns the string to the conda bash script.
- The conda bash script returns the string to the conda bash function.
- The conda bash function sets PS1 to the new string, which is just
"(base)".

### Workaround ###

My personal workaround is to skip the "conda init bash" step entirely, but
do something equivalent manually:
- Call "conda shell.bash hook" and store the output in ~/.
conda_shell.bash_hook.sh .
- Replace the "$CONDA_EXE" at the top of the file with
"${HOME}/.guix-profile/bin/conda"; otherwise it contains an absolute path,
including a guix hash. (This is another problem, I now realize.)
- Add "--norc -i" to every "$CONDA_EXE" call in that file.
- Add ". ~/.conda_shell.bash_hook.sh" to ~/.bash_profile (has to be
.bash_profile and not .bashrc because otherwise it would be called from the
conda bash script, leading to recursion).

The crux is that now it calls the conda bash script (created by guix) with
the -i flag (interactive mode), which prevents PS1 from being eaten. The
--norc prevents .bash_profile from being read again, thereby preventing
recursion.

### Proper Solution ###

I can see two possible proper solutions; but I cannot vouch for the
feasibility in guix.

1) Use another shell than bash for the executable-replacing-script of the
conda package. Only bash eats PS1, other POSIX-compliant shells do not;
e.g. dash. Is it possible to use another shell than bash for guix packages?

2) "conda shell.bash hook" uses the contents of /etc/profile.d/conda.sh to
create the conda bash function. Perhaps it would be possible to patch that
file to include the "--norc -i" flags. Maybe this would also require
placing the "conda init" output in ".bash_profile"
 instead of ".bashrc", not sure.

### Disclaimer ###

My ultimate goal is to ditch conda altogether and use guix instead.
However, it will take a while to convince everyone. In the meantime it
would help to have conda running well. That would also make it easier to
attract conda package maintainers.

Cheers,
Hugo