Packaging & Environments
- Create and activate a virtual environment and explain what it isolates
- Declare dependencies in requirements.txt and in pyproject.toml, and say when each fits
- Lay out a project in src/ style and install it editable with pip install -e .
- Expose a console-script entry point so your tool runs as a real command
- Read a semantic version number and predict what a major/minor/patch bump promises
| Spaced-rep warm-up: due flashcards | 10 min |
| ELI5 + tech read: venvs, pyproject, entry points, semver | 20 min |
| Guided: clean kitchen + the recipe box | 40 min |
| Practice: the stranger test | 20 min |
| Project: package the analyzer skeleton | 20 min |
| Quiz + flashcards | 10 min |
Builds on: Day 3 β Modules & imports Β· Day 6 β The terminal & PATH Β· Day 16 β The analyzer CLI
You've perfected a dish in your own kitchen. A friend across the country wants to cook it. Mailing a note that says "make my curry" fails instantly: her kitchen has different pots, a different stove, and none of your spices. What actually works is shipping a recipe box: the recipe, an exact ingredient list with brands and amounts ("coconut milk, brand X, 400ml" β not "some coconut milk"), and a label saying which kitchen setup it needs.
That is packaging. A virtual environment is a clean rented kitchen per project β its own shelf of installed ingredients, so project A's tomato version never contaminates project B. requirements.txt and pyproject.toml are the ingredient list: exact names and versions, so pip (the grocery delivery service) can stock any empty kitchen identically. An entry point is the finishing touch: instead of telling your friend "run python path/to/that/script.py", the box installs a button on her counter labeled with your dish's name β she types loganalyzer anywhere and it just works. The test of a good recipe box is brutal and simple: someone who has never seen your kitchen can cook the dish from the box alone.
"Works on my machine" is the most expensive sentence in software, and environments are the cure. Every serious Python project you'll touch β sklearn pipelines (Day 82), the FastAPI service (Day 42), the capstone (Day 119) β starts with a venv and a pyproject.toml. Docker (Day 148) is this exact idea taken one level deeper: shipping the whole kitchen, not just the recipe box. For an FDE, reproducible installs are the difference between a 10-minute customer setup call and a lost afternoon of dependency archaeology on someone else's laptop.
Guided practice
A clean kitchen: venv + pip, end to end
15 min- Terminal: in a new folder
pkg-lab, create and activate a venv:python -m venv .venvthensource .venv/bin/activate(Windows PowerShell:.venv\Scripts\Activate.ps1). Your prompt should now show(.venv). - Prove isolation:
pip listβ nearly empty, no matter what your system Python has.which python(Windows:Get-Command python) β it points INSIDE .venv. - Install something:
pip install rich, then runpython -c "from rich import print; print('[bold green]isolated![/bold green]')". - Snapshot:
pip freeze > requirements.txtand open the file β notice rich brought friends (transitive dependencies), all pinned. - Deactivate (
deactivate), runpython -c "import rich"β with the system Python this should fail (or use a different rich). Reactivate. You now viscerally know what a venv isolates.
The recipe box: pyproject.toml + editable install + entry point
25 min- Inside
pkg-lab(venv active), build the src layout shown in the starter:src/greetkit/__init__.pyandsrc/greetkit/cli.py, pluspyproject.tomlat the root. - Type the pyproject.toml from the starter code β every line of it, so you know what each does.
- Terminal: install your own package editable:
pip install -e .β pip builds and links it. Runpip listand find greetkit pointing at your folder. - Run your new command from ANY directory:
greet Adathengreet Ada --shout. There is no "python file.py" anymore β you shipped a tool. - Prove "editable": change the greeting text in
cli.py, save, rungreet Adaagain β the change is live with no reinstall. - Bump
version = "0.1.1"and state out loud what that bump promises under semver (bug fix only).
On your own
The stranger test
20 minSimulate a teammate's laptop: somewhere OUTSIDE pkg-lab, create a fresh venv, and get greetkit running in it using only what you would actually ship (the project folder without .venv).
Goal: greet World works in the fresh venv. Then break it on purpose: add dependencies = ["rich>=13"] to pyproject.toml, import rich in cli.py to colorize output, and reinstall β confirm pip pulled rich in automatically because the recipe box declared it.
Constraints: do not copy the old .venv; do not pip install rich manually β the metadata must do it.
Hints: pip install -e /path/to/pkg-lab works from anywhere; if the import fails, ask whether the dependency is declared where pip can see it.
Package the log analyzer (skeleton)
Restructure your analyzer into a real package: src/loganalyzer/ containing __init__.py, parser.py (the parsing/reporting logic from Days 14β16), and cli.py (the argparse cockpit); a pyproject.toml with metadata, version = "0.1.0", and a [project.scripts] entry so loganalyzer report sample.log works as a bare command; a fresh venv with the package installed editable; and .venv/ plus analyzer.log in .gitignore. Pass your own stranger test: fresh venv, pip install -e ., run the command. Commit the restructure. On Day 18 this package grows a test suite; on Day 21 it ships to GitHub as v0.1.0.
Common mistakes & misconceptions
- Installing everything into the system Python "just this once." Two projects later you have version conflicts nobody can untangle. One project, one venv, always.
- Committing the .venv/ folder. It is machine-specific and huge; commit the declarations (pyproject.toml / requirements.txt) β the pantry is rebuilt from the list.
- Confusing the two files: pyproject.toml declares direct needs with ranges; pip freeze output pins everything installed. Freezing INTO pyproject.toml buries intent under transitive noise.
- Forgetting to activate the venv, then wondering why pip installed "somewhere else." Check the prompt prefix or which python before installing.
- Running scripts by path (python src/loganalyzer/cli.py) after packaging. That bypasses the install and breaks package imports β use the console script or python -m loganalyzer.
- Reading version numbers as decoration. Under semver, accepting any-version dependencies means agreeing to future breaking changes sight-unseen; ranges like >=13,<14 are a contract.
Q1. What does pip install -e . actually do?
Q2. A library you depend on jumps from 2.4.1 to 3.0.0. Under semantic versioning, what should you expect?
Q3. Why does the src/ layout catch packaging bugs that a flat layout hides?
Go deeper β curated resources
- uv β the fast modern package manager β The tool "uv" replaces venv+pip with one fast command set and real lockfiles (uv sync). Once the pip ritual is muscle memory, try recreating today's lab with uv init / uv add / uv run.
- greetkit installs editable and runs as a bare command from any directory
- Stranger test passed in a fresh venv, including the declared-dependency variant
- Analyzer restructured to src layout with working console script β committed
- Quiz β₯ 2/3
β Back: Day 3's import system is what packaging organizes; Day 6's PATH is where console scripts land; the CLI you exposed as an entry point is Day 16's cockpit.
Forward β: Day 18 installs pytest into this venv and tests the package. Day 21 ships it as v0.1.0. Day 148 (Docker) is the same reproducibility promise extended to the OS itself β the whole kitchen, not just the recipe box.
Unlocks: D18 Testing I β pytest Fundamentals Β· D20 GitHub Collaboration Β· D21 Week 3 Checkpoint: Ship a Tested Package Β· D81 Experiment Tracking & Reproducibility