From 743c482b7cb07b2ac2a46ddb1d2b8d6bc0b5e317 Mon Sep 17 00:00:00 2001 From: Yu-Tsen Wei <59054102+frobel0520@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:45:19 +0800 Subject: [PATCH 1/6] fix(curriculum): add actionable itertools assertion messages (#131) --- checks/itertools/itertools3.py | 10 +++++++++- checks/itertools/itertools4.py | 8 +++++++- checks/itertools/itertools5.py | 5 ++++- checks/itertools/itertools6.py | 5 ++++- checks/itertools/itertools7.py | 5 ++++- checks/itertools/itertools8.py | 10 ++++++++-- 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/checks/itertools/itertools3.py b/checks/itertools/itertools3.py index 629c89d..2527119 100644 --- a/checks/itertools/itertools3.py +++ b/checks/itertools/itertools3.py @@ -1,2 +1,10 @@ -assert coordinates == [(0, "low"), (0, "high"), (1, "low"), (1, "high")] +assert coordinates == [ + (0, "low"), + (0, "high"), + (1, "low"), + (1, "high"), +], ( + "coordinates should contain every (x, y) pair from xs and ys, " + f"got {coordinates!r}" +) print("itertools3 ok") diff --git a/checks/itertools/itertools4.py b/checks/itertools/itertools4.py index 858745e..fc6f17a 100644 --- a/checks/itertools/itertools4.py +++ b/checks/itertools/itertools4.py @@ -1,2 +1,8 @@ -assert grouped == {"fruit": ["apple", "pear"], "veg": ["carrot"]} +assert grouped == { + "fruit": ["apple", "pear"], + "veg": ["carrot"], +}, ( + "grouped should map each category to its item names, " + f"got {grouped!r}" +) print("itertools4 ok") diff --git a/checks/itertools/itertools5.py b/checks/itertools/itertools5.py index de89c34..838ce14 100644 --- a/checks/itertools/itertools5.py +++ b/checks/itertools/itertools5.py @@ -1,2 +1,5 @@ -assert running_totals == [3, 7, 12, 18] +assert running_totals == [3, 7, 12, 18], ( + "running_totals should contain cumulative sums [3, 7, 12, 18], " + f"got {running_totals!r}" +) print("itertools5 ok") diff --git a/checks/itertools/itertools6.py b/checks/itertools/itertools6.py index ffb0e86..b72da97 100644 --- a/checks/itertools/itertools6.py +++ b/checks/itertools/itertools6.py @@ -1,2 +1,5 @@ -assert repeated == ["red", "blue", "red", "blue", "red", "blue"] +assert repeated == ["red", "blue", "red", "blue", "red", "blue"], ( + "repeated should cycle through colors for six values, " + f"got {repeated!r}" +) print("itertools6 ok") diff --git a/checks/itertools/itertools7.py b/checks/itertools/itertools7.py index 6a3219b..d955fed 100644 --- a/checks/itertools/itertools7.py +++ b/checks/itertools/itertools7.py @@ -1,2 +1,5 @@ -assert pairs == [("a", 1), ("b", "?"), ("c", "?")] +assert pairs == [("a", 1), ("b", "?"), ("c", "?")], ( + "pairs should fill missing right-side values with '?', " + f"got {pairs!r}" +) print("itertools7 ok") diff --git a/checks/itertools/itertools8.py b/checks/itertools/itertools8.py index e188cba..77605aa 100644 --- a/checks/itertools/itertools8.py +++ b/checks/itertools/itertools8.py @@ -1,3 +1,9 @@ -assert flattened == [1, 2, 3, 4, 5] -assert adjacent_pairs == [(1, 2), (2, 3), (3, 4), (4, 5)] +assert flattened == [1, 2, 3, 4, 5], ( + "flattened should contain all batch items in order, " + f"got {flattened!r}" +) +assert adjacent_pairs == [(1, 2), (2, 3), (3, 4), (4, 5)], ( + "adjacent_pairs should contain each neighboring pair, " + f"got {adjacent_pairs!r}" +) print("itertools8 ok") From 24812fcde389b49001511fa113aac3ea1d89847f Mon Sep 17 00:00:00 2001 From: Yu-Tsen Wei <59054102+frobel0520@users.noreply.github.com> Date: Wed, 2 Sep 2026 07:33:25 +0800 Subject: [PATCH 2/6] fix(curriculum): add actionable enum assertion messages (#136) --- checks/enums/enums1.py | 12 +++++++++--- checks/enums/enums2.py | 10 ++++++++-- checks/enums/enums3.py | 8 ++++++-- checks/enums/enums4.py | 4 +++- checks/enums/enums5.py | 12 +++++++++--- checks/enums/enums6.py | 8 ++++++-- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/checks/enums/enums1.py b/checks/enums/enums1.py index 244efe8..0ea55e4 100644 --- a/checks/enums/enums1.py +++ b/checks/enums/enums1.py @@ -1,4 +1,10 @@ -assert Color.RED.value == "red" -assert Color.BLUE.value == "blue" -assert favorite is Color.RED +assert Color.RED.value == "red", ( + f"Color.RED.value should be 'red', got {Color.RED.value!r}" +) +assert Color.BLUE.value == "blue", ( + f"Color.BLUE.value should be 'blue', got {Color.BLUE.value!r}" +) +assert favorite is Color.RED, ( + f"favorite should be Color.RED, got {favorite!r}" +) print("enums1 ok") diff --git a/checks/enums/enums2.py b/checks/enums/enums2.py index 7deb8fa..fa2a666 100644 --- a/checks/enums/enums2.py +++ b/checks/enums/enums2.py @@ -1,3 +1,9 @@ -assert [member.name for member in Status] == ["TODO", "DOING", "DONE"] -assert [member.value for member in Status] == [1, 2, 3] +assert [member.name for member in Status] == ["TODO", "DOING", "DONE"], ( + "Status names should be ['TODO', 'DOING', 'DONE'], got " + f"{[member.name for member in Status]!r}" +) +assert [member.value for member in Status] == [1, 2, 3], ( + f"Status values should be [1, 2, 3], got " + f"{[member.value for member in Status]!r}" +) print("enums2 ok") diff --git a/checks/enums/enums3.py b/checks/enums/enums3.py index 0a7be71..f292f3a 100644 --- a/checks/enums/enums3.py +++ b/checks/enums/enums3.py @@ -1,3 +1,7 @@ -assert selected_name == "HIGH" -assert selected_value == 5 +assert selected_name == "HIGH", ( + f"selected_name should be 'HIGH', got {selected_name!r}" +) +assert selected_value == 5, ( + f"selected_value should be 5, got {selected_value!r}" +) print("enums3 ok") diff --git a/checks/enums/enums4.py b/checks/enums/enums4.py index 1686e35..6bd2355 100644 --- a/checks/enums/enums4.py +++ b/checks/enums/enums4.py @@ -1,2 +1,4 @@ -assert size_codes == ["S", "M", "L"] +assert size_codes == ["S", "M", "L"], ( + f"size_codes should be ['S', 'M', 'L'], got {size_codes!r}" +) print("enums4 ok") diff --git a/checks/enums/enums5.py b/checks/enums/enums5.py index 0a6792a..71d0817 100644 --- a/checks/enums/enums5.py +++ b/checks/enums/enums5.py @@ -1,4 +1,10 @@ -assert is_terminal(TicketState.OPEN) is False -assert is_terminal(TicketState.CLOSED) is True -assert is_terminal(TicketState.CANCELLED) is True +assert is_terminal(TicketState.OPEN) is False, ( + "is_terminal(TicketState.OPEN) should be False" +) +assert is_terminal(TicketState.CLOSED) is True, ( + "is_terminal(TicketState.CLOSED) should be True" +) +assert is_terminal(TicketState.CANCELLED) is True, ( + "is_terminal(TicketState.CANCELLED) should be True" +) print("enums5 ok") diff --git a/checks/enums/enums6.py b/checks/enums/enums6.py index bd6af35..88e7c23 100644 --- a/checks/enums/enums6.py +++ b/checks/enums/enums6.py @@ -1,3 +1,7 @@ -assert Direction.NORTH.delta() == (0, -1) -assert Direction.EAST.delta() == (1, 0) +assert Direction.NORTH.delta() == (0, -1), ( + "Direction.NORTH.delta() should return (0, -1)" +) +assert Direction.EAST.delta() == (1, 0), ( + "Direction.EAST.delta() should return (1, 0)" +) print("enums6 ok") From 458d1d1be121d97221e83ca0978c2a51a1f45aff Mon Sep 17 00:00:00 2001 From: Yu-Tsen Wei <59054102+frobel0520@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:28:51 +0800 Subject: [PATCH 3/6] fix(curriculum): add actionable pathlib assertion messages (#137) --- checks/pathlib/pathlib1.py | 8 ++++++-- checks/pathlib/pathlib2.py | 4 +++- checks/pathlib/pathlib3.py | 8 +++++--- checks/pathlib/pathlib4.py | 5 ++++- checks/pathlib/pathlib5.py | 4 +++- checks/pathlib/pathlib6.py | 8 ++++++-- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/checks/pathlib/pathlib1.py b/checks/pathlib/pathlib1.py index ed4369d..ac5a715 100644 --- a/checks/pathlib/pathlib1.py +++ b/checks/pathlib/pathlib1.py @@ -1,5 +1,9 @@ from pathlib import Path -assert path == Path("notes/today.txt") -assert filename == "today.txt" +assert path == Path("notes/today.txt"), ( + f"path should be Path('notes/today.txt'), got {path!r}" +) +assert filename == "today.txt", ( + f"filename should be 'today.txt', got {filename!r}" +) print("pathlib1 ok") diff --git a/checks/pathlib/pathlib2.py b/checks/pathlib/pathlib2.py index 87b94a3..eb84ad5 100644 --- a/checks/pathlib/pathlib2.py +++ b/checks/pathlib/pathlib2.py @@ -1,4 +1,6 @@ from pathlib import Path -assert source == Path("pythonlings/src/main.py") +assert source == Path("pythonlings/src/main.py"), ( + f"source should be Path('pythonlings/src/main.py'), got {source!r}" +) print("pathlib2 ok") diff --git a/checks/pathlib/pathlib3.py b/checks/pathlib/pathlib3.py index a6c57dc..298f60e 100644 --- a/checks/pathlib/pathlib3.py +++ b/checks/pathlib/pathlib3.py @@ -1,6 +1,8 @@ from pathlib import Path -assert parent == Path("docs") -assert stem == "guide" -assert suffix == ".md" +assert parent == Path("docs"), ( + f"parent should be Path('docs'), got {parent!r}" +) +assert stem == "guide", f"stem should be 'guide', got {stem!r}" +assert suffix == ".md", f"suffix should be '.md', got {suffix!r}" print("pathlib3 ok") diff --git a/checks/pathlib/pathlib4.py b/checks/pathlib/pathlib4.py index 0f3fbfe..8cf5c1b 100644 --- a/checks/pathlib/pathlib4.py +++ b/checks/pathlib/pathlib4.py @@ -1,4 +1,7 @@ from pathlib import Path -assert python_files == [Path("app.py"), Path("tests.py")] +assert python_files == [Path("app.py"), Path("tests.py")], ( + f"python_files should be [Path('app.py'), Path('tests.py')], " + f"got {python_files!r}" +) print("pathlib4 ok") diff --git a/checks/pathlib/pathlib5.py b/checks/pathlib/pathlib5.py index c6d7e17..2943c7a 100644 --- a/checks/pathlib/pathlib5.py +++ b/checks/pathlib/pathlib5.py @@ -1,4 +1,6 @@ from pathlib import Path -assert final == Path("reports/final.txt") +assert final == Path("reports/final.txt"), ( + f"final should be Path('reports/final.txt'), got {final!r}" +) print("pathlib5 ok") diff --git a/checks/pathlib/pathlib6.py b/checks/pathlib/pathlib6.py index addba6e..ff04253 100644 --- a/checks/pathlib/pathlib6.py +++ b/checks/pathlib/pathlib6.py @@ -1,5 +1,9 @@ from pathlib import Path -assert processed == Path("data/raw.json") -assert parts[-2:] == ("data", "raw.json") +assert processed == Path("data/raw.json"), ( + f"processed should be Path('data/raw.json'), got {processed!r}" +) +assert parts[-2:] == ("data", "raw.json"), ( + f"parts[-2:] should be ('data', 'raw.json'), got {parts[-2:]!r}" +) print("pathlib6 ok") From d821adfbb5d7265cd2a399cadc1b7044c8524e6c Mon Sep 17 00:00:00 2001 From: Dream <2468001320@qq.com> Date: Wed, 2 Sep 2026 14:06:10 +0800 Subject: [PATCH 4/6] fix(curriculum): add actionable advanced OOP assertion messages (batch 2) (#139) * curriculum: add assertion message to checks/oop_advanced/oop_advanced7.py * curriculum: add assertion message to checks/oop_advanced/oop_advanced8.py * curriculum: add assertion message to checks/oop_advanced/oop_advanced9.py * curriculum: add assertion message to checks/oop_advanced/oop_advanced10.py * curriculum: add assertion message to checks/oop_advanced/oop_advanced11.py * curriculum: add assertion message to checks/oop_advanced/oop_advanced12.py * fix(curriculum): normalize advanced OOP checks --------- Co-authored-by: Abhik Sarkar --- checks/oop_advanced/oop_advanced10.py | 4 ++-- checks/oop_advanced/oop_advanced11.py | 5 ++++- checks/oop_advanced/oop_advanced12.py | 10 ++++++++-- checks/oop_advanced/oop_advanced7.py | 8 ++++++-- checks/oop_advanced/oop_advanced8.py | 5 ++++- checks/oop_advanced/oop_advanced9.py | 8 +++++--- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/checks/oop_advanced/oop_advanced10.py b/checks/oop_advanced/oop_advanced10.py index 6d86d7a..bbd04bd 100644 --- a/checks/oop_advanced/oop_advanced10.py +++ b/checks/oop_advanced/oop_advanced10.py @@ -1,3 +1,3 @@ -assert issubclass(Square, Shape) -assert Square(4).area() == 16 +assert issubclass(Square, Shape), "Square should inherit from Shape" +assert Square(4).area() == 16, "Square(4).area() should be 16 (4 * 4)" print("oop_advanced10 ok") diff --git a/checks/oop_advanced/oop_advanced11.py b/checks/oop_advanced/oop_advanced11.py index 58c7645..9d46d16 100644 --- a/checks/oop_advanced/oop_advanced11.py +++ b/checks/oop_advanced/oop_advanced11.py @@ -1,3 +1,6 @@ product = Product("book", 12) -assert product.to_dict() == {"name": "book", "price": 12} +assert product.to_dict() == {"name": "book", "price": 12}, ( + "Product('book', 12).to_dict() should return " + "{'name': 'book', 'price': 12}" +) print("oop_advanced11 ok") diff --git a/checks/oop_advanced/oop_advanced12.py b/checks/oop_advanced/oop_advanced12.py index 37c1079..ccc3140 100644 --- a/checks/oop_advanced/oop_advanced12.py +++ b/checks/oop_advanced/oop_advanced12.py @@ -1,4 +1,10 @@ result = Vector(1, 2) + Vector(3, 4) -assert isinstance(result, Vector) -assert (result.x, result.y) == (4, 6) +assert isinstance(result, Vector), ( + f"Adding two Vector instances should return a Vector, " + f"got {type(result).__name__}" +) +assert (result.x, result.y) == (4, 6), ( + f"Vector(1, 2) + Vector(3, 4) should be (4, 6), " + f"got {(result.x, result.y)!r}" +) print("oop_advanced12 ok") diff --git a/checks/oop_advanced/oop_advanced7.py b/checks/oop_advanced/oop_advanced7.py index 6b54ae7..dd0912d 100644 --- a/checks/oop_advanced/oop_advanced7.py +++ b/checks/oop_advanced/oop_advanced7.py @@ -1,3 +1,7 @@ -assert Book("A", "123") == Book("B", "123") -assert Book("A", "123") != Book("C", "999") +assert Book("A", "123") == Book("B", "123"), ( + "Two books with the same ISBN should be equal" +) +assert Book("A", "123") != Book("C", "999"), ( + "Two books with different ISBNs should not be equal" +) print("oop_advanced7 ok") diff --git a/checks/oop_advanced/oop_advanced8.py b/checks/oop_advanced/oop_advanced8.py index 11193f8..f1a656a 100644 --- a/checks/oop_advanced/oop_advanced8.py +++ b/checks/oop_advanced/oop_advanced8.py @@ -1,3 +1,6 @@ coord = Coordinate.from_text("3,4") -assert (coord.x, coord.y) == (3, 4) +assert (coord.x, coord.y) == (3, 4), ( + f"Coordinate.from_text('3,4') should produce (3, 4), " + f"got {(coord.x, coord.y)!r}" +) print("oop_advanced8 ok") diff --git a/checks/oop_advanced/oop_advanced9.py b/checks/oop_advanced/oop_advanced9.py index d187057..9fb071b 100644 --- a/checks/oop_advanced/oop_advanced9.py +++ b/checks/oop_advanced/oop_advanced9.py @@ -1,4 +1,6 @@ -assert Grade.is_valid(0) is True -assert Grade.is_valid(100) is True -assert Grade.is_valid(101) is False +assert Grade.is_valid(0) is True, "Grade 0 should be a valid score" +assert Grade.is_valid(100) is True, "Grade 100 should be a valid score" +assert Grade.is_valid(101) is False, ( + "Grade 101 should be rejected as out of range" +) print("oop_advanced9 ok") From 7bd85bf764b53ce515b0ad6efd8106eea11cc4d4 Mon Sep 17 00:00:00 2001 From: Yu-Tsen Wei <59054102+frobel0520@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:18:05 +0800 Subject: [PATCH 5/6] docs: clarify automatic checks in built-in editor (#138) --- docs-site/how-it-works.md | 4 ++-- docs-site/index.md | 5 +++-- docs-site/quick-start.md | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs-site/how-it-works.md b/docs-site/how-it-works.md index 6018f85..fd8543b 100644 --- a/docs-site/how-it-works.md +++ b/docs-site/how-it-works.md @@ -9,8 +9,8 @@ Pythonlings follows a tight edit-check-advance loop. Here is what happens at eac ## The Learner Loop 1. **Open the next pending exercise** — the TUI shows the first incomplete exercise in the curriculum. -2. **Edit the broken code** — fix the intentional mistake directly in the built-in editor (or any external editor you prefer). -3. **Checks rerun automatically** — roughly 0.6 s after you stop typing, Pythonlings re-evaluates the exercise in a fresh subprocess. There is no filesystem watcher; the debounce fires inside the TUI editor. +2. **Edit the broken code** — fix the intentional mistake directly in the built-in editor. This is the automatic edit-check-advance workflow. If you use an external editor, save the file and run `pythonlings run ` to check it manually. +3. **Checks rerun automatically** — roughly 0.6 s after you stop typing in the built-in editor, Pythonlings re-evaluates the exercise in a fresh subprocess. There is no filesystem watcher, so external saves do not trigger this automatic check. 4. **Remove the `# I AM NOT DONE` marker** — the marker is your explicit "I'm finished" signal. Passing checks alone does not advance the exercise (see below). 5. **Advance** — once checks are green *and* the marker is gone, the exercise is marked complete and the TUI moves to the next one. diff --git a/docs-site/index.md b/docs-site/index.md index 4dfe3df..935f63d 100644 --- a/docs-site/index.md +++ b/docs-site/index.md @@ -7,8 +7,9 @@ hide:
Rustlings for Python
Learn Python by fixing tiny broken programs.
-
292 exercises across 31 topics. Hidden checks rerun the - instant you save — fix the code, watch it go green, advance.
+
292 exercises across 31 topics. In the built-in editor, + hidden checks rerun after you stop typing — fix the code, watch it go green, + advance.
$ uvx pythonlings init
Get started → diff --git a/docs-site/quick-start.md b/docs-site/quick-start.md index ddcea0a..c549e4a 100644 --- a/docs-site/quick-start.md +++ b/docs-site/quick-start.md @@ -61,6 +61,8 @@ pythonlings update # pull new exercises into an existing workspace ## Exercise Loop Each exercise contains a `# I AM NOT DONE` marker. Open the file in the TUI -editor (or any editor), fix the broken code, then remove the marker. Pythonlings -runs the hidden check automatically; when the check passes and the marker is gone, -the exercise is marked complete and progress advances to the next one. +editor and fix the broken code, then remove the marker. The built-in editor runs +the hidden check automatically; if you use an external editor, save the file and +run `pythonlings run ` to check it manually. When the check passes and the +marker is gone in the TUI, the exercise is marked complete and progress advances +to the next one. From ca3dd1e4418c7a20380673f5d1bb56a0e7a6d270 Mon Sep 17 00:00:00 2001 From: Ali-Nawaz-devt Date: Sun, 20 Sep 2026 20:27:45 +0500 Subject: [PATCH 6/6] curriculum: add actionable assertion messages to generators checks --- checks/generators/generators1.py | 9 +++++---- checks/generators/generators10.py | 14 +++++++------- checks/generators/generators3.py | 7 ++++--- checks/generators/generators6.py | 8 ++++---- checks/generators/generators7.py | 8 ++++---- checks/generators/generators8.py | 14 +++++++------- checks/generators/generators9.py | 14 +++++++------- 7 files changed, 38 insertions(+), 36 deletions(-) diff --git a/checks/generators/generators1.py b/checks/generators/generators1.py index 4f3822e..dab7bcb 100644 --- a/checks/generators/generators1.py +++ b/checks/generators/generators1.py @@ -1,4 +1,5 @@ -assert list(count_up_to(4)) == [1, 2, 3, 4] -assert list(count_up_to(1)) == [1] -assert list(count_up_to(0)) == [] -print("generators1 ✓") +# generators1.py +assert list(count_up_to(4)) == [1, 2, 3, 4], "Expected list(count_up_to(4)) to be [1, 2, 3, 4]" +assert list(count_up_to(1)) == [1], "Expected list(count_up_to(1)) to be [1]" +assert list(count_up_to(0)) == [], "Expected list(count_up_to(0)) to be [] (no elements when limit is 0)" +print("generators1 ✓") \ No newline at end of file diff --git a/checks/generators/generators10.py b/checks/generators/generators10.py index c5cabcd..8e2c955 100644 --- a/checks/generators/generators10.py +++ b/checks/generators/generators10.py @@ -1,7 +1,7 @@ -assert list(running_total([1, 2, 3, 4])) == [1, 3, 6, 10] -assert list(running_total([10, -3, 7])) == [10, 7, 14] -assert list(running_total([])) == [] -assert list(running_total([5])) == [5] -# Works as a streaming pipeline over a generator input. -assert list(running_total(x * x for x in range(1, 5))) == [1, 5, 14, 30] -print("generators10 ✓") +# generators10.py +assert list(running_total([1, 2, 3, 4])) == [1, 3, 6, 10], "Expected list(running_total([1, 2, 3, 4])) to be [1, 3, 6, 10]" +assert list(running_total([10, -3, 7])) == [10, 7, 14], "Expected list(running_total([10, -3, 7])) to be [10, 7, 14]" +assert list(running_total([])) == [], "Expected list(running_total([])) to be [] (no elements to sum)" +assert list(running_total([5])) == [5], "Expected list(running_total([5])) to be [5]" +assert list(running_total(x * x for x in range(1, 5))) == [1, 5, 14, 30], "Expected running_total over a generator input (1, 4, 9, 16) to yield [1, 5, 14, 30]" +print("generators10 ✓") \ No newline at end of file diff --git a/checks/generators/generators3.py b/checks/generators/generators3.py index 8dc4615..94c70b9 100644 --- a/checks/generators/generators3.py +++ b/checks/generators/generators3.py @@ -1,4 +1,5 @@ +# generators3.py — first assert already has a message, leave it exactly as-is assert collected == [2, 4, 6, 8], f"expected [2, 4, 6, 8], got {collected}" -assert list(evens_up_to(5)) == [2, 4] -assert list(evens_up_to(1)) == [] -print("generators3 ✓") +assert list(evens_up_to(5)) == [2, 4], "Expected list(evens_up_to(5)) to be [2, 4]" +assert list(evens_up_to(1)) == [], "Expected list(evens_up_to(1)) to be [] (no even numbers \u2264 1)" +print("generators3 ✓") \ No newline at end of file diff --git a/checks/generators/generators6.py b/checks/generators/generators6.py index 8aabc65..4c6834f 100644 --- a/checks/generators/generators6.py +++ b/checks/generators/generators6.py @@ -1,8 +1,8 @@ +# generators6.py — first assert unchanged; big/1000 are plain variables, safe to echo assert first_ten == list(range(1, 11)), ( f"expected [1..10], got {first_ten}" ) -# Confirm the generator really is infinite: pull 1000 values. big = list(itertools.islice(natural_numbers(), 1000)) -assert len(big) == 1000 -assert big[-1] == 1000 -print("generators6 ✓") +assert len(big) == 1000, f"Expected 1000 values pulled from natural_numbers(), got {len(big)}" +assert big[-1] == 1000, f"Expected the 1000th value to be 1000, got {big[-1]!r}" +print("generators6 ✓") \ No newline at end of file diff --git a/checks/generators/generators7.py b/checks/generators/generators7.py index a043998..66b0e43 100644 --- a/checks/generators/generators7.py +++ b/checks/generators/generators7.py @@ -1,11 +1,11 @@ +# generators7.py — first assert unchanged import itertools first8 = list(itertools.islice(fibonacci(), 8)) assert first8 == [0, 1, 1, 2, 3, 5, 8, 13], ( f"expected [0, 1, 1, 2, 3, 5, 8, 13], got {first8}" ) -# Confirm more terms keep coming. first15 = list(itertools.islice(fibonacci(), 15)) -assert len(first15) == 15 -assert first15[-1] == 377 -print("generators7 ✓") +assert len(first15) == 15, f"Expected 15 terms from fibonacci(), got {len(first15)}" +assert first15[-1] == 377, f"Expected the 15th Fibonacci term to be 377, got {first15[-1]!r}" +print("generators7 ✓") \ No newline at end of file diff --git a/checks/generators/generators8.py b/checks/generators/generators8.py index d1e027d..2e3e7c8 100644 --- a/checks/generators/generators8.py +++ b/checks/generators/generators8.py @@ -1,8 +1,8 @@ -assert list(Countdown(3)) == [3, 2, 1] -assert list(Countdown(1)) == [1] -assert list(Countdown(5)) == [5, 4, 3, 2, 1] -# Should be re-iterable: a fresh iteration each time. +# generators8.py — Countdown is the class under test, no re-invocation in messages +assert list(Countdown(3)) == [3, 2, 1], "Expected list(Countdown(3)) to be [3, 2, 1]" +assert list(Countdown(1)) == [1], "Expected list(Countdown(1)) to be [1]" +assert list(Countdown(5)) == [5, 4, 3, 2, 1], "Expected list(Countdown(5)) to be [5, 4, 3, 2, 1]" cd = Countdown(3) -assert list(cd) == [3, 2, 1] -assert list(cd) == [3, 2, 1] -print("generators8 ✓") +assert list(cd) == [3, 2, 1], "Expected first iteration over cd to be [3, 2, 1]" +assert list(cd) == [3, 2, 1], "Expected second iteration over cd to also be [3, 2, 1] \u2014 Countdown should be re-iterable, not exhausted after one pass" +print("generators8 ✓") \ No newline at end of file diff --git a/checks/generators/generators9.py b/checks/generators/generators9.py index 691b7eb..2f3cd9d 100644 --- a/checks/generators/generators9.py +++ b/checks/generators/generators9.py @@ -1,13 +1,13 @@ -assert list(Counter(4)) == [0, 1, 2, 3] -assert list(Counter(1)) == [0] -assert list(Counter(0)) == [] -# StopIteration must be raised at the right moment. +# generators9.py +assert list(Counter(4)) == [0, 1, 2, 3], "Expected list(Counter(4)) to be [0, 1, 2, 3]" +assert list(Counter(1)) == [0], "Expected list(Counter(1)) to be [0]" +assert list(Counter(0)) == [], "Expected list(Counter(0)) to be [] (no elements when stop is 0)" c = Counter(2) -assert next(c) == 0 -assert next(c) == 1 +assert next(c) == 0, "Expected the first value from Counter(2) to be 0" +assert next(c) == 1, "Expected the second value from Counter(2) to be 1" try: next(c) raise AssertionError("expected StopIteration after stop elements") except StopIteration: pass -print("generators9 ✓") +print("generators9 ✓") \ No newline at end of file