Skip to content
elephantoo

pip & virtual environments

Lesson 16 of 38 15 min read

Create venvs, install packages with pip, requirements.txt, the externally-managed error, pipx and uv.


Python's standard library is big, but the real superpower is PyPI, the Python Package Index, with over half a million packages: requests for HTTP, pandas for data, Flask and FastAPI for web apps, pytest for testing. You install them with pip — and you install them into a virtual environment, so each project gets its own isolated set of packages. Every professional Python developer does this daily.

The problem virtual environments solve#

Without isolation, every package you install lands in one global location:

  • Project A needs Django 4.2, project B needs Django 5.1 — only one can be installed.
  • Upgrading a package for one project silently breaks another.
  • On Linux, installing into the system Python can break operating-system tools that depend on it.

A virtual environment ("venv") is just a folder containing a link to a Python interpreter plus its own private site-packages directory. Activate it and python and pip refer to that environment; deactivate it and you're back to normal.

Creating and activating a venv#

From your project folder:

Terminal
mkdir weather-app && cd weather-app
python3 -m venv .venv

This creates a .venv folder (the leading dot keeps it tidy; .venv is also what most editors look for). On Debian/Ubuntu, if you get "ensurepip is not available", install the venv module first with sudo apt install python3-venv. Fedora includes it by default.

Activate it:

Terminal
# Linux / macOS (bash, zsh)
source .venv/bin/activate

# Windows PowerShell
.venv\Scripts\Activate.ps1

Your prompt gains a (.venv) prefix, and python now points inside the folder:

Terminal
which python
python --version
Output
/home/you/weather-app/.venv/bin/python
Python 3.12.3

Inside an activated venv you can type plain python and pip — they're guaranteed to be the environment's versions. Leave it with deactivate.

You don't strictly need to activate: running .venv/bin/python script.py or .venv/bin/pip install ... uses the environment directly, which is handy in scripts and cron jobs.

Installing packages with pip#

Terminal
pip install requests              # latest version
pip install "requests==2.34.2"    # exact version
pip install "flask>=3.0,<4"       # version range
pip install --upgrade requests    # upgrade
pip uninstall requests            # remove

Always quote version specifiers so your shell doesn't interpret > and < as redirections.

Inspecting what's installed:

Terminal
pip list                  # everything in this environment
pip show requests         # details: version, dependencies, location
pip list --outdated       # what has newer versions
Output
Name: requests
Version: 2.34.2
Summary: Python HTTP for Humans.
...
Requires: certifi, charset_normalizer, idna, urllib3

Notice that installing requests also pulled in its dependencies (urllib3, certifi and friends). pip resolves and installs them automatically.

Once installed, a package is imported like any module:

Python
import requests

print(requests.__version__ >= "2.0")
Output
True

Tip: the name you pip install isn't always the name you import. pip install beautifulsoup4 → import bs4; pip install python-dotenv → import dotenv; pip install Pillow → import PIL. Check the package's documentation.

Recording dependencies: requirements.txt#

Your venv folder should never be committed to git or copied between machines — it contains machine-specific paths. Instead, record what is installed and recreate it anywhere:

Terminal
pip freeze > requirements.txt
requirements.txt
certifi==2026.7.22
charset-normalizer==3.5.2
idna==3.20
requests==2.34.2
urllib3==2.8.0

A teammate (or your deployment server) then runs:

Terminal
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

…and gets exactly the same versions. Add .venv/ to your .gitignore.

Many teams keep a short hand-written list of direct dependencies (requests, flask>=3) and generate the fully pinned list with a tool, or declare dependencies in pyproject.toml — you'll see that in the packaging lesson.

The "externally-managed-environment" error#

On Ubuntu 23.04+, Debian 12+, Fedora 38+ and recent Homebrew, running pip install against the system Python gives:

Output
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

This is a safety feature (PEP 668), not a bug. Your options, in order of preference:

  1. Use a virtual environment — this is the answer 99% of the time.
  2. For command-line tools you want everywhere (like black, httpie, ruff), use pipx: sudo apt install pipx (or sudo dnf install pipx), then pipx install ruff. Each tool gets its own hidden venv.
  3. For system-wide libraries, use the distro package: sudo apt install python3-requests.

Never "fix" it with sudo pip install or --break-system-packages on your main machine.

Always use python -m pip#

If you have several Pythons installed, pip might belong to a different one than python. Running pip through the interpreter removes the ambiguity:

Terminal
python3 -m pip install requests

Inside an activated venv, plain pip is fine — but python -m pip is a good habit, and it's what the official docs recommend.

Checking which environment you're in#

From Python itself:

Python
import sys

print(sys.prefix != sys.base_prefix)   # True inside a venv
print(sys.executable)
Output
True
/home/you/weather-app/.venv/bin/python

If imports fail with ModuleNotFoundError even though you "installed it", 9 times out of 10 you installed into one environment and ran another. Compare which python with pip --version — both should point inside your .venv. In VS Code, use Python: Select Interpreter and pick the .venv one.

Modern alternative: uv#

uv is a very fast, increasingly popular tool that replaces pip, venv, pip-tools and even installs Python versions. The concepts are the same:

Terminal
uv venv                       # create .venv
uv pip install requests       # pip-compatible commands, much faster
uv init my-project && cd my-project
uv add requests               # adds to pyproject.toml and installs
uv run main.py                # runs inside the project's environment

Poetry, PDM and Hatch are other project managers you'll meet in the wild. Learn venv + pip first — every other tool builds on the same ideas.

A typical project workflow#

Terminal
mkdir quotes && cd quotes
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install requests
pip freeze > requirements.txt
echo ".venv/" >> .gitignore

Then write code, git commit your source plus requirements.txt, and anyone can rebuild the environment in seconds.

Common mistakes#

  • Committing .venv to git — commit requirements.txt instead.
  • Installing with sudo pip — never needed with venvs, and it can damage the system.
  • Forgetting to activate the venv in a new terminal, then wondering where your packages went.
  • Moving or renaming a venv folder — venvs contain absolute paths. Delete it and recreate from requirements.txt instead.
  • Naming the venv after a package or putting your code inside .venv — keep code next to it, not in it.

What's next#

With packages and environments sorted, let's work with data that outlives your program: files and context managers.

Check your understanding

Quick quiz

0/3 answered
  1. 1.Why should every project have its own virtual environment?

  2. 2.On Ubuntu 24.04, pip install requests (outside a venv) fails with externally-managed-environment. What's the right fix?

  3. 3.What does pip freeze > requirements.txt do?

Finished reading?

Mark this lesson complete to track your progress.