""" Interactive Witch Trial ======================= This tutorial demonstrates: - how to create a custom exception; - how to inherit from a built-in exception; - how to raise an exception with `raise`; - how to catch it with `try` and `except`; - how `finally` always runs; - how to validate user input with a dictionary; - how to repeat a program with `while True`; - how to leave a loop with `break`; - how to use `if __name__ == "__main__"`. Inspired by Monty Python and the Holy Grail. """ class WitchWeightError(ValueError): """Raised when the alleged witch does not weigh the same as a duck.""" pass class WitchTrial: def __init__(self): # Strictly scientific medieval measurements, obviously. self.duck_weight = 4.2 self.is_made_of_wood = True def evaluate_citizen(self, woman_weight: float) -> None: print("\nBEDEVERE: “Well... Why do witches burn?”") print("VILLAGER: “Because they are made of... WOOD!”") print("BEDEVERE: “And what else floats on water?”") print("A KNIGHT: “A duck!”") print("-" * 60) if woman_weight > self.duck_weight: raise WitchWeightError( f"She weighs {woman_weight} pounds: much more than a duck.\n" "She may be a sturdy citizen, but medieval science remains uncertain." ) if woman_weight < self.duck_weight: raise WitchWeightError( f"She weighs only {woman_weight} pounds: less than a duck. " "Even the villagers find that slightly suspicious." ) print("⚖️ Verdict: she weighs exactly the same as a duck!") if self.is_made_of_wood: print("🪵 Logic: therefore, she is made of wood...") print("🔥 Perfectly scientific conclusion: SHE IS A WITCH!") else: print("BEDEVERE: “Wait... she is not made of wood?”") def ask_for_weight() -> float: choices = { "a": 4.2, # The correct answer "b": 88.0, "c": 200.0, } print("How much does the accused woman weigh?") print(" a / 4.2 pounds") print(" b / 88 pounds") print(" c / 200 pounds") while True: answer = input("\nType a, b or c: ").strip().lower() if answer in choices: return choices[answer] print( "PATSY: “That choice is not correct, O my lord.\n" "\tOnly a, b and c are accepted in this case.\n" "\tRemember the Holy Hand Grenade of Antioch,\n" "\twhere three was the only possible choice!”" ) def replay() -> None: trial = WitchTrial() try: selected_weight = ask_for_weight() trial.evaluate_citizen(selected_weight) except WitchWeightError as error: print(f"\n❌ MEDIEVAL EXCEPTION: {error}") print("BEDEVERE: “We shall have to review the scientific protocol.”") finally: print("\n" + "-" * 60) print("This program is dedicated to Monty Python.") print("If you smiled, mission accomplished.") print("-" * 60) if __name__ == "__main__": # Run this block only when the file is executed directly. # Keep replaying the program until the user chooses to quit. while True: replay() answer = input( "\nPress ENTER to always look on the bright side of life... 🎵\n" 'Type "r", then press ENTER, to replay: ' ).strip().lower() if answer != "r": break