Bank Ledger with Custom Exceptions
Hard · Errors
Custom exceptions let callers catch exactly the errors they expect. A common pattern is one base class for your module plus specific subclasses, so except LedgerError catches all of them.
1. Define LedgerError(Exception) and three subclasses of it: InvalidAmount, UnknownAccount and InsufficientFunds.
2. Complete Ledger. It starts from a dict of account balances (whole numbers). Checks happen in this order, and a failed operation changes nothing:
- the amount must be an int greater than 0, else raise InvalidAmount("invalid amount: <amount>")
- every account must exist (the source is checked before the target), else raise UnknownAccount("unknown account: <name>")
- the source needs enough money, else raise InsufficientFunds("<source> has <balance>, needs <amount>")
Methods: deposit(name, amount), transfer(source, target, amount), transfer_many(transfers) and balances(), which returns a copy of the balances dict.
transfer_many takes a list of [source, target, amount] and is all-or-nothing. Transfers run in order, so an earlier one can fund a later one. If any of them fails, restore every balance to what it was before the batch and raise that error.
The driver run_ledger(balances, ops) is already written. It catches LedgerError only, so your exceptions must inherit from it.