Use python3 as the canonical command name course-wide (#104)
CI / check (pull_request) Successful in 7s

Most current systems (default Debian/Ubuntu, recent macOS) install Python
only as `python3`, with no bare `python` on PATH, so learners who copied
`python cli.py ...` into their host shell hit "command not found".

Convert host-shell `python <cmd>` -> `python3 <cmd>` across module/lab
READMEs, lab `.py` docstrings & usage strings, blog posts, lab prompt and
instruction files, the M04 verify.sh message, and the M10/M24 lab patches.
Module 01's convention note (and its blog/02 mirror) is rewritten so
`python3` is canonical and `python` is the documented fallback.

Stop-lines respected: Docker image tags (`python:3.12-slim`), `.venv/.../python`
and `...\.venv\Scripts\python.exe` paths, the M20 `"command": "python"`
teaching example and surrounding venv prose, container-internal invocations
(M16/M18 Dockerfiles, M16 README `docker run` examples), and CI-workflow
`run:` steps fed by `actions/setup-python` / `image: python:3.12` are left
as `python` on purpose.

pip was left out of scope: most occurrences are prose or CI/container-internal,
and `pip3` does not fix the PEP 668 externally-managed-environment refusal that
the course already addresses with venvs. The M01 note is worded to stay
consistent with bare `pip` (use whichever pip pairs with your Python).

Build (tools/build_wiki.py) and tools/check.sh both pass.

Closes #104

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GAEzanEoGJT5o1VizQar47
This commit is contained in:
2026-06-23 20:18:04 -04:00
parent 7f439212ac
commit 3221f7abe8
102 changed files with 380 additions and 378 deletions
@@ -24,7 +24,7 @@ Ask for these if they weren't given:
- Core logic lives in `tasks.py` (the `TaskList` class). The CLI front end is `cli.py`. State
persists to `tasks.json`. **Never edit `tasks.json` by hand; it's generated.**
- Tests live in `test_tasks.py` and run with `python -m unittest`. Standard library only; no
- Tests live in `test_tasks.py` and run with `python3 -m unittest`. Standard library only; no
third-party packages, no new dependencies.
- The human-facing change log is `CHANGELOG.md`, newest entry on top.
@@ -42,10 +42,10 @@ Ask for these if they weren't given:
crash." Assert the end state (e.g. after `clear()`, `len(tasks) == 0` and `pending()` is empty).
A test that passes against a broken implementation is worse than no test.
4. **Run the tests.** `python -m unittest` from the project root. Do not claim success until it's
4. **Run the tests.** `python3 -m unittest` from the project root. Do not claim success until it's
green. If it fails, fix the code, not the test, and run again.
5. **Smoke-test the CLI.** Actually run it: `python cli.py COMMAND_NAME`, then `python cli.py list`
5. **Smoke-test the CLI.** Actually run it: `python3 cli.py COMMAND_NAME`, then `python3 cli.py list`
to confirm the visible result. Paste what you ran and what it printed.
6. **Add a `CHANGELOG.md` entry.** One line under the top heading, present tense:
@@ -57,8 +57,8 @@ Ask for these if they weren't given:
## Done when
- `python -m unittest` is green and includes a new test that actually exercises `COMMAND_NAME`.
- `python cli.py COMMAND_NAME` does `WHAT_IT_DOES` and you've shown the output.
- `python3 -m unittest` is green and includes a new test that actually exercises `COMMAND_NAME`.
- `python3 cli.py COMMAND_NAME` does `WHAT_IT_DOES` and you've shown the output.
- `CHANGELOG.md` has a new top line for the command.
- One commit contains the code, the test, and the changelog line, and nothing else (no
`tasks.json`, no unrelated reformatting).
@@ -1,9 +1,9 @@
"""Tiny command-line front end for the demo task app.
Run it:
python cli.py add "write the lesson"
python cli.py list
python cli.py count
python3 cli.py add "write the lesson"
python3 cli.py list
python3 cli.py count
State is kept in tasks.json next to this file. The same minimal app from Module 1 onward; the
target your "add a command" skill extends.
@@ -32,7 +32,7 @@ def save(tlist: TaskList) -> None:
def main(argv: list[str]) -> int:
tlist = load()
if not argv:
print("usage: python cli.py [add <title> | list | done <index> | count]")
print("usage: python3 cli.py [add <title> | list | done <index> | count]")
return 1
command = argv[0]
@@ -1,6 +1,6 @@
"""Test suite for the tasks-app. Run from this folder with:
python -m unittest
python3 -m unittest
Your "add a command" skill should ADD a test here for every new command. The point is to assert
intended behavior, not just that nothing crashed.
@@ -1,9 +1,9 @@
"""Tiny command-line front end for the demo task app.
Run it:
python cli.py add "write the lesson"
python cli.py list
python cli.py count
python3 cli.py add "write the lesson"
python3 cli.py list
python3 cli.py count
State is kept in tasks.json next to this file. The same minimal app from Module 1 onward; the
target your "add a command" skill extends.
@@ -32,7 +32,7 @@ def save(tlist: TaskList) -> None:
def main(argv: list[str]) -> int:
tlist = load()
if not argv:
print("usage: python cli.py [add <title> | list | done <index> | count]")
print("usage: python3 cli.py [add <title> | list | done <index> | count]")
return 1
command = argv[0]
@@ -1,6 +1,6 @@
"""Test suite for the tasks-app. Run from this folder with:
python -m unittest
python3 -m unittest
Your "add a command" skill should ADD a test here for every new command. The point is to assert
intended behavior, not just that nothing crashed.