2025-10-11, 2025-10-12
Using Virtual Environments in Python
You wouldn't believe
how long it took me to make this image in the
GIMP.
Hi! This is an anti-slop blog that just presents some simple information without ads and a tremendous backstory about how my grandfather used Python back in the day or whatever. (He used FORTRAN and punch cards, for the record, but already I digress.)
And here I'm using the Zsh shell, but a generally-compatible one like Bourne or Bash will work. Check out the official instructions for other shells or tons more detail on this topic.
Let's go!
What is a Virtual Environment?
It's a place you can install Python programs and libraries that are
isolated from the rest of the system. So you can pip install things
and not mess anything up elsewhere. A common use case is that you want
to run someone's Python program and install its related packages but
don't want to pollute your system with all these unmanaged tools and
dependencies.
On Arch Linux (BTW) and others, this is the recommended way to install Python packages when they're not part of the official package manager.
There are four steps:
- Create the virtual environment (one-off step per environment).
- Activate it before use.
- Do things in it.
- Deactivate it when done.
You only have to create each environment once. After that it's a matter of activating it when you need it and deactivating it when you're done.
And you can have as many virtual environments as you want scattered around in various directories.
Creating the Virtual Environment
The virtual environment will live in a directory. You specify the
directory when creating it. Here I create one in a directory called
fooenv out of my current directory:
python -m venv fooenv
Or you can use a full path or whatever.
Activating It
Switch into the directory and then source the shell script
bin/activate.
cd fooenv
. bin/activate # or "source bin/activate" if you prefer
The shell prompt will change to have a prefix with the name of the virtual environment in it; this is an in-your-face reminder that you're "inside" the virtual environment.
All the
activatescript effectively does (besides change your prompt and a couple other things) is set yourPATHso that when you runpythonorpipit uses the one in the environment'sbin/directory instead of your system version. Runwhich pythonand see!
Do Things In It
Now you can run pip install and install things that are only in this
virtual environment. And when you run python you can see them.
Have fun!
Deactivate It
When you're done having fun, you can get back to the real world by running this:
deactivate
It's a secret function that the bin/activate script created. This gets
you back to the non-virtual environment where everything is real and
wholesome again. Run which python and see!
(You can also just exit the shell. That effectively deactivates it.)
Deleting It
If you're done with the virtual environment and want to get rid of it, just carefully delete the directory that holds it. It will be as if it never existed [spooky ghost noises].
Bonus: Running from a Shell Script
What if you have a shell script and you want to call something in that virtual environment? Well, you just have to do the same steps.
- Create the environment somewhere that's relatively permanent. Maybe
in a
~/.local/venvs/directory where you keep them all. - In your script, run
bin/activatein that directory. - In your script, do fun things.
- In your script, just exit when done. Or run
deactivatethen exit, either way.
Here's an example script that prints out the path to python in the
virtual environment:
# foo.sh
. /home/beej/fooenv/bin/activate # Change to your path
which python
deactivate
When I run that I get:
$ sh foo.sh
/home/beej/fooenv/bin/python
Bonus II: Running Python Scripts Directly
HT to IrgndSonShreck for this one:
If you specify the path to python in the virtual environment, even
if it's not activated, you'll run it inside the virtual environment.
So if I have a Python program and I need it to run in the environment, I can do this:
/home/beej/fooenv/bin/python foo.py
No need to activate.
I dug around a bit to see how Python knows that you've run it from the virtual environment in this way, since
VENV/bin/pythonis just a symlink to the system Python. Apparently there's not a standard way to do this. Linux has you look at the/proc/PID/exesymlink to determine the path to the executable, and other systems have other methods.In any case, Python figures out where it's being run from and then tries to figure out if it's a virtual environment somehow. I suspect, but have not verified, this involves it looking for the
pyvenv.cfgfile that was created when the environment was made.
There's also nothing stopping you from putting the full path to the venv Python in a shebang in your script:
#!/home/beej/fooenv/bin/python
import sys
print(sys.prefix)
When run, this will always use the virtual environment. So for me, it outputs:
/home/beej/fooenv
But that's all pretty fragile and ugly. Though there's not any consensus I can find, people either do that or wrap the thing up in a shell script, it seems.
And one more related thing. If you want to shebang Python code that will run in the real environment if you're in it and also run in the virtual environment if you're in that, use this (which you should be generally using anyway):
#!/usr/bin/env python
import sys
print(sys.prefix)
Here's an example of me running that outside and inside the virtual environment:
$ ./foo.py
/usr
$ . fooenv/bin/activate
(fooenv) $ ./foo.py
/home/beej/fooenv
So that Python code will just run in whatever environment you're in at the time.
Bonus III: Using a Third-Party Tool
HT to Stephen
Illingworth
for this one. He suggests a tool called
uv to streamline this entire
process and make your life easier.
The README has a pretty impressive list of highlights.
I've not tried it, but it definitely looks worth checking out. And I'm sure there are many others.
All the