prize18-10 Exercise 3: Locating the Faulty Stage ================================================= Plan: build four correct round-trip stages (file, JSON, database, export). For each experiment replace exactly one stage by a faulty version and call first_bad_stage, which feeds the text through the stages in order and returns the first one whose output differs from the original. ex3.py: import json, sqlite3, tempfile, pathlib original = "Zo\u00eb\u2019s caf\u00e9 \u20ac5" tmp = pathlib.Path(tempfile.mkdtemp()) def lenient_decode(raw): out = [] for b in raw: try: out.append(bytes([b]).decode("cp1252")) except UnicodeDecodeError: out.append(chr(b)) return "".join(out) def file_ok(s): p = tmp / "a.txt" p.write_text(s, encoding="utf-8", newline="\n") return p.read_text(encoding="utf-8") def json_ok(s): return json.loads(json.dumps({"v": s}))["v"] def db_ok(s): con = sqlite3.connect(":memory:") con.execute("CREATE TABLE t (v TEXT)") con.execute("INSERT INTO t VALUES (?)", (s,)) return con.execute("SELECT v FROM t").fetchone()[0] def wrong_read(s): return lenient_decode(s.encode("utf-8")) def to_ascii(s): return s.encode("ascii", errors="replace").decode("ascii") def first_bad_stage(text, stages): current = text for name, fn in stages: current = fn(current) if current != text: return name, current return None, current names = ["file", "json", "database", "export"] good = {"file": file_ok, "json": json_ok, "database": db_ok, "export": json_ok} for fault, bad in [(None, None), ("file", wrong_read), ("database", wrong_read), ("export", to_ascii)]: stages = [(n, bad if n == fault else good[n]) for n in names] name, result = first_bad_stage(original, stages) print(f"fault in {fault!s:9} -> first bad stage: {name!s:9} {result}") Real output (Python 3.14): fault in None -> first bad stage: None Zoë’s café €5 fault in file -> first bad stage: file Zoë’s café €5 fault in database -> first bad stage: database Zoë’s café €5 fault in export -> first bad stage: export Zo??s caf? ?5 Each fault is reported at the stage where it was injected, and the fault-free run reports none. The mojibake outputs could be repaired with peel (they are UTF-8 bytes read as Windows-1252); the last output has question marks where characters could not be encoded, so the information is gone: the repair must be made by fixing the export stage and exporting again from the original data.