Introducing Actifish: Easily Activating Python Virtual Environments in the fish Shell
Introducing Actifish: Easily Activating Python Virtual Environments in the fish Shell
Activating Python virtual environments can be surprisingly cumbersome, making a small but persistent annoyance in daily development. When a friend shared his frustrations about this—and about some limitations in existing tools like uv
and VirtualFish—I saw an opportunity to simplify things.
Activating Python virtual environments can be surprisingly cumbersome, making a small but persistent annoyance in daily development. When a friend shared his frustrations about this—and about some limitations in existing tools like uv
and VirtualFish—I saw an opportunity to simplify things.
In order to activate a Python virtual environment, you traditionally have to source the appropriate activation script in the shell manually. This is necessary because any external program 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.
My friend and I appreciate how VirtualFish makes virtual environments available globally, but were both looking for slightly more control over how those environments are created. I realized that the real pain point that VirtualFish solves for me isn’t creating the virtual environments—I’m perfectly happy to run a few one-time commands—it was the ease of activating the global environments that is the real win.
The Actifish Solution
I decided to write a small fish shell function to help with activating virtual environments, both in the current directory tree and from a global location. The code is available at https://github.com/scolby33/actifish.
Inspired by tools like Git, I decided to search upwards in the directory tree from the current directory and activate the first virtual environment it encountered.
For the global virtual environment functionality, I wanted configurability for the storage location of the actual environments, so I used a PATH-like variable to allow the user to set as many storage locations as desired.
Once the function is stored in a file at .config/fish/functions/activate.fish
, it is immediately available in any running instances of fish. Run activate
in any fish shell session, and it will locate and activate the nearest virtual environment automatically.
While there may be some room for optimization, the current implementation prioritizes simplicity and avoids external dependencies or process-creation overhead. And while there may be edge cases that I haven’t anticipated, I’m confident that Actifish’s use of only fish built-ins keeps it as efficient as it needs to be for the task at hand.
In the repo, there’s also a README with further instructions, particularly around the global environment functionality.
Take a few minutes and try Actifish out, and feel free to reach out on GitHub with any issues or suggestions. I hope this is useful to some fish and Python users out there!