A virtual environment has an interpreter boundary
A Python virtual environment is selected by the interpreter that runs your program. Learn how pyvenv.cfg, interpreter prefixes, shell activation, and python -m pip reveal which environment owns installed packages.
A directory becomes an environment through its interpreter
A virtual environment is not a second operating system or a shell trick. It is a directory built around a Python installation, with its own interpreter entry point and package directories. During creation, venv writes pyvenv.cfg with a home setting that points to the Python installation used as the base. It also creates a bin directory on POSIX systems, or Scripts on Windows, and a site-packages location for packages. The environment’s interpreter uses this configuration to identify its relationship to the base installation. [1]
This distinction matters when a project has several Pythons available. A prompt label such as (.venv) is only a clue about shell configuration. The interpreter actually executing a script determines its import paths and package context. If a service manager launches /srv/app/.venv/bin/python directly, the service uses that interpreter whether or not an interactive shell has ever been activated.
Read the prefixes as runtime evidence
The useful runtime comparison is sys.prefix against sys.base_prefix. In an active virtual environment, sys.prefix and sys.exec_prefix identify the environment, while sys.base_prefix and sys.base_exec_prefix identify the base Python. A difference between sys.prefix and sys.base_prefix is sufficient to recognize the environment according to Python’s documentation. The supplied snippet prints the executable path and both prefixes, then reports whether they differ. Its output is evidence about the running process, not a guess based on the current directory.
The executable path adds a practical check: it shows which Python binary the process resolved. The prefix comparison answers a different question, namely whether that interpreter is running within a virtual environment. These values can help diagnose a common mismatch: a developer installed a dependency into one interpreter but launched the application with another. The import error is then consistent with both operations succeeding—the package exists, but in the other interpreter’s package directory.
import sys
print(sys.executable)
print(sys.prefix)
print(sys.base_prefix)
print("venv:", sys.prefix != sys.base_prefix)
Activation edits command lookup
A POSIX activation script prepends the environment’s bin directory to PATH. That changes which executable a shell finds when someone types python, and makes installed command-line scripts available without their full paths. Activation does not place the process inside a kernel-enforced sandbox, nor does it make the environment impossible to access from other interpreters. It is shell configuration that improves command selection.
Activation is optional. Running the environment’s Python by full path selects it directly, and scripts installed into the environment are designed to point to its interpreter in their shebang. The VIRTUAL_ENV variable is set by activation, but it is not a reliable test for whether a process uses a virtual environment: direct invocation needs no activation and can still use the environment’s interpreter. When debugging, inspecting the interpreter and prefixes is more direct than trusting a prompt or environment variable.
Bind installation to the Python you will run
Installation tools such as pip use the current environment when invoked from within it. A robust habit is to run python -m pip install package with the same python command that will run the application. The module invocation binds pip to that interpreter, so an unactivated shell can use .venv/bin/python -m pip, while an activated one can use python -m pip. A bare pip command depends on PATH resolution and can point somewhere else.
Consider a terminal where python resolves to .venv/bin/python but pip resolves to /usr/bin/pip. Installing through that bare command may update the base environment, while the application keeps failing to import the dependency. Invoking python -m pip makes the relationship explicit: whichever interpreter was selected loads its pip module and targets that environment. Recreating the environment from a dependency specification is also safer than copying or moving it, since installed scripts can contain absolute interpreter paths. The boundary is therefore concrete: choose the interpreter first, and package visibility follows it.