Exercise 1: A Real, Catchable Page Fault — Possible Solution ==================================================================== THE TEST ------------------------------ mem = PhysicalMemoryFramed(num_frames=4) pt = PageTable(page_size=64) pfn = mem.alloc_frame() pt.map_page(vpn=0, pfn=pfn) try: pt.translate(64 * 3) # virtual page 3 -- never mapped fault_raised = False except PageFault as e: fault_raised = True print(e) valid = pt.translate(5) # a genuinely mapped address, right next door RESULT ------------------------------ translating an unmapped virtual address raised: no mapping for virtual page 3 (virtual address 192) translating a genuinely mapped address still works fine: 5 fault_raised is True, and the second translate() call (a real, mapped address) succeeds normally and returns the correct physical address. WHY THE FAULT IS RAISED CLEANLY, NOT AS A KEYERROR ------------------------------ translate()'s own logic checks membership explicitly before ever touching self.table[vpn]: if vpn not in self.table: raise PageFault(f"no mapping for virtual page {vpn}") pfn = self.table[vpn] Without that explicit check, self.table[vpn] on an unmapped vpn would raise a raw Python KeyError instead -- technically also an exception, but one that exposes translate()'s own internal dict-based implementation detail to anyone catching it, rather than a real, purpose-built PageFault type that means something specific: "this virtual address has no mapping." A caller catching PageFault specifically doesn't need to know or care that a dict is involved at all. WHY THE NEIGHBORING VALID ADDRESS STILL WORKS ------------------------------ Virtual address 5 belongs to virtual page 0 (5 // 64 == 0), which IS mapped -- to pfn, the frame allocated at the start of the test. This confirms the PageFault for address 192 is specific to that one unmapped page, not a sign that the whole page table or the translate() method itself is broken. A single missing mapping doesn't poison translation for every other, genuinely valid address. WHY THIS WORKS AS AN ANSWER ------------------------------ Testing both the failing case (a real page fault, correctly typed and catchable) and a neighboring success case in the same test confirms the fault-handling code path is deliberate and precise -- it fires exactly when it should, and only then, rather than being a fragile side effect of how the page table happens to be implemented internally.