Exception Handling and Caught Exceptions
A promise is the sending side that matches a future's get(), providing put operations set_value() and set_exception().
Try it: try / except / else / finally
How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.
How it works
- Statements in try run until one raises an exception; the rest of try is skipped.
- Python checks the except clauses in order and runs the first one whose type matches.
- If nothing was raised, the else block runs instead.
- finally runs in every case.
Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.
Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.
Loading the simulation…
Why a Result Needs a Route to a Reader
Programs often start work in one place and need the outcome in another. A task may compute a value in one part of the program while a different part is waiting for that value. The two parts need a way to pass the outcome from one to the other, and that outcome might be a normal result or the news that the work failed.
This article follows one question through the mechanism: when the producing code has a result, or when it has caught an error, how does that outcome reach the code that is waiting on it? Before reading on, try to predict the answer for yourself. If the work fails, what should the waiting side receive: a value, an exception, or nothing at all?
The Future and the Promise as a Pair
A promise is the sending side that matches a future's get(), providing put operations set_value() and set_exception().
A future is a place where a result will appear later. Someone has to put that result there, and someone else reads it out. The future is the reading side: it offers get() to retrieve the result. The promise is the writing side. Its main job is to provide simple put operations that match the future's get(). The two halves are designed to fit together, so a result produced in one place can be delivered to the future that another part of the program is waiting on.
The names future and promise are historical. They do not describe the mechanics perfectly, so it helps to think of them as a pair. The promise is the handle you use to send something in, and the future is the handle you use to take it out.
Think of the promise as the write side and the future as the read side. Whatever the promise sends, the future holds until get() is called.
The Promise-to-Future Channel
The flow works like a relay. A producing task creates a result of type X and places it in a promise. The promise forwards that outcome to the future through one of its two put operations, and the future hands it back when get() is called. The diagram below traces that path from the producer, through the promise and future, to the reader's get() call.
Notice where the outcome is read. The put comes from the sending side, and the get comes from the receiving side. The receiver's call to get() is what finally exposes the outcome. This is why the promise and the future must be matched: the operations on one side are designed to correspond to the operations on the other.
Sending a Value with set_value()
The value path is the ordinary case. The task finishes its work successfully, so it has a real result to give. It calls set_value() on the promise with that result, and the future now holds it. Later, a get() call on that future is where the value becomes visible to the reader.
Tracing a successful task
A task computes the number 42 and must deliver it to a waiting reader through a promise and a future.
- 1Step 1: the task has a result
The work finished normally, so the task holds a real result of type X, here 42.
- 2Step 2: the task calls set_value()
The task passes 42 to the promise through
set_value(), the put operation for a normal result. - 3Step 3: the future holds the value
The promise forwards the outcome to the future, which now holds 42.
- 4Step 4: the reader calls get()
The reader calls
get()on the future and receives 42 as the outcome.
The reader receives the normal value 42 through get(). No exception is involved.
- This is pseudocode that mirrors the promise and future pairing described in the source, not a specific library's exact API.
set_value()is the put operation for a normal result.
When Work Fails: Sending an Exception
Sometimes the work does not succeed. Instead of a value, the promise must carry an exception to the future. The source names a helper for this situation: current_exception(). It refers to the exception that was caught. When the task has caught an error, current_exception() gives you a way to point at that caught exception so it can be passed along to the future.
The purpose is the same as the value path, but the payload is different. The future is still the place where the outcome will be read. The reader learns that something went wrong rather than receiving a normal result. The promise operation for this case is set_exception(), which sends the exception through the promise to the future.
- The try block holds the work that might fail.
- The except block is the handling code that caught the error.
current_exception()refers to the caught exception, andset_exception()sends it to the future. This is a pseudocode illustration of the pattern in the source.
Choosing Between the Two Outcomes
Once the producing code has either a result or a caught error, the choice of promise operation is fixed. The rule from the source is that you pass either a value or an exception as the outcome, never both as a single outcome. The flowchart below shows the decision.
What do you think happens?
The producing code catches an error while doing its work. Which promise operation carries the outcome to the future?
Reveal answer
set_exception()
The source describes set_exception() as the promise operation for passing an exception. get() belongs to the future, the reading side, not the promise.
Following a Caught Exception Step by Step
The sequence below follows the failure path from start to finish. The producing code hits a failure and the handler catches it. The handler uses current_exception() to name the caught exception, and set_exception() carries it through the promise to the future. A separate reader later calls get() to take the outcome out. The order of events is the same whether the put carried a value or an exception.
Tracing a failed task
A task tries to parse a value and the parse fails. The waiting reader must learn that the work failed rather than receive a normal result.
- 1Step 1: the error is caught
The handling code catches the error raised during the work.
- 2Step 2: name the caught exception
current_exception()refers to the exception that was caught, so it can be handed onward. - 3Step 3: send it to the future
set_exception()places that exception into the promise's outcome, which the future now holds. - 4Step 4: the reader calls get()
When the reader calls
get(), it learns the work went wrong instead of receiving a normal value.
The waiting reader sees a failure outcome. No value is delivered for this outcome.
Mistakes When Pairing Promises and Futures
Common mistake Passing both a value and an exception as one outcome
Why it failsThe source says you pass either a value or an exception for a given result, never both as a single outcome.
Do this insteadPick one put operation per outcome: set_value() for a normal result or set_exception() for a failure.
Common mistake Treating current_exception() as a general-purpose error reporter
Why it failscurrent_exception()refers to the exception that was caught, and it makes most sense inside the handling code that caught it.Do this insteadCall current_exception() in the handler where the error was caught, then pass its result to set_exception().
Common mistake Confusing the promise with the reading side
Why it failsThe promise is the sending side with put operations, while the future is the reading side that offers
get().Do this insteadUse set_value() or set_exception() on the promise, and call get() on the future.
Common mistake Assuming the names describe the mechanics exactly
Why it failsThe source notes that the names future and promise are historical and not meant to describe the mechanics perfectly.
Do this insteadThink of them as a matched pair: the promise sends the outcome in, and the future hands it out.
Practice Tracing the Outcome
A producing task finishes its work successfully and has the result 7. Describe which promise operation it calls, what the future holds afterward, and what get() returns to the reader.
Hints
- Ask first whether the task has a normal result or a caught error.
- A normal result uses the value path, and the promise operation for that path is named in the source.
- The reader's
get()exposes whatever the future holds.
A handler catches an error during the work. Explain the full path of that outcome, naming current_exception(), the promise operation used, and what the reader learns when it calls get().
Hints
- Start from the caught exception and follow it to the promise.
- Remember the reader learns the work failed rather than receiving a normal result.
Key Takeaways
- A promise is the sending side that matches a future's
get(); the promise supplies put operations and the future supplies the reading operation. - A promise carries either a value, through
set_value(), or an exception, throughset_exception(), never both as a single outcome. current_exception()refers to a caught exception and is used, inside the handling code, to send that exception to a future throughset_exception().- The future holds the outcome until a reader calls
get(), which is where the value or the failure becomes visible.