From 439c89b524fc997eecb8664adbd6733aa815dd87 Mon Sep 17 00:00:00 2001 From: mercael91 Date: Sat, 17 Oct 2026 10:18:24 +0300 Subject: [PATCH 01/11] Wrap long lines in advanced OOP checks to satisfy 80-char limit Signed-off-by: mercael --- checks/oop_advanced/oop_advanced1.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/checks/oop_advanced/oop_advanced1.py b/checks/oop_advanced/oop_advanced1.py index b99139f..6aabbfc 100644 --- a/checks/oop_advanced/oop_advanced1.py +++ b/checks/oop_advanced/oop_advanced1.py @@ -1,4 +1,8 @@ dog = Dog() -assert isinstance(dog, Animal) -assert dog.speak() == "sound" +assert isinstance(dog, Animal), ( + "Dog() should be an instance of Animal" +) +assert dog.speak() == "sound", ( + "Dog().speak() should return 'sound', got %r" % (dog.speak(),) +) print("oop_advanced1 ok") From cae5a542917ea13957c26b71e9c07fe23dd5c188 Mon Sep 17 00:00:00 2001 From: mercael91 Date: Sat, 17 Oct 2026 10:18:26 +0300 Subject: [PATCH 02/11] Wrap long lines in advanced OOP checks to satisfy 80-char limit Signed-off-by: mercael --- checks/oop_advanced/oop_advanced2.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/checks/oop_advanced/oop_advanced2.py b/checks/oop_advanced/oop_advanced2.py index 2f17a53..8e77431 100644 --- a/checks/oop_advanced/oop_advanced2.py +++ b/checks/oop_advanced/oop_advanced2.py @@ -1,4 +1,10 @@ employee = Employee("Ada", "Engineer") -assert employee.name == "Ada" -assert employee.role == "Engineer" +assert employee.name == "Ada", ( + "Employee('Ada', 'Engineer').name should be 'Ada', got %r" + % (employee.name,) +) +assert employee.role == "Engineer", ( + "Employee('Ada', 'Engineer').role should be 'Engineer', got %r" + % (employee.role,) +) print("oop_advanced2 ok") From c26e98811cecbb61c3848de32231e5c00b26905d Mon Sep 17 00:00:00 2001 From: mercael91 Date: Sat, 17 Oct 2026 10:18:27 +0300 Subject: [PATCH 03/11] Wrap long lines in advanced OOP checks to satisfy 80-char limit Signed-off-by: mercael --- tests/test_oop_advanced_checks.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_oop_advanced_checks.py diff --git a/tests/test_oop_advanced_checks.py b/tests/test_oop_advanced_checks.py new file mode 100644 index 0000000..1be3a78 --- /dev/null +++ b/tests/test_oop_advanced_checks.py @@ -0,0 +1,18 @@ +import ast +from pathlib import Path + + +CHECKS_DIR = Path(__file__).resolve().parent.parent / "checks" / "oop_advanced" + + +def test_check_lines_within_80_chars(): + for path in sorted(CHECKS_DIR.glob("oop_advanced*.py")): + for lineno, line in enumerate(path.read_text().splitlines(), 1): + assert len(line) <= 80, ( + "%s:%d exceeds 80 chars (%d)" % (path.name, lineno, len(line)) + ) + + +def test_checks_parse(): + for path in sorted(CHECKS_DIR.glob("oop_advanced*.py")): + ast.parse(path.read_text(), filename=str(path)) From 9a9cfe663939c5cc7f613cbb07d35f52c5d23691 Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:23:53 +0300 Subject: [PATCH 04/11] checks: add actionable assertion messages to advanced OOP checks (#107) --- checks/oop_advanced/oop_advanced1.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/checks/oop_advanced/oop_advanced1.py b/checks/oop_advanced/oop_advanced1.py index 6aabbfc..5ac3c1f 100644 --- a/checks/oop_advanced/oop_advanced1.py +++ b/checks/oop_advanced/oop_advanced1.py @@ -1,8 +1,5 @@ dog = Dog() -assert isinstance(dog, Animal), ( - "Dog() should be an instance of Animal" -) -assert dog.speak() == "sound", ( - "Dog().speak() should return 'sound', got %r" % (dog.speak(),) -) +assert isinstance(dog, Animal), "Dog() should be an instance of Animal" +got = dog.speak() +assert got == "sound", f"Dog().speak() should return 'sound', got {got!r}" print("oop_advanced1 ok") From 35ce6c41d64878875823c42cbac5f53c90014777 Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:23:54 +0300 Subject: [PATCH 05/11] checks: add actionable assertion messages to advanced OOP checks (#107) --- checks/oop_advanced/oop_advanced2.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/checks/oop_advanced/oop_advanced2.py b/checks/oop_advanced/oop_advanced2.py index 8e77431..5aee10c 100644 --- a/checks/oop_advanced/oop_advanced2.py +++ b/checks/oop_advanced/oop_advanced2.py @@ -1,10 +1,6 @@ employee = Employee("Ada", "Engineer") -assert employee.name == "Ada", ( - "Employee('Ada', 'Engineer').name should be 'Ada', got %r" - % (employee.name,) -) +assert employee.name == "Ada", f"expected name 'Ada', got {employee.name!r}" assert employee.role == "Engineer", ( - "Employee('Ada', 'Engineer').role should be 'Engineer', got %r" - % (employee.role,) + f"expected role 'Engineer', got {employee.role!r}" ) print("oop_advanced2 ok") From dea31cc29be0e6378c1694a5e5fd3c64a09e2157 Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:23:55 +0300 Subject: [PATCH 06/11] checks: add actionable assertion messages to advanced OOP checks (#107) --- checks/oop_advanced/oop_advanced3.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/checks/oop_advanced/oop_advanced3.py b/checks/oop_advanced/oop_advanced3.py index 924f784..d9eb8d0 100644 --- a/checks/oop_advanced/oop_advanced3.py +++ b/checks/oop_advanced/oop_advanced3.py @@ -1,3 +1,4 @@ shapes = [Square(3), Rectangle(2, 5)] -assert total_area(shapes) == 19 +area = total_area(shapes) +assert area == 19, f"expected total_area(shapes) == 19, got {area!r}" print("oop_advanced3 ok") From 9797fbaa061f0e18ed3b83c0e60a77bc2453b787 Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:23:56 +0300 Subject: [PATCH 07/11] checks: add actionable assertion messages to advanced OOP checks (#107) --- checks/oop_advanced/oop_advanced4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/oop_advanced/oop_advanced4.py b/checks/oop_advanced/oop_advanced4.py index b3b5bd8..db59a9c 100644 --- a/checks/oop_advanced/oop_advanced4.py +++ b/checks/oop_advanced/oop_advanced4.py @@ -1,3 +1,3 @@ user = User("Lin") -assert user.name == "Lin" +assert user.name == "Lin", f"expected name 'Lin', got {user.name!r}" print("oop_advanced4 ok") From 91528fb97c18da3c310bbd432ff68dcafde4a9f9 Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:23:58 +0300 Subject: [PATCH 08/11] checks: add actionable assertion messages to advanced OOP checks (#107) --- checks/oop_advanced/oop_advanced5.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/checks/oop_advanced/oop_advanced5.py b/checks/oop_advanced/oop_advanced5.py index 3ef9051..fcae36b 100644 --- a/checks/oop_advanced/oop_advanced5.py +++ b/checks/oop_advanced/oop_advanced5.py @@ -1,3 +1,4 @@ playlist = Playlist(["one", "two", "three"]) -assert len(playlist) == 3 +count = len(playlist) +assert count == 3, f"expected len(playlist) == 3, got {count!r}" print("oop_advanced5 ok") From c4c139f5182b797c580f93c16fafca3305ac2e9a Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:23:59 +0300 Subject: [PATCH 09/11] checks: add actionable assertion messages to advanced OOP checks (#107) --- checks/oop_advanced/oop_advanced6.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/checks/oop_advanced/oop_advanced6.py b/checks/oop_advanced/oop_advanced6.py index 5d5043f..23a05df 100644 --- a/checks/oop_advanced/oop_advanced6.py +++ b/checks/oop_advanced/oop_advanced6.py @@ -1,2 +1,5 @@ -assert repr(Point(2, 5)) == "Point(x=2, y=5)" +text = repr(Point(2, 5)) +assert text == "Point(x=2, y=5)", ( + f"expected repr 'Point(x=2, y=5)', got {text!r}" +) print("oop_advanced6 ok") From edbe590f6a3b851c0a91112dfe7010a2ea214e5f Mon Sep 17 00:00:00 2001 From: mercael Date: Sat, 17 Oct 2026 10:24:04 +0300 Subject: [PATCH 10/11] test: drop helper test not part of #107 scope --- tests/test_oop_advanced_checks.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 tests/test_oop_advanced_checks.py diff --git a/tests/test_oop_advanced_checks.py b/tests/test_oop_advanced_checks.py deleted file mode 100644 index 1be3a78..0000000 --- a/tests/test_oop_advanced_checks.py +++ /dev/null @@ -1,18 +0,0 @@ -import ast -from pathlib import Path - - -CHECKS_DIR = Path(__file__).resolve().parent.parent / "checks" / "oop_advanced" - - -def test_check_lines_within_80_chars(): - for path in sorted(CHECKS_DIR.glob("oop_advanced*.py")): - for lineno, line in enumerate(path.read_text().splitlines(), 1): - assert len(line) <= 80, ( - "%s:%d exceeds 80 chars (%d)" % (path.name, lineno, len(line)) - ) - - -def test_checks_parse(): - for path in sorted(CHECKS_DIR.glob("oop_advanced*.py")): - ast.parse(path.read_text(), filename=str(path)) From 2b954abd8ab4a62c13f00744cb5c631b1983007f Mon Sep 17 00:00:00 2001 From: Abhik Sarkar Date: Wed, 4 Nov 2026 13:34:18 +0530 Subject: [PATCH 11/11] fix(curriculum): preserve learner globals in advanced OOP checks Keep the original assertions and helper statements while adding messages. Avoid temporary bindings that overwrite learner variables in the shared namespace. Add a regression for the learner-owned area variable and the required file-location comments. Refs #107. --- checks/oop_advanced/oop_advanced1.py | 4 ++-- checks/oop_advanced/oop_advanced2.py | 1 + checks/oop_advanced/oop_advanced3.py | 4 ++-- checks/oop_advanced/oop_advanced4.py | 1 + checks/oop_advanced/oop_advanced5.py | 4 ++-- checks/oop_advanced/oop_advanced6.py | 6 +++--- tests/unit/test_check_namespaces.py | 21 +++++++++++++++++++++ 7 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/unit/test_check_namespaces.py diff --git a/checks/oop_advanced/oop_advanced1.py b/checks/oop_advanced/oop_advanced1.py index 5ac3c1f..02866da 100644 --- a/checks/oop_advanced/oop_advanced1.py +++ b/checks/oop_advanced/oop_advanced1.py @@ -1,5 +1,5 @@ +# checks/oop_advanced/oop_advanced1.py dog = Dog() assert isinstance(dog, Animal), "Dog() should be an instance of Animal" -got = dog.speak() -assert got == "sound", f"Dog().speak() should return 'sound', got {got!r}" +assert dog.speak() == "sound", "Dog().speak() should return 'sound'" print("oop_advanced1 ok") diff --git a/checks/oop_advanced/oop_advanced2.py b/checks/oop_advanced/oop_advanced2.py index 5aee10c..349cd95 100644 --- a/checks/oop_advanced/oop_advanced2.py +++ b/checks/oop_advanced/oop_advanced2.py @@ -1,3 +1,4 @@ +# checks/oop_advanced/oop_advanced2.py employee = Employee("Ada", "Engineer") assert employee.name == "Ada", f"expected name 'Ada', got {employee.name!r}" assert employee.role == "Engineer", ( diff --git a/checks/oop_advanced/oop_advanced3.py b/checks/oop_advanced/oop_advanced3.py index d9eb8d0..ae65e1d 100644 --- a/checks/oop_advanced/oop_advanced3.py +++ b/checks/oop_advanced/oop_advanced3.py @@ -1,4 +1,4 @@ +# checks/oop_advanced/oop_advanced3.py shapes = [Square(3), Rectangle(2, 5)] -area = total_area(shapes) -assert area == 19, f"expected total_area(shapes) == 19, got {area!r}" +assert total_area(shapes) == 19, "total_area(shapes) should be 19" print("oop_advanced3 ok") diff --git a/checks/oop_advanced/oop_advanced4.py b/checks/oop_advanced/oop_advanced4.py index db59a9c..94bbfff 100644 --- a/checks/oop_advanced/oop_advanced4.py +++ b/checks/oop_advanced/oop_advanced4.py @@ -1,3 +1,4 @@ +# checks/oop_advanced/oop_advanced4.py user = User("Lin") assert user.name == "Lin", f"expected name 'Lin', got {user.name!r}" print("oop_advanced4 ok") diff --git a/checks/oop_advanced/oop_advanced5.py b/checks/oop_advanced/oop_advanced5.py index fcae36b..421b723 100644 --- a/checks/oop_advanced/oop_advanced5.py +++ b/checks/oop_advanced/oop_advanced5.py @@ -1,4 +1,4 @@ +# checks/oop_advanced/oop_advanced5.py playlist = Playlist(["one", "two", "three"]) -count = len(playlist) -assert count == 3, f"expected len(playlist) == 3, got {count!r}" +assert len(playlist) == 3, "len(playlist) should be 3" print("oop_advanced5 ok") diff --git a/checks/oop_advanced/oop_advanced6.py b/checks/oop_advanced/oop_advanced6.py index 23a05df..cbeef84 100644 --- a/checks/oop_advanced/oop_advanced6.py +++ b/checks/oop_advanced/oop_advanced6.py @@ -1,5 +1,5 @@ -text = repr(Point(2, 5)) -assert text == "Point(x=2, y=5)", ( - f"expected repr 'Point(x=2, y=5)', got {text!r}" +# checks/oop_advanced/oop_advanced6.py +assert repr(Point(2, 5)) == "Point(x=2, y=5)", ( + "repr(Point(2, 5)) should be 'Point(x=2, y=5)'" ) print("oop_advanced6 ok") diff --git a/tests/unit/test_check_namespaces.py b/tests/unit/test_check_namespaces.py new file mode 100644 index 0000000..bf09e6d --- /dev/null +++ b/tests/unit/test_check_namespaces.py @@ -0,0 +1,21 @@ +# tests/unit/test_check_namespaces.py +from pathlib import Path + +from solutions._answers import ANSWERS + + +def test_area_check_preserves_learner_globals() -> None: + namespace = {"__name__": "__main__"} + exec(ANSWERS["oop_advanced3"], namespace) + namespace["area"] = 0 + check = ( + Path(__file__).resolve().parents[2] + / "checks" / "oop_advanced" / "oop_advanced3.py" + ) + + exec( + compile(check.read_text(encoding="utf-8"), str(check), "exec"), + namespace, + ) + + assert namespace["area"] == 0