Two Cooks, One Kitchen
Imagine two cooks sharing one kitchen in a hostel mess. Ramesh makes the breakfast poha, and his recipe needs the old spice mix his family has used for years. Sunita makes the evening biryani, and she needs the new, stronger version of the same brand. There is only one shelf. If Sunita replaces the old packet with the new one, Ramesh’s poha tastes wrong the next morning. The simple fix: give each cook their own shelf, with the exact spices their recipes need.
Python projects have the same problem. Projects use libraries (also called packages): code that other people wrote, such as requests for the web or Django for websites. You install them with a tool called pip. If every project installs into one shared Python, sooner or later two projects need different versions of the same library, and one of them breaks.
A virtual environment (often shortened to venv) is that private shelf. It is a folder inside your project that holds its own copy of pip and its own libraries. In this lesson you will create one, install libraries into it, save the list so a friend can rebuild it, and fix the errors almost every beginner meets.
Where does this lesson run? Most of it is terminal work on your own computer, so those boxes have no Try it yourself button. A few Python examples that inspect the environment do run in the playground. The playground is a special Python inside your browser, so its answers will look different from your computer’s.
Why Projects Need Their Own Libraries
Without virtual environments (one shared shelf):
Global Python
└── Django 4.2 <- Project A needs 4.2, Project B needs 5.1. Only one can win.
With virtual environments (one shelf per project):
canteen-app/ college-fest/
├── .venv/ ├── .venv/
│ └── Django 4.2 │ └── Django 5.1
└── app.py └── app.py
Isolation gives you three benefits:
- No conflicts. Each project keeps the versions it was built and tested with.
- A clean main Python. You do not fill your system with hundreds of libraries you used once.
- Easy sharing. You can write down exactly what the project needs, so it works the same on a friend’s laptop or on a server.
Look Before You Install: Which Python Is Running?
Before creating anything, learn to ask Python about itself. The sys module knows the version and the location of the Python program that is running your code. This one fact solves most environment problems: if the path is not the one you expect, you are using the wrong Python.
import sys
import platform
print("Python version:", sys.version.split()[0]) # for example 3.12.4
print("Running on:", platform.system()) # for example Windows, Darwin (Mac) or Linux
print("Python program:", sys.executable)
# On your computer, sys.executable is the full path of the python program
# that is running this script. Inside a venv it ends with .venv/bin/python
# (Mac/Linux) or .venv\Scripts\python.exe (Windows).
Step 1: Create a Virtual Environment
Open a terminal in your project folder. In VS Code, use Terminal → New Terminal, which opens in the current folder. Then run the built-in venv module. The last word, .venv, is the name of the folder it creates. .venv is the most common name, and VS Code recognises it automatically.
# Windows (PowerShell or Command Prompt), inside your project folder
cd Desktop\tiffin-orders
py -m venv .venv
# Mac or Linux, inside your project folder
cd ~/Desktop/tiffin-orders
python3 -m venv .venv
After a few seconds, a new .venv folder appears. It contains a small Python program, pip, and an empty place for libraries. You create it once per project.
Step 2: Activate It
Activating tells your terminal: “for now, when I type python or pip, use the ones inside .venv”. The command depends on your system and terminal:
# Windows PowerShell
.venv\Scripts\Activate.ps1
# Windows Command Prompt
.venv\Scripts\activate.bat
# Mac or Linux (Terminal)
source .venv/bin/activate
# Your prompt now starts with (.venv), for example:
(.venv) PS C:\Users\asha\Desktop\tiffin-orders>
# When you are done for the day
deactivate
The (.venv) at the start of the prompt is your sign that it worked. You must activate again every time you open a new terminal. Inside an active environment, python works on every system, even on Mac and Linux.
You can also check from Python itself. Inside a venv, sys.prefix (where this Python keeps its libraries) is different from sys.base_prefix (the main installation). The playground is not a venv, so it prints the second message:
import sys
def in_virtual_env():
# Inside a venv, sys.prefix points to the .venv folder,
# while sys.base_prefix still points to the main Python installation.
return sys.prefix != sys.base_prefix
print("sys.prefix: ", sys.prefix)
print("sys.base_prefix:", sys.base_prefix)
if in_virtual_env():
print("You are inside a virtual environment.")
else:
print("You are using the main (global) Python.")
Step 3: Install and Manage Packages with pip
pip downloads packages from the Python Package Index (PyPI), the official online store of free Python libraries. With your venv active, these are the commands you will use every week:
# Install the newest version of a package
python -m pip install requests
# Install one exact version
python -m pip install "Django==5.1.2"
# Upgrade a package
python -m pip install --upgrade requests
# See everything installed in this environment
python -m pip list
# Details of one package: version, summary, what it needs
python -m pip show requests
# Remove a package (it asks y/n before deleting)
python -m pip uninstall requests
Why python -m pip instead of just pip? The -m option means “run the pip that belongs to this python”. It removes any doubt about which Python receives the package. Plain pip also works inside an active venv, but python -m pip is the safer habit.
When you install requests, pip also installs the libraries that requests itself needs, called dependencies. That is why pip list shows more than you asked for. Your version numbers may be newer:
(.venv) $ python -m pip list
Package Version
------------------ ---------
certifi 2024.8.30
charset-normalizer 3.4.0
idna 3.10
pip 24.2
requests 2.32.3
urllib3 2.2.3
Your Python code can read the same list with importlib.metadata. This is useful when a program must check that a library is installed before using it. Run it here to see what the playground has:
from importlib import metadata
# Every installed package, with its version, sorted by name
packages = sorted(
(dist.metadata["Name"], dist.version) for dist in metadata.distributions()
)
print(f"{len(packages)} packages installed")
for name, version in packages[:10]:
print(f" {name}=={version}")
# Ask for one package by name
for wanted in ["pip", "requests"]:
try:
print(wanted, "version", metadata.version(wanted))
except metadata.PackageNotFoundError:
print(wanted, "is not installed here")
Step 4: Save the List with requirements.txt
You never share the .venv folder itself. It is large, and it only works on the computer where it was made. Instead you share a short text file, requirements.txt, that lists every package with its exact version. pip freeze prints that list, and the > symbol saves it to a file. Anyone can then rebuild the same environment with -r:
# Save the exact versions of everything installed
python -m pip freeze > requirements.txt
# Later, on another computer (or after deleting .venv):
py -m venv .venv # Windows
python3 -m venv .venv # Mac or Linux
# ...activate it, then:
python -m pip install -r requirements.txt
The == sign pins an exact version. This is the “exact spice mix” for your project. Here is how such a file looks, and how a script could read it:
# Step 1: create a sample requirements.txt (pip freeze writes files like this)
sample = """# Libraries for the college fest website
Django==5.1.2
requests==2.32.3
python-dotenv==1.0.1
"""
with open("requirements.txt", "w") as f:
f.write(sample)
# Step 2: read it back and understand each line
with open("requirements.txt") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue # skip empty lines and comments
name, version = line.split("==")
print(f"{name:<15} needs version {version}")
# Django needs version 5.1.2
# requests needs version 2.32.3
# python-dotenv needs version 1.0.1
Run pip freeze again every time you install or upgrade something, so the file stays correct.
Step 5: Keep .venv Out of Git
If you use Git (see the Git and GitHub lessons), tell it to ignore the environment folder. Create a file named .gitignore in the project folder with these lines:
# .gitignore
.venv/
__pycache__/
Your project then looks like this. Only the last three files go to GitHub:
tiffin-orders/
├── .venv/ <- not shared, never committed
├── .gitignore <- contains .venv/
├── requirements.txt <- shared: the list of libraries
└── orders.py <- your code
Step 6: Select the Interpreter in VS Code
VS Code must also know which Python to use, both for the Run button and for checking your imports. Press Ctrl + Shift + P (on a Mac, Cmd + Shift + P), type Python: Select Interpreter and choose the one that shows .venv in its path. VS Code usually suggests it as “Recommended”. After that, new terminals in VS Code activate the environment for you. If VS Code underlines import requests with a warning even though you installed it, the wrong interpreter is selected.
Real-Life Example: Sharing a Project with a Friend
Asha builds a small tiffin-order script that uses requests. Her friend Imran wants to help. Asha runs python -m pip freeze > requirements.txt and pushes the code, the .gitignore and the requirements file to GitHub. Imran downloads it, runs py -m venv .venv, activates it and runs python -m pip install -r requirements.txt. In two minutes his shelf matches hers exactly, without changing anything else on his laptop. When Asha later starts a second project with a newer requests, it gets its own .venv, and the tiffin project keeps working.
Many teams add a small check script like the one in the Practice Task below, so anyone can confirm their setup in one command.
Try these changes:
- Create two folders, make a venv in each, and install a different version of
requestsin each. Runpython -m pip show requestsin both. - Delete a
.venvfolder completely and rebuild it fromrequirements.txt. - Run the package-listing example above on your computer, once inside the venv and once outside, and compare the numbers.
Common Mistakes
- PowerShell refuses to activate. Windows blocks scripts by default, so
Activate.ps1fails. Allow local scripts for your own account once, or use Command Prompt withactivate.batinstead:
# The error on PowerShell:
.venv\Scripts\Activate.ps1 cannot be loaded because running scripts is disabled on this system.
# Fix: allow local scripts for your user account only (run once)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
- “pip is not recognised”. The
pipcommand is not on your PATH (the list of folders your terminal searches). Run pip through Python, which always works:
# The error:
'pip' is not recognized as an internal or external command
# Fix: run pip through Python instead
py -m pip install requests # Windows
python3 -m pip install requests # Mac or Linux
- Installing into the wrong Python. You installed a package, but your script says
ModuleNotFoundError. Usually the install went into the global Python while the script runs in the venv, or the other way round. Activate first, then check:
# Which Python and pip am I using right now?
where python # Windows (Command Prompt)
which python # Mac or Linux
python -m pip --version
# pip 24.2 from C:\Users\asha\Desktop\tiffin-orders\.venv\Lib\site-packages\pip (python 3.12)
# If the path does not mention .venv, activate the environment first.
- Committing
.venvto Git. It adds thousands of files and does not work on other computers. Add it to.gitignoreand sharerequirements.txtinstead. - Forgetting to activate in a new terminal. No
(.venv)in the prompt means you are using the global Python. Activate again, or let VS Code do it after selecting the interpreter.
Practice Task: Set Up a Clean Project
On your own computer, set up a project called tiffin-orders. Your finished project should have:
- A
.venvfolder created withvenv, and activated (you see(.venv)). - The
requestslibrary installed inside it. - A
requirements.txtmade withpip freeze. - A
.gitignorecontaining.venv/. - A
check_env.pyscript, built from the starter below, that prints the Python version, whether it runs inside a venv, and the installed version ofrequests.
You can run the starter in the playground too. There it will say that it is not inside a venv, and most likely that requests is not installed.
import sys
from importlib import metadata
# This is check_env.py. Run it inside your activated .venv on your computer.
# TODO 1: print the Python version (only the number, like 3.12.4)
# TODO 2: print "Inside a venv" or "Not inside a venv"
# (compare sys.prefix with sys.base_prefix)
# TODO 3: print the version of "requests", or
# "requests is not installed" if it is missing
Show the terminal commands
# Windows (PowerShell)
mkdir tiffin-orders
cd tiffin-orders
py -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install requests
python -m pip freeze > requirements.txt
Set-Content .gitignore ".venv/"
python check_env.py
# Mac or Linux
mkdir tiffin-orders
cd tiffin-orders
python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests
python -m pip freeze > requirements.txt
echo ".venv/" > .gitignore
python check_env.py
Show one possible check_env.py
import sys
from importlib import metadata
print("Python", sys.version.split()[0])
if sys.prefix != sys.base_prefix:
print("Inside a venv")
else:
print("Not inside a venv")
try:
print("requests", metadata.version("requests"))
except metadata.PackageNotFoundError:
print("requests is not installed")
# Inside your activated .venv you should see something like:
# Python 3.12.4
# Inside a venv
# requests 2.32.3
Key Takeaways
- A virtual environment is a private shelf of libraries for one project, so projects never fight over versions.
- Create it with
py -m venv .venv(Windows) orpython3 -m venv .venv(Mac/Linux), then activate it in every new terminal. - Use
python -m pip install,list,showanduninstallto manage packages. - Share
requirements.txtfrompip freeze, rebuild withpip install -r, and keep.venvin.gitignore. - When something is “not found”, check which Python is running:
sys.executable,sys.prefix != sys.base_prefix, and the interpreter selected in VS Code.