Free tools Windows power users keep installed
One-click scans. No signup required.
Before changing an exception handler in a Python dispatcher, record what callers can observe on every relevant path: which exceptions escape, what the function returns, what status appears in a returned mapping, and what warning-or-higher logs are emitted. Turn those observations into characterization tests, then make one narrow edit and rerun them. This checks a practical compatibility contract that may include more than exceptions alone.
What counts as the error contract?
A dispatcher’s contract is not necessarily a single error type. Existing callers may distinguish an escaping exception from None, inspect a returned mapping or its integer status, or rely on warning logs. Changing an except clause can alter any of these outcomes even when the function signature stays the same.
As an Amazon Associate I earn from qualifying purchases.
Start by defining the outcomes that callers actually observe. A useful initial pin records, for each fixture:
- The escaping exception type, or that no exception escapes.
- The return shape, such as
Noneor a mapping. - The integer status when the returned value is a mapping.
- The count of warning-or-higher log records.
Leave exact message strings out of the first pin unless a caller depends on them; wording can change during a harmless refactor. Add message assertions later only where they represent a real compatibility requirement.
#1 Best Overall
Inventory callers before writing tests
Tests derived only from the dispatcher can miss behavior that matters to its callers. Find the call sites, then inspect what each caller does with the result and which exception types it catches. For example, searches such as these can help locate relevant branches:
grep -R "dispatcher(" -n .
grep -R "is None|except ValueError|except RuntimeError" -n .
Adjust the search terms and paths to match the project. Treat the results as leads, not proof that every relevant caller has been found. Use the actual call sites to build the fixture list, including paths that test for None or catch an exception.
Rank #2
Build a characterization pin
The following table is a worked example of expected assertions, not a production trace or a universal error taxonomy. Replace or extend its cases to match your dispatcher and callers.
| Fixture | Escaping behavior | Return | Status | WARN+ records |
|---|---|---|---|---|
| Empty body | RuntimeError |
n/a | n/a | 0 |
| Invalid JSON | ValueError |
n/a | n/a | 0 |
| JSON list | ValueError |
n/a | n/a | 0 |
| Missing ID | none | None |
n/a | 1 |
Send raises TypeError |
none | None |
n/a | 1 |
Send raises TimeoutError |
none | None |
n/a | 1 |
| Downstream response | none | mapping | 429 | 1 |
| Downstream success | none | mapping | 200 | 0 |
Write one characterization test per case. Assert the relevant fields directly: use exception assertions for escapes, return-value checks for None or mappings, and captured logs for warning counts. Keep the test input tied to an identified caller path wherever possible.
Check a proposed unified-error rewrite deliberately
Before editing, make the consequence of a broader rewrite visible. For example, temporarily test what would happen if all failures were converted into one unified error shape. Compare that result with the characterization pin: which current cases would change from an escaping exception to a return value, or from None to a mapping? This check helps expose compatibility changes before they are mixed into an extraction.
It does not mean a unified shape is always wrong. It means that changing the contract should be a conscious decision, with callers audited and the change communicated or versioned as appropriate.
Make one narrow edit and rerun the pin
- Copy the existing handler into a branch without editing it. Keep a recoverable baseline before changing exception boundaries.
- Inventory dispatcher call sites and caller checks. Look for result checks such as
is Noneand exception handlers such asexcept ValueErrororexcept RuntimeError. - Fill in the case table from those paths. Write one characterization test for each relevant row and include observed returns and logs, not only exceptions.
- Run the tests locally and confirm the pin is green. The workflow depends on runnable offline tests; if pytest collection is unavailable, stop the refactor until the pin can be run.
- Restore the original handler, then make one extraction or one exception-clause edit. Avoid combining multiple behavior changes in the same step.
- Rerun every characterization case. If an observed shape changes unexpectedly, revert and investigate before continuing.
Why the send handler and parse handler need different care
Preserve the send-side behavior first
In the worked example, a send-side TypeError is caught and becomes None, with a warning. Narrowing that except Exception first could let the TypeError escape instead, changing what callers observe. An extraction can be safer if it preserves the same warning and None result.
Narrow the JSON parse catch only against its observed contract
The example suggests narrowing a JSON parse catch to json.JSONDecodeError when malformed text still becomes the documented ValueError, and non-object JSON still becomes ValueError. Confirm those cases with tests before changing the handler. If the edit uses raise ... from None, it suppresses the displayed cause; add a cause-focused fixture if a caller inspects exception causes.
Best Value
Know what a green pin does not prove
A characterization harness checks only the outputs it records for the fixtures it runs. It does not prove semantic equality, and it may miss timing, retry storms, byte-for-byte identity, or caller paths absent from the fixture set. Treat a green result as evidence that the selected observations stayed stable—not as proof that every effect is unchanged.
- Do not use compatibility pinning as a substitute for security review: it can preserve insecure behavior.
- For a greenfield API, design a coherent error shape rather than inheriting accidental legacy outcomes.
- If a published OpenAPI error schema defines mapping responses, use it to inform mapping assertions; also account for process-local exceptions that escape rather than appear in that schema.
The guiding rule is simple: “Change one except clause only after the pin stays green.”
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Recommended Free Tools




