Easily Activating Python Virtual Environments in the fish Shell
Easily Activating Python Virtual Environments in the fish Shell
My friend was complaining to me that uv
couldn’t activate its own Python virtual environments and instead required him to run the appropriate activation script in the shell manually. This is necessary because uv
runs as a child process of the shell and thus cannot affect the shell’s …
My friend was complaining to me that uv
couldn’t activate its own Python virtual environments and instead required him to run the appropriate activation script in the shell manually. This is necessary because uv
runs as a child process of the shell and thus cannot affect the shell’s (its parent’s) environment, including the various changes made by the activation script.
I decided to write a small fish shell function to do this instead, in order to save the repetitive typing of source
.venv/bin/activate.fish
. Taking inspiration from tools like Git, I decided to search upwards in the directory tree from the current directory and activate the first virtual environment it encountered.
Here is the resulting function:
function activate -d 'search for a Python virtual environment in the current directory and all parent directories and activate the first one encountered'
set current_dir (pwd)
set parent_dir (path dirname $current_dir)
while test $current_dir != $parent_dir # make sure we're not at the root
if test -e $current_dir/pyvenv.cfg
source $current_dir/bin/activate.fish
return
end
for f in $current_dir/*/pyvenv.cfg $current_dir/.*/pyvenv.cfg
source (path dirname $f)/bin/activate.fish
return
end
set current_dir $parent_dir
set parent_dir (path dirname $current_dir)
end
# one more iteration now that we've reached the root
# (why are you storing a virtual environment at the root of your filesystem?)
if test -e $current_dir/pyvenv.cfg
source $current_dir/bin/activate.fish
return
end
for f in $current_dir/*/pyvenv.cfg $current_dir/.*/pyvenv.cfg
source (path dirname $f)/bin/activate.fish
return
end
echo "couldn't find a virtual environment to activate" >&2
return 1
end
As noted in the code block header, I stored this as a file under my home directory in .config/fish/functions
, which makes it immediately available to any instances of fish I have running: simply running activate
will find the nearest virtual environment and activate it!
I have a bit of a suspicion that this code could be made more efficient somehow (but I don’t know exactly how), and, since this is just a quick script, there’s definitely a chance that I missed an edge case or two. However, I purposely used only fish built-ins, meaning that there are no external dependencies and it should run without any process-creation overhead or anything like that.
I hope this is useful to any fish and Python users out there!