Python Fundamentals: Reproducible Environments and Project Setup
I standardized Python project setup on isolated virtual environments with pinned dependencies and a clean package layout. Per-project venvs eliminated dependency conflicts and made every script reproducible from a single requirements file.
Objective & Context
Global package installs cause version conflicts across projects. This lab establishes venv isolation and pinned requirements as the baseline for every later Python lab, ensuring deterministic runs on any machine.
Learning Objectives
- Create and activate an isolated venv per project so dependencies never leak between projects.
- Install packages with pip and pin the full dependency tree with pip freeze.
- Verify which interpreter and package set a shell is using with sys.prefix and pip list.
- Recreate an environment from requirements.txt on a fresh directory.
- Adopt a standard project layout (src/, requirements, .venv) reused across all later labs.
Environment & Prerequisites
Prerequisites
Python 3.11or newer installed; check withpython3 --versionvenvmodule available (python3 -m venv --help); bundled with standard CPythonpip23 or newer (python3 -m pip install --upgrade pip)- A POSIX or PowerShell shell and an empty scratch project directory
- Assumed skill: running commands from a terminal and editing text files
Step-by-Step Execution
1. Create and activate an isolated environment
python3.11 -m venv .venv && source .venv/bin/activateExpected output:
No output; the shell prompt gains a (.venv) prefix after activation.
What this teaches: venv creation is silent by design. Activation only rewrites PATH and the prompt, so verification is a deliberate separate step. Fresh-system note: on Debian/Ubuntu, python3 -m venv needs the split-out package (sudo apt install python3-venv); without it you get "No module named venv".
2. Pin dependencies
pip install requests && pip freeze > requirements.txtExpected output:
requirements.txt now lists requests plus pinned transitive deps (certifi, charset-normalizer, idna, urllib3).
What this teaches: pip freeze snapshots exact resolved versions including transitive dependencies, which is what lets another machine reproduce this environment. The pinned version numbers will differ from any example here depending on when you install.
3. Verify the interpreter and packages
python -c "import sys; print(sys.prefix)" && pip listExpected output:
/home/taki/project/.venv
Package Version
---------- -------
pip 23.3.1
requests 2.31.0
setuptools 68.2.2
What this teaches: sys.prefix reveals the active interpreter's home and pip list confirms the package set. Both must point inside .venv, never at the system Python. Version numbers and the exact package list vary by install date; the check is the location, not the versions.
How It Works
How It Works
A Python virtual environment is really just a directory holding its own interpreter reference, a private site-packages folder, and an activation script. Running python3 -m venv .venv creates that isolated directory tree; nothing else on the system changes.
Activation moves and reinstalls nothing. The activate script prepends the venv's bin directory to PATH and adjusts the prompt, so the names python and pip resolve inside .venv. Deactivating restores the previous PATH, which is why a venv is disposable: delete the folder and it is gone.
Pinning matters because pip install requests today and pip install requests next year can resolve different dependency versions. pip freeze records the exact resolved tree so requirements.txt reproduces the same set on any machine, which is the foundation of deterministic builds.
shared site-packages"] --> VENV["python3 -m venv .venv"] VENV --> ISO["Isolated .venv
own site-packages"] ISO --> PIN["pip freeze > requirements.txt"] PIN --> REP["Reproducible install
pip install -r"]
Validation & Testing
Recreate the environment from requirements on a clean directory and confirm identical package versions. Pass criteria: the venv activates, which python points inside .venv, and pip install -r reproduces the exact set.
Advanced: Troubleshooting
- Wrong interpreter: confirm activation;
which pythonmust resolve inside the venv. - Conflicting versions: never
pip installglobally; always activate first. - Irreproducible builds: pin transitive deps with pip-tools or a lock file.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| which python points outside .venv | the shell was not activated, or a new shell tab was opened | run source .venv/bin/activate in every new shell; confirm the prompt shows (.venv) |
| pip install fails with externally-managed-environment | system Python on Debian/Ubuntu blocks global installs (PEP 668) | always install inside an activated venv instead of fighting the guardrail |
| requirements.txt missing transitive pins | the file was hand-edited to list only direct deps | regenerate with pip freeze > requirements.txt after installing; consider pip-tools for layered pins |
| venv breaks after moving the project folder | activation scripts and shebangs embed absolute paths | recreate the venv in the new location instead of moving it |
| two projects need conflicting versions | a single global environment was used for both | one venv per project; this is the core habit this lab teaches |
Key Results
- Eliminated cross-project dependency conflicts via per-project venvs.
- Made 100% of scripts reproducible from a pinned requirements file.
- Standardized one project layout reused across 14 Python labs.
- Cut environment setup to a single two-command bootstrap.
Key Takeaways
- One project, one venv: isolation is the default, not the exception.
- Pin the full tree with pip freeze so installs are deterministic on any machine.
- Verify the interpreter with sys.prefix instead of assuming activation worked.
- A venv is disposable: recreate it from requirements.txt rather than debugging a polluted one.
- A standard layout (src/, requirements, .venv) makes every later lab start the same way.
Practice Challenges
Challenge 1: Bootstrap from scratch
Goal: Delete .venv and recreate the exact environment using only requirements.txt, then prove pip list matches the original.
Hint: The two commands from steps 1 and 2 are all you need; diff pip list output before and after.
Challenge 2: Catch a leak
Goal: With the venv active, explain why a package installed with pip install --target into system site-packages never shows up in pip list.
Hint: Inspect python -c "import sys; print(sys.path)" to see the isolation boundary.