ooad1-7 Exercise 1: Return a Book, Drawn From the Code and Then Checked =========================================================================== return.drawn.mmd (Mermaid 12.1.0), drawn by reading library_b.py without running it: sequenceDiagram actor Librarian participant LoanDesk participant Catalogue participant Member participant Loan Librarian->>LoanDesk: return_book(barcode, today) LoanDesk->>Catalogue: open_loan_for(barcode) Catalogue-->>LoanDesk: member and loan LoanDesk->>Catalogue: copy(barcode) Catalogue-->>LoanDesk: Copy LoanDesk->>Member: give_back(loan, copy, today) Member->>Loan: fine(today, kind) Loan->>Loan: days_overdue(today) Loan-->>Member: fine (pence) Member-->>LoanDesk: fine (pence) LoanDesk-->>Librarian: fine (pence) ex1.py (needs tracetool.py, conformance.py and library_b.py from the chapter folder; run from there): """Exercise 1: check the hand-drawn 'return' diagram against what the code does. Run from the chapter folder: py ex1/ex1.py""" import difflib import pathlib import sys import types from datetime import date HERE = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(HERE)) from conformance import ordered_calls, HIDDEN # noqa: E402 from tracetool import to_mermaid, trace # noqa: E402 import library_b as lib # noqa: E402 HIDDEN |= {"loan_limit", "fine_per_day"} # the diagram hides the member kind's answers desk = lib.LoanDesk([lib.Member("M1", date(2027, 1, 1), lib.KINDS["standard"])], [lib.Copy("B0")]) desk.borrow("M1", "B0", date(2026, 10, 10)) # an open loan, made before tracing starts events, fine, error = trace("library_b.py", desk.return_book, "B0", date(2026, 11, 3)) actual = ordered_calls(to_mermaid(events)) drawn = ordered_calls((pathlib.Path(__file__).parent / "return.drawn.mmd").read_text(encoding="utf8")) print(f"drawn: {len(drawn)} calls | traced (same level of detail): {len(actual)} calls | fine {fine}p") if actual == drawn: print("the drawn diagram matches the code") else: print("DIFFERENCES ('-' drawn, '+' actual):") for line in difflib.unified_diff(drawn, actual, lineterm="", n=0): if not line.startswith(("---", "+++", "@@")): print(" ", line) Run with: py ex1/ex1.py Output: drawn: 6 calls | traced (same level of detail): 6 calls | fine 30p the drawn diagram matches the code What the check ignores: like the borrow check, it leaves out the helper calls the diagram hides (open_loans and is_open, which find the open loan inside the catalogue, and the member kind's fine_per_day, which the Loan asks for). The first two are bookkeeping; the third is chapter 6's polymorphism, which a more detailed diagram would show. WHY THIS WORKS AS AN ANSWER --------------------------- The diagram has one participant per object that holds a responsibility: the desk receives the request, the catalogue finds the loan and copy, the member gives the book back, the loan works out the fine (and asks itself for the days overdue). Drawing it from the code first, then tracing, is a real test: the match here means my reading of the code was right at this level of detail. A mismatch would have meant either the diagram or my understanding was wrong.