Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
What happens if I activate a python venv inside another venv?
What happens if I do
python3 -m venv venv1
python3 -m venv venv2
source venv1/bin/activate
source venv2/bin/activate
Which venv(s) am I now in?
1 answer
tl;dr
The second one; and deactivating it does not revert to the first.
Understanding virtual environment activation
Being "in" a virtual environment is just an illusion created by setting a few environment variables. Doing that is platform-specific, and therefore the scripts are as well.
But as a general description, the activation scripts provided by ordinary virtual environments (as created using python -m venv etc.) will:
python -m venv etc.) will:-
Ensure that a
deactivatecommand is available (the plain Windows CMD version provides a separatedeactivate.bat; others, including Powershell, define a new command) -
Remember the "base" settings of the environment variables it changes (this involves deactivating any currently active virtual environment!), so that
deactivatecan undo changes (it remembers them in additional, undocumented environment variables) -
Set
VIRTUAL_ENV_PROMPTto be the name of the environment's root folder (this is pre-computed when the environment is created) -
Set
VIRTUAL_ENVto be the path to the environment's root folder (this is pre-computed when the environment is created) -
Add
VIRTUAL_ENVto the start ofPATH -
Unset
PYTHONHOME(a mechanism for telling the Python interpreter a different place to look for standard library files; this would prevent the virtual environment's Python from starting up correctly) -
Unless disabled (by the user setting
VIRTUAL_ENV_DISABLE_PROMPT), modify the prompt (typically by settingPS1)
The scripts are designed so that the activation does not "nest" — activating a second virtual environment implicitly deactivates the first, and so deactivating after that returns to the original situation with no active virtual environment. This is simpler to implement (otherwise the scripts would need to remember a stack of previous environment variable values, and there is often limited space for these) and was apparently considered simpler to understand and just as useful.
Going without
Python itself doesn't actually care about this activation process, and in particular it doesn't check the VIRTUAL_ENV environment variable.
It detects and uses virtual environments by a completely different mechanism:
-
If
PYTHONHOMEis set, that overrides everything else — Python will setsys.prefixandsys.base_prefixto that value, and skip the rest of this process. -
If there is a config file called
pyvenv.cfgeither adjacent to the Python executable or in the parent folder, and that file passes some basic validation, it's used to setsys.base_prefix(according to a pre-calculated value stored in the config file when the environment is created) andsys.prefix(to the directory containing the config file). -
Otherwise, Python walks the directory tree backwards from its own location, looking for what it expects for the standard library (specifically, a
libfolder containingos.py).
Each virtual environment has its own Python executable: on Windows, this is a stub that redirects to the original Python, while Linux can just use a symbolic link (Python will be able to search starting from the link's path rather than the resolved path to the base Python).
Once sys.prefix and sys.base_prefix are determined (by the above process), Python can import the standard library site module and use it to finish configuring sys.path (in particular, adding a site-packages folder either from the virtual environment or from within the Python installation).
As a consequence, in general it is not necessary to activate virtual environments in order to use them[1].
The main idea behind activation is that it modifies your PATH, so that the python command uses the environment's Python, which causes the environment's installed libraries to be used. But you can equally well just specify that Python directly.
For example:
$ python -m venv example-venv
$ # The wrapper inside the virtual environment runs, which will
$ # use the virtual environment's Python, and therefore install
$ # in that environment:
$ example-venv/bin/pip install package-installation-test
$ # Which may also provide more wrappers, accessed similarly:
$ example-venv/bin/demo-example-package
$ # Explicitly running the virtual environment's Python allows
$ # access to what's installed there:
$ example-venv/bin/python -m example_package
(I created the package-installation-test package some time ago, specifically to do these sorts of diagnostics.)
-
Unless you have, for example, a shell script that cares about the
VIRTUAL_ENVvalue, or a script that does something likesubprocess.call(['python', '-c', 'pass'])— which should be usingsys.executableinstead of relying on path lookup. ↩︎

1 comment thread