Warm-up · Activity 1 of 7
// I5.4 · ~30 min · Intermediate
Virtual environments and pip
By the end you can give each project its own virtual environment, install packages into it and record them so anyone can rebuild it.
You will be able to
- Create, activate and deactivate a virtual environment, and tell whether Python runs inside one
- Install and remove packages with python -m pip in the active environment, pinning a version when it matters
- Record dependencies in requirements.txt, reinstall them with -r, and keep .venv itself out of Git
Predict · Activity 2 of 7
Predict it before reading on. This creates an environment called demo and asks its interpreter for sys.prefix (Linux or macOS). What does it print?
import subprocess import tempfile import venv from pathlib import Path with tempfile.TemporaryDirectory() as tmp: env = Path(tmp) / "demo" venv.create(env, with_pip=False) python = env / "bin" / "python" out = subprocess.run([str(python), "-c", "import sys; print(sys.prefix)"], capture_output=True, text=True) print(Path(out.stdout.strip()).name)Practice · Activity 3 of 7
Fill in the standard-library module that creates a virtual environment in the folder .venv.
python -m ____ .venvpython -m .venvPractice · Activity 4 of 7
Match each shell command to what it does.
Practice · Activity 5 of 7
The prompt shows (.venv). You run python -m pip install requests. Where does requests go?
Brain teaser · Activity 6 of 7
Puzzle. An environment made with with_pip=False tries to import pip (Linux or macOS). What does it print?
import subprocess import tempfile import venv with tempfile.TemporaryDirectory() as tmp: venv.create(tmp, with_pip=False) result = subprocess.run([tmp + "/bin/python", "-c", "import pip"], capture_output=True) print(result.returncode)Apply · Activity 7 of 7
Small task. Write setup_env.py for a project folder: it creates .venv with pip using the venv module unless .venv/pyvenv.cfg already exists, then prints the activate command for the platform it runs on. Run it twice on your machine: the second run must not recreate the environment.
Check your work against this list
Build it yourself
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
Look inside a new environment
This program asks whether it runs in a virtual environment, then creates one in a temporary folder, which is what python -m venv .venv does, but without pip so it is quick. It lists the keys in pyvenv.cfg and runs the new environment's interpreter to ask the same question. Run it on your machine with the system Python, then again with an activated environment, and compare the first line.
main.py
import subprocess
import sys
import tempfile
import venv
from pathlib import Path
def in_venv() -> bool:
"""True when this interpreter runs from a virtual environment."""
return sys.prefix != sys.base_prefix
def env_python(env: Path) -> Path:
"""The interpreter inside a virtual environment."""
if sys.platform == "win32":
return env / "Scripts" / "python.exe"
return env / "bin" / "python"
print("running in a venv:", in_venv())
with tempfile.TemporaryDirectory() as tmp:
env = Path(tmp) / ".venv"
# python -m venv .venv does the same; with_pip=False skips installing pip.
venv.create(env, with_pip=False)
config = (env / "pyvenv.cfg").read_text()
keys = [line.split("=")[0].strip() for line in config.splitlines() if "=" in line]
print("pyvenv.cfg keys:", sorted(keys))
check = subprocess.run(
[str(env_python(env)), "-c", "import sys; print(sys.prefix != sys.base_prefix)"],
capture_output=True,
text=True,
check=True,
)
print("new env's python in a venv:", check.stdout.strip())
Run it with
python main.pyOutput
running in a venv: False
pyvenv.cfg keys: ['command', 'executable', 'home', 'include-system-site-packages', 'version']
new env's python in a venv: True- The first line is False with the system Python and True when you start the program from an activated environment.
- home and executable in pyvenv.cfg point back to the Python the environment was built from.
- include-system-site-packages is false by default, so the environment sees none of the system's packages.
- The new interpreter reports True without any activation: running it by its full path is enough.
Exercises
Exercise 1 of 2
Where am I running?
Complete two helpers. in_venv() returns True when the running interpreter is inside a virtual environment, using the check the venv docs recommend. env_python(env, platform) returns the path of the interpreter inside the environment folder env: Scripts/python.exe when platform is "win32", otherwise bin/python. Run it on your machine with and without an activated environment and watch the first line change.
This exercise needs Python on your computer (the browser version cannot run it). The files and commands are below.
Hints
Hint 1
The venv docs: it is sufficient to check sys.prefix != sys.base_prefix.
Hint 2
Compare the platform argument, not sys.platform, so the tests can ask about Windows on any machine.
Hint 3
if platform == "win32": return env / "Scripts" / "python.exe"
Show a solution
One way to solve it. Yours can look different and still pass the checks.
import sys
from pathlib import Path
def in_venv() -> bool:
"""True when this interpreter runs from a virtual environment."""
return sys.prefix != sys.base_prefix
def env_python(env: Path, platform: str = sys.platform) -> Path:
"""The interpreter inside the virtual environment env, for this platform."""
if platform == "win32":
return env / "Scripts" / "python.exe"
return env / "bin" / "python"
if __name__ == "__main__":
print("in a venv:", in_venv())
print("interpreter:", env_python(Path(".venv")))
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
import sys
from pathlib import Path
def in_venv() -> bool:
"""True when this interpreter runs from a virtual environment."""
# Compare the two prefixes the venv docs name.
return False
def env_python(env: Path, platform: str = sys.platform) -> Path:
"""The interpreter inside the virtual environment env, for this platform."""
# Windows ("win32") keeps it somewhere else.
return env / "bin" / "python"
if __name__ == "__main__":
print("in a venv:", in_venv())
print("interpreter:", env_python(Path(".venv")))
test_main.py
import sys
from pathlib import Path
from unittest.mock import patch
import main
def test_in_venv_true():
"""in_venv() is True when sys.prefix differs from sys.base_prefix"""
with patch.object(sys, "prefix", "/project/.venv"), patch.object(sys, "base_prefix", "/usr/local"):
assert main.in_venv() is True, "the prefixes differ, so this is a virtual environment"
def test_in_venv_false():
"""in_venv() is False when both prefixes are the same"""
with patch.object(sys, "prefix", "/usr/local"), patch.object(sys, "base_prefix", "/usr/local"):
assert main.in_venv() is False, "the prefixes are equal, so this is not a virtual environment"
def test_posix_path():
"""On Linux and macOS the interpreter is bin/python"""
for platform in ("linux", "darwin"):
got = main.env_python(Path(".venv"), platform)
assert got == Path(".venv", "bin", "python"), f"{platform}: got {got}"
def test_windows_path():
"""On Windows the interpreter is Scripts/python.exe"""
got = main.env_python(Path(".venv"), "win32")
assert got == Path(".venv", "Scripts", "python.exe"), f"win32: got {got}"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 2 of 2
Create an environment from Python
make_env(path) should create a virtual environment at path with the venv module, without pip so it is fast, and return the path of its interpreter. The starter only computes the path. The tests create environments in temporary folders and run the returned interpreter to confirm it reports sys.prefix != sys.base_prefix. Run it locally: it leaves a .venv in the current folder.
This exercise needs Python on your computer (the browser version cannot run it). The files and commands are below.
Hints
Hint 1
venv.create(path) does what python -m venv path does on the command line.
Hint 2
Pass with_pip=False: installing pip takes several seconds and the tests do not need it.
Hint 3
Call venv.create(path, with_pip=False) as the first line of make_env, before the return.
Show a solution
One way to solve it. Yours can look different and still pass the checks.
import sys
import venv
from pathlib import Path
def make_env(path: Path) -> Path:
"""Create a virtual environment at path and return its interpreter."""
venv.create(path, with_pip=False)
if sys.platform == "win32":
return path / "Scripts" / "python.exe"
return path / "bin" / "python"
if __name__ == "__main__":
python = make_env(Path(".venv"))
print("created:", python.exists())
print("run it with:", python)
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
import sys
import venv
from pathlib import Path
def make_env(path: Path) -> Path:
"""Create a virtual environment at path and return its interpreter."""
# Create the environment first, without pip, then return its interpreter.
if sys.platform == "win32":
return path / "Scripts" / "python.exe"
return path / "bin" / "python"
if __name__ == "__main__":
python = make_env(Path(".venv"))
print("created:", python.exists())
print("run it with:", python)
test_main.py
import subprocess
import tempfile
from pathlib import Path
import main
def test_creates_environment():
"""make_env writes pyvenv.cfg into the new folder"""
with tempfile.TemporaryDirectory() as tmp:
env = Path(tmp) / ".venv"
main.make_env(env)
assert (env / "pyvenv.cfg").exists(), "no pyvenv.cfg: call venv.create(path, with_pip=False)"
def test_returns_interpreter():
"""make_env returns the path of the new interpreter, and it exists"""
with tempfile.TemporaryDirectory() as tmp:
python = main.make_env(Path(tmp) / ".venv")
assert python.exists(), f"{python} does not exist"
def test_interpreter_is_in_the_venv():
"""The returned interpreter reports that it runs in a virtual environment"""
with tempfile.TemporaryDirectory() as tmp:
python = main.make_env(Path(tmp) / ".venv")
check = subprocess.run(
[str(python), "-c", "import sys; print(sys.prefix != sys.base_prefix)"],
capture_output=True,
text=True,
)
assert check.stdout.strip() == "True", f"{python} printed {check.stdout.strip()!r}{check.stderr}"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyCommon mistakes
Typing a shell command into Python
# Setting up the project
python -m venv .venv
What Python prints
SyntaxError: invalid syntaxWhy, and the fix
python -m venv .venv, source .venv/bin/activate and pip install are commands for the terminal (bash, zsh, PowerShell), not Python code. Typed into a .py file or the >>> prompt they are a syntax error. Run them at the shell prompt; from inside Python, use the venv module or subprocess.
Installed, but into another Python
import requests
print(requests.__version__)
What Python prints
ModuleNotFoundError: No module named 'requests'Why, and the fix
You installed requests, but into a different environment than the python running this file: perhaps pip belonged to another Python, or the environment was not activated in this terminal. Activate .venv, check with python -c "import sys; print(sys.prefix)", and install with python -m pip install requests so pip and python are the same interpreter.
A misspelled requirements file
import subprocess
import sys
subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirments.txt"], check=True)
What Python prints
ERROR: Could not open requirements file: [Errno 2] No such file or directory: 'requirments.txt'Why, and the fix
pip install -r reads the file you name, relative to the current folder, and stops if it is not there; here requirments.txt is missing an e. Check the name and run the command from the project folder. check=True then turns pip's non-zero exit status into CalledProcessError, so a script cannot carry on as if the install worked.
Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source
Exit ticket
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.