Building better software, one stack at a time.

Programming

Python Virtual Environments Explained: venv, pip and requirements.txt for Beginners

Python Virtual Environments Explained: venv, pip and requirements.txt for Beginners

You install a package, run your script, and everything works. A month later you open the same project and Python greets you with a wall of red text: ModuleNotFoundError, or worse, a version conflict that makes no sense. Nine times out of ten the culprit is not your code. It is where your packages ended up.

By default, pip install drops everything into one shared pile of libraries used by every Python project on your machine. Virtual environments give each project its own pile. They take about thirty seconds to set up, and they will save you hours of confusion later.

The trouble with installing everything globally

When you install a package without a virtual environment, it lands in the same folder as every other package that Python knows about. With one project on your laptop, that is fine. With four, it becomes a problem.

Suppose one project depends on an older release of a web framework, and a newer project needs the current one. There is only one copy installed, so one of them will break. You upgrade for project B and project A stops working. Downgrade for A and B falls over. This is the point at which people start blaming Python rather than their setup.

There is a second, quieter problem: you lose track of what a project actually needs. Looking at a shared package list tells you that something is installed, but not which version the project was built against, and not which packages were pulled in as dependencies of something else.

On macOS and most Linux distributions, there is a third reason to be careful. The Python that ships with the operating system is used by system tools. Modern distributions now refuse to let pip write to it at all, producing an externally managed environment error. That message is not a bug — it is the system protecting itself. The answer is a virtual environment.

What a virtual environment actually is

Strip away the jargon and a virtual environment is just a folder. When you create one, Python sets up a directory containing its own interpreter, its own pip, and its own place to store packages. When the environment is active, the words python and pip in your terminal point at that folder instead of the system-wide installation.

Nothing is copied from your other projects. Nothing leaks out. If the environment becomes a mess, you delete the folder and build a fresh one — a genuinely useful escape hatch.

Creating your first environment

The standard tool ships with Python itself, so there is nothing to install. From your project folder:

  1. Open a terminal and move into your project directory.
  2. Run python -m venv .venv (use python3 if python is not recognised).
  3. Activate it. On Windows: .venv\Scripts\activate. On macOS and Linux: source .venv/bin/activate.
  4. Check the prompt. You should see (.venv) at the start of the line. That is your confirmation.
  5. When you are finished, run deactivate to step back out.

Naming the folder .venv is a convention rather than a rule, but it is a good one: the leading dot signals that it is not source code, and the name is unlikely to clash with anything else.

Do not commit it

A virtual environment contains thousands of files, many of them binaries built for your specific operating system. It should never go into Git. Add a line containing .venv/ to your .gitignore file before your first commit, and your collaborators will thank you.

Installing packages with pip

With the environment active, pip behaves exactly as before — it just writes somewhere else. The safest habit is to call it through Python, which guarantees you are using the interpreter tied to your active environment:

python -m pip install requests

To see what is installed, use python -m pip list. To inspect a single package and its dependencies, python -m pip show requests is more informative than most people expect.

Version pinning is worth learning early. Writing python -m pip install "requests==2.31.0" installs one exact release rather than whatever is newest. For a quick experiment that does not matter. For anything you intend to deploy, it does.

A note on sudo

If pip refuses to install and suggests adding sudo, stop. Running pip with administrator rights is how people break their system Python. Activate the environment instead, or create one if you have not.

requirements.txt: your environment on paper

Because the environment folder is not shared, you need another way to tell someone — or your future self — what the project needs. That is what a requirements file is for.

With the environment active, run:

python -m pip freeze > requirements.txt

This writes every installed package with its exact version into a plain text file. To rebuild the same environment elsewhere, create and activate a fresh virtual environment, then run python -m pip install -r requirements.txt. Within minutes you have a working setup, no guesswork involved.

Freeze or hand-write?

A freeze captures everything, including packages you never asked for directly. That is excellent for reproducibility and noisy for reading. Hand-writing a short list of top-level packages — the ones you actually chose — gives a clearer picture but does not pin versions.

A common compromise is to commit a pinned requirements file for reproducibility and keep a short comment at the top explaining which packages are the direct dependencies. When you deliberately add or remove a package, regenerate the file rather than editing it by hand.

Habits that keep projects tidy

  • Create one environment per project, inside the project folder.
  • Activate it before installing anything, and glance at the prompt to confirm.
  • Add the environment folder to .gitignore immediately.
  • Commit requirements.txt and update it whenever the dependency list changes.
  • Pin versions for anything you plan to run on another machine.
  • Recreate rather than repair: if an environment gets strange, delete the folder and start again.
  • Never install project packages with sudo.

Your next project, start to finish

Make a folder. Run python -m venv .venv. Activate it. Install what you need with python -m pip install. Save the list with python -m pip freeze > requirements.txt. Write code, safe in the knowledge that nothing you install can disturb another project on the same machine.

That is the whole routine, and it is worth repeating until it becomes automatic. The first time a colleague clones your repository, runs a single install command and gets a working environment without asking you a single question, the effort pays for itself.

Photo: Innovalabs / Pixabay

Related Posts

A Beginner's Guide to UK GDPR for Web Developers: Cookies, Consent and Data Requests

Web Development

A Beginner's Guide to UK GDPR for Web Developers: Cookies, Consent and Data Requests

Practical UK GDPR guidance for small websites: how cookie consent really works, what to record, and how to answer...

How to Prepare Your E-commerce Site for the UK Christmas Shopping Rush

Web Development

How to Prepare Your E-commerce Site for the UK Christmas Shopping Rush

A practical guide to load testing, caching, payment limits, stock messaging and calm launch freezes so your shop...