Reading Python Tracebacks
An AssertionError is raised when an assert statement's condition is False, and it appears in the traceback with the file name and line number where the assertion failed.
Try it: A Retry Decorator, Step by Step
What a decorator does: @retry(max_attempts) turns a function into a wrapper that calls the original. When the original's assertion fails, the wrapper catches the AssertionError, logs the attempt and runs the WHOLE function again (repeating its side effect) until it succeeds or runs out of attempts, then re-raises (with a traceback whose deepest frame is the assert) or returns a default.
How it works
- @retry(max_attempts=3) is evaluated first: retry(3) returns decorate; decorate(save_reading) returns wrapper; the name save_reading is bound to the wrapper.
- Calling save_reading(42) runs the wrapper, which loops over attempts and calls the original function inside try.
- A false assert raises AssertionError immediately; the original stops and the exception moves up to the wrapper's except block.
- The wrapper logs the failed attempt and starts a new, complete execution of the original: side effects before the assert (saved.append) happen again.
- After the last allowed attempt the wrapper's design decides: raise last_error (the traceback's deepest frame is the assert line; the wrapper frames only caught and re-raised it) or return None.
Default run (36 steps): Line 1: def retry(max_attempts): creates the decorator factory and binds the name retry. Nothing inside it runs yet. … The program finished: the wrapper returned 'saved' from attempt 3. The side effect ran 3 times for one reading: a retried function should be idempotent.
Simplified: One fixed program; you choose the per-attempt sensor results, max_attempts (1-5), whether the write comes before or after the check, and raise vs return None. Your settings are never executed: the lab's model reproduces CPython 3.12's line-by-line execution, printed output and traceback text for all 640 possible programs (checked against real runs). The "database" is a Python list.
Loading the simulation…
The Failure Path
When an assert statement evaluates a condition as False, Python raises an AssertionError immediately. The current function stops at that point, and the exception moves up through the call stack unless an exception handler catches it. A traceback records this path so you can investigate both the function that failed and the calls that led to it.
What do you think happens?
A decorated function reaches a false assertion on its first attempt, and the decorator is configured to retry failures. What happens next?
Reveal answer
The decorator catches the exception, logs the failed attempt, and re-executes the function.
A retry decorator intercepts the wrapped function's execution. It can catch AssertionError, record the attempt, and execute the entire wrapped function again, up to its maximum number of attempts.
Decorator Interception
A decorator places a wrapper between the caller and the original function. The caller starts the wrapped operation, the wrapper invokes the original function, and the original function either completes or raises an exception. If an exception occurs, the wrapper's exception handler can catch it before it reaches the caller. A retry decorator then decides whether to log the failure, retry the operation, or propagate the exception.
Retrying the Whole Operation
A retry mechanism does not jump back to only the assertion line. It re-executes the entire wrapped function. The decorator catches the exception, logs the failure with the attempt number, and starts the function again. This process continues until the function succeeds or the maximum number of attempts is reached.
First Attempt Fails, Second Attempt Proceeds
Trace a decorated function that fails with an exception on its first attempt and is then re-executed by a retry decorator.
- 1Call enters the wrapper
The caller invokes the decorated function, so execution first passes through the decorator's wrapper.
- 2Original function runs
The wrapper invokes the original function. The function performs its work and reaches an assertion.
- 3Assertion fails
Because the assertion condition is False, Python raises an
AssertionErrorand stops the current function. - 4Wrapper catches the exception
The retry decorator catches the exception, logs the failure with the attempt number, and checks whether another attempt is allowed.
- 5Function runs again
The decorator re-executes the entire wrapped function rather than resuming at the failed assertion line.
- 6Process ends
If a later attempt succeeds, execution continues. If attempts continue to fail until the limit is reached, the decorator may raise the last exception or return a default value, depending on its design.
The wrapper controls the transition from failure to retry. The original function is started again from the beginning of its wrapped execution.
Reading the Call Stack
Read traceback frames as a path through the program. Earlier frames show calls that led to the failure. A decorator frame explains why the wrapper was involved, but the deepest frame, listed last, identifies where the exception originated. For an AssertionError, inspect that final relevant frame for the file name, line number, function, and assertion that evaluated to False.
- 1Start with the final traceback frame rather than the decorator frame.
- 2Read the file name to identify the source file.
- 3Read the line number to locate the relevant source line.
- 4Confirm that the line contains the assertion whose condition evaluated to False.
- 5Read earlier frames to understand which caller and wrapper led to the failure.
- 6Check retry logs to determine whether the decorator caught the exception and started another attempt.
Mistakes in Traceback Reading
Common mistake Treating the decorator frame as the failure location
What it looks likeThe traceback includes a retry wrapper, so the wrapper is assumed to contain the faulty assertion.
Why it failsThe wrapper is part of the call stack and may catch the exception, but the deepest frame listed last shows where the exception actually originated.
Do this insteadInspect the final relevant frame and use its file name and line number to locate the failed assertion.
Common mistake Assuming a retry resumes at the failed line
What it looks likeAfter an assertion fails, only that assertion is expected to run again.
Why it failsA retry decorator re-executes the entire wrapped function.
Do this insteadTrace the next attempt from the beginning of the wrapped function and account for repeated side effects.
Common mistake Assuming every caught failure reaches the caller immediately
What it looks likeAn AssertionError appears during the first attempt, so the caller is expected to receive it at once.
Why it failsThe decorator can catch the exception, log the attempt, and retry before deciding what the caller receives.
Do this insteadLook for the decorator's retry decision and maximum-attempt behavior.
Common mistake Ignoring the line number
What it looks likeThe function name is used to search the whole file without checking the traceback location.
Why it failsThe traceback supplies a file name and line number that pinpoint the failed assertion.
Do this insteadOpen the named source file at the reported line and inspect the assertion condition.
Read the traceback in two passes. First, locate the origin of the exception by finding the deepest frame and its source location. Second, reconstruct the route to that frame by reading the earlier caller and decorator frames. This separates the question “Where did it fail?” from the question “Why was this function running?”
Traceback Practice
A traceback lists a caller frame, a decorator frame, and a final frame inside the original function. The final frame identifies a source file and line containing an assert statement. The decorator log says the first attempt failed and another attempt began. Explain where the AssertionError originated, what the decorator did, and what code must be considered when checking for repeated side effects.
Hints
- Use the deepest frame to identify the origin.
- Separate catching the exception from creating the exception.
- A retry starts the entire wrapped function again.
Practice Solution
Interpret the traceback and retry information from the practice situation.
- 1Locate the origin
The
AssertionErrororiginated in the deepest frame, inside the original function at the reported file and line number. - 2Interpret the decorator frame
The decorator frame shows that a wrapper intercepted the call and handled the exception; it is not necessarily where the assertion failed.
- 3Interpret the retry log
The first attempt failed, the decorator logged that attempt, and the retry mechanism started another execution.
- 4Check repeated work
Because the whole wrapped function runs again, any side effects performed before the assertion can occur again on the retry.
The traceback identifies the failed assertion, while the decorator frames and logs explain how the failure was handled and why another full execution occurred.
Key Takeaways
- 1An
AssertionErroris raised immediately when an assert condition is False. - 2The deepest traceback frame, listed last, identifies where the exception originated; decorator frames show the surrounding call path.
- 3A retry decorator can catch the exception, log the attempt, and re-execute the entire wrapped function up to a maximum number of attempts.
- 4Use the traceback's file name and line number to locate the failed assertion in source code.
- 5Because retries repeat the whole function, side effects can happen again on each attempt.
At a glance
- Find the deepest traceback frame to locate the actual
AssertionErrororigin. - Use the file name and line number to inspect the assertion whose condition evaluated to False.
- Remember that a decorator can catch the exception before it reaches the caller.
- A retry re-executes the complete wrapped function, not only the failed line.
- Inspect repeated side effects and retry logs when diagnosing decorated failures.