Path re-ordering in sub-shell with activated environment
-
[x] I am using Fish shell version 3.1 or higher.
-
[x] I am using Python version 3.8 or higher.
-
[x] I have searched the issues (including closed ones) and believe that this is not a duplicate.
-
[x] If related to a plugin, I prefixed the issue title with the name of the plugin.
-
OS version and name: Linux Mint 22.1
-
Fish shell version: 4.0.2
-
VirtualFish version: 2.5.9
-
Python version: 3.12
Issue
When I open a sub-shell from fish in which a virtual environment was already activated, then it stays activated (which is intended) but the PATH in the sub-shell changes due to the ordering of $fish_user_paths.
Here is an example:
# normal fish shell (without plugins)
> echo $VIRTUAL_ENV
> echo $fish_user_paths
/home/user/.local/bin
> echo $PATH
/home/user/.local/bin /usr/local/bin ...
> vf activate main
> echo $VIRTUAL_ENV
/home/user/.virtualenvs/main
> echo $fish_user_paths
/home/user/.local/bin
> echo $PATH
/home/user/.virtualenvs/main/bin /home/user/.local/bin /usr/local/bin ...
# everything works so far as intended
# now start a sub-shell
> fish
>> echo $VIRTUAL_ENV
/home/user/.virtualenvs/main
# the virtualenv is already activated (which makes sense)
>> echo $fish_user_paths
/home/user/.local/bin
>> echo $PATH
/home/user/.local/bin /home/user/.virtualenvs/main/bin /usr/local/bin ...
# but the order has switched because fish prepended $fish_user_paths to PATH
In the sub-shell, tools from .local/bin will be found before the virtualenv local ones.
While I am not sure if this is a bug or not, it seem strange that the behaviour would change between shells.
I at least would have expected the PATH to have the same order in the sub-shell, if the environment comes already activated.
This problem also occurs if one opens tmux in their shell configuration with an already activated environment (e.g. due to auto_activation plugin) because by then, fish will have started twice (which is how I stumbled upon this).
I could imagine virtualfish could append to $fish_user_paths instead of PATH or it could re-order the virtualenv directory if it sees it further back in PATH.
Also, deactivating and then re-activating the environment in the sub-shell also seems to restore the correct PATH ordering.
Or is this intended behaviour?
Thank you
Also, small addition:
With the compat_aliases plugin installed that normally provides the deactivate function (and does in the parent shell), it does not provide the deactivate function in the sub-shell even though the environment is activated. (I thought this might be related)
I do not think I have ever used a sub-shell, so the short answer to your question is, unfortunately, I don’t know.
So what I did until now to fix this was to add the following to my config.fish:
if set -q VIRTUAL_ENV; and contains (basename $VIRTUAL_ENV) (vf ls)
set -l VF_ENV_NAME (basename $VIRTUAL_ENV)
vf deactivate
vf activate $VF_ENV_NAME
end
Basically, cleanly activating the environment again, if the shell came with an already activated virtual environment.
I just saw that vf deactivate does not work correctly in the sub-shell: (which means my solution above also does not work correctly)
> vf activate main
> echo $VIRTUAL_ENV
/home/user/.virtualenvs/main
> echo $PATH
/home/user/.virtualenvs/main/bin /home/user/.local/bin /usr/local/bin ...
> fish
>> echo $VIRTUAL_ENV
/home/user/.virtualenvs/main
>> echo $PATH
/home/user/.local/bin /home/user/.virtualenvs/main/bin /usr/local/bin ...
>> vf deactivate # succeeds with exit code 0
>> echo $VIRTUAL_ENV
>> echo $PATH
/home/user/.local/bin /home/user/.virtualenvs/main/bin /usr/local/bin ...
# still in path and wrong order ...
Update: My temporary work-around in config.fish now looks like this:
if set -q VIRTUAL_ENV; and contains (basename $VIRTUAL_ENV) (vf ls)
function _vf_clean_remaining_virtualenv_paths --on-event virtualenv_did_deactivate
if test -n "$VIRTUALFISH_HOME"
set new_path
for p in $PATH
if not string match -q "$VIRTUALFISH_HOME/*" $p
set new_path $new_path $p
end
end
set PATH $new_path
end
end
set -l VF_ENV_NAME (basename $VIRTUAL_ENV)
vf deactivate
vf activate $VF_ENV_NAME
end
Thank you for the follow-up and the details about your successful workaround. If you find a way to resolve this in the form of a pull request, I would be happy to review and merge it.
I'll see what I can manage or if I find the time for it 👍
Update: There are two problems:
- Fish rebuilds
fish_user_pathsalways on shell startup (you cannot persist it withset -gxfor sub-shells - also, you cannot just persist that part of the path as a universal variable because it would influence other shells) - Fish moves the parts in
fish_user_pathsto the front ofPATHon shell startup (meaning that what was originally first inPATHmay no longer be in a sub-shell
The only way in which I can imaging it could work is, to basically check in virtual.fish whether an virtual environment is already set and if it was set by virtualfish and then, move the _VF_EXTRA_PATH to the front of fish_user_paths again.
I am pretty sure I could build that.
Is that something you would accept for virtualfish or are there use cases you could imagine where this would break something else?
Fair question. The honest answer is that I don’t keep VirtualFish’s code persistent in my brain’s version of RAM, so without an exhaustive reloading of the code from ”disk” into my neurons, I don’t have a clear picture of what could be affected by such a change.
That said, we can try it, test it as thoroughly as we can, and then see if any issues arise. 😊