Set up a venv and avoid breaking the system Python

short · 25 min · Objective 4.3

Task

Create a virtual environment, install a dependency into it, and record it reproducibly -- and see the guard that now stops you damaging the system interpreter. Then meet the KeyError-versus-None distinction that crashes scripts reading other people's JSON.

Steps

  1. Confirm which Python you have: python3 --version, which python3, and note whether a bare python even exists.
  2. Try to install into the system Python: pip3 install requests. On a recent system observe the externally-managed-environment refusal, and explain what damage it is preventing.
  3. Create a virtual environment: python3 -m venv ~/.venvs/lab, activate it, and confirm python and pip now point inside it.
  4. Install a dependency into the venv and confirm it imports.
  5. Record it: pip freeze > requirements.txt, then rebuild the environment on a fresh venv with pip install -r requirements.txt and confirm it matches.
  6. Write a tiny script demonstrating the dict trap: d["missing"] raises KeyError and stops the script, while d.get("missing") returns None. Show both against a small parsed-JSON structure.
  7. Guard an importable module with if __name__ == "__main__": and prove that importing it does not run its work.

Verify

source ~/.venvs/lab/bin/activate
python -c 'import sys; print(sys.prefix)' | grep -q '.venvs/lab' && echo "inside the venv"
python -c 'import requests; print("import ok")'
python - <<'PY'
d = {"a": 1}
try:
    d["missing"]; print("no error -- wrong")
except KeyError:
    print("KeyError as expected")
print("get returns:", d.get("missing"))
PY

sys.prefix pointing inside the venv is the confirmation that you are isolated from the system Python. The KeyError versus None output is the distinction behind a large share of crashes in scripts that read JSON they did not produce.

Notes

sudo pip install into the system Python can overwrite a library dnf itself depends on, with no record in the RPM database, and the breakage surfaces later during an unrelated update. PEP 668's refusal is a guard, not an obstacle -- the flag that defeats it is named --break-system-packages for a reason.