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
+9 -9
View File
@@ -49,7 +49,7 @@ that runs a piece of your code and asserts that the result is what it should be.
holds, the test passes silently. If it doesn't, the test fails loudly and tells you exactly which
expectation broke.
You've already been testing, by hand. Every time you ran `python cli.py list` and eyeballed the
You've already been testing, by hand. Every time you ran `python3 cli.py list` and eyeballed the
output, you ran a manual test: *do something, check the result looks right.* The problem with the
manual version is the same problem copy-paste had in Module 1: it doesn't scale across files or
across time. You can't re-run "eyeball every command" on every change, so you don't, so regressions
@@ -71,12 +71,12 @@ class TestTaskList(unittest.TestCase):
self.assertEqual(tl.tasks[0].title, "write the tests")
```
The whole suite runs from the project folder with a single command: `python -m unittest`
The whole suite runs from the project folder with a single command: `python3 -m unittest`
auto-discovers files named `test_*.py`, and `-v` prints each test name and its result. A verbose run
looks like:
```text
$ python -m unittest -v
$ python3 -m unittest -v
test_add_appends_a_task (test_tasks.TestTaskList) ... ok
----------------------------------------------------------------------
@@ -167,7 +167,7 @@ intent has to come from you.
One more framing before the lab. A test file just sitting in your repo is useful when you remember to
run it; like the manual eyeball check, you eventually won't. The full payoff comes in
**Module 14**, where Continuous Integration runs this exact `python -m unittest` command
**Module 14**, where Continuous Integration runs this exact `python3 -m unittest` command
automatically on every push, so a regression can't reach `main` without something going red first.
That's why this module comes immediately before CI: **tests are the content CI runs.** You can't
@@ -256,7 +256,7 @@ Do this once yourself so the tool isn't magic. From inside your working copy of
2. Run it:
```bash
python -m unittest -v
python3 -m unittest -v
```
You should see one test, and `OK`. That's the entire mechanism. Everything else is more of these.
@@ -286,7 +286,7 @@ Do this once yourself so the tool isn't magic. From inside your working copy of
5. Run the suite:
```bash
python -m unittest -v
python3 -m unittest -v
```
At least one `pending_count` test should **FAIL**, with something like
@@ -310,8 +310,8 @@ Do this once yourself so the tool isn't magic. From inside your working copy of
return len(self.pending())
```
Re-run `python -m unittest -v`; green. Confirm the app agrees:
`python cli.py add a && python cli.py add b && python cli.py done 0 && python cli.py count`
Re-run `python3 -m unittest -v`; green. Confirm the app agrees:
`python3 cli.py add a && python3 cli.py add b && python3 cli.py done 0 && python3 cli.py count`
should report **1 task(s) pending**.
> Using your own app from earlier modules instead? If your `count` command was already correct,
@@ -365,7 +365,7 @@ The honest limits, because a green suite invites overconfidence:
**You're done when:**
- You can run `python -m unittest -v` in your `tasks-app` and see your own tests pass.
- You can run `python3 -m unittest -v` in your `tasks-app` and see your own tests pass.
- You watched an intent-encoding test **fail**, traced it to the real `pending_count` bug, fixed the
*code*, and watched it pass.
- You can articulate, in your own words, the difference between a test that asserts current behavior
@@ -1,10 +1,10 @@
"""Reference test suite for the Module 13 lab. Peek only after you've tried it yourself.
Named `reference_test_tasks.py` (not `test_*.py`) on purpose, so `python -m unittest discover`
Named `reference_test_tasks.py` (not `test_*.py`) on purpose, so `python3 -m unittest discover`
does NOT pick it up automatically. To run it, copy it next to your working `tasks.py` (e.g.
`~/ai-workflow-course/work/tasks-app/`) and run, from that directory:
python -m unittest reference_test_tasks
python3 -m unittest reference_test_tasks
It assumes `tasks.py` is importable, which is why you run it from the tasks-app directory.
@@ -15,11 +15,11 @@ has a `count` command (the Module 2 lab added one). The planted bug in this copy
## Run it
```bash
python cli.py add "write the tests"
python cli.py add "fix the bug"
python cli.py done 0
python cli.py list
python cli.py count
python3 cli.py add "write the tests"
python3 cli.py add "fix the bug"
python3 cli.py done 0
python3 cli.py list
python3 cli.py count
```
Requires Python 3.10+. No third-party packages; tests use the standard library `unittest`.
@@ -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. Same minimal app from Modules 1 and 2, with a
`count` command bolted on.
@@ -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]
@@ -15,11 +15,11 @@ has a `count` command (the Module 2 lab added one). The planted bug in this copy
## Run it
```bash
python cli.py add "write the tests"
python cli.py add "fix the bug"
python cli.py done 0
python cli.py list
python cli.py count
python3 cli.py add "write the tests"
python3 cli.py add "fix the bug"
python3 cli.py done 0
python3 cli.py list
python3 cli.py count
```
Requires Python 3.10+. No third-party packages; tests use the standard library `unittest`.
@@ -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. Same minimal app from Modules 1 and 2, with a
`count` command bolted on.
@@ -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]