AI Skill Report Card
Handling Python Exceptions and Warnings
Quick Start14 / 15
Python# Raise the most specific built-in exception that fits def get_item(d, key): if key not in d: raise KeyError(f"{key!r} not found") return d[key] # Chain exceptions to preserve context try: get_item({}, "x") except KeyError as e: raise RuntimeError("lookup failed") from e # Emit a warning instead of failing hard import warnings def old_api(): warnings.warn("old_api() is deprecated, use new_api()", DeprecationWarning, stacklevel=2)
Recommendation▾
Add an example showing a bad/anti-pattern output alongside a good one for contrast
Workflow14 / 15
Progress checklist for designing error handling:
- Identify the failure mode: programmer error, runtime/environment error, or a non-fatal condition
- Pick the narrowest matching built-in exception/warning class (see table below)
- If no built-in fits, define a custom exception inheriting from the closest built-in base
- Decide whether to chain (
raise X from Y) or suppress (raise X from None) the causing exception - Use
try/except/else/finallycorrectly:elsefor success-only code,finallyfor cleanup - For warnings, choose the right category and set
stacklevelso the reported location is the caller's - Configure warning filters only at the application entry point, not inside library code
Exception class selection
| Situation | Use |
|---|---|
| Wrong type passed | TypeError |
| Value of correct type but invalid | ValueError |
| Key missing from mapping | KeyError |
| Index out of range | IndexError |
| Attribute doesn't exist | AttributeError |
| Name not found | NameError (usually not raised manually) |
| Operation not supported / not yet implemented | NotImplementedError |
| Feature unsupported on this platform/config | NotImplementedError or custom |
| Invalid internal state / logic bug | AssertionError (via assert) or RuntimeError |
| File/OS-level errors | OSError (and subclasses: FileNotFoundError, PermissionError, IsADirectoryError, TimeoutError) |
| Arithmetic problems | ArithmeticError subclasses: ZeroDivisionError, OverflowError |
| Iteration exhausted | StopIteration (don't catch manually in generators) |
| User interrupted program | KeyboardInterrupt (never catch broadly) |
| Recursion too deep | RecursionError |
| Custom domain error | Subclass Exception directly (never subclass BaseException unless it must escape normal handling like SystemExit) |
Warning category selection
| Situation | Use |
|---|---|
| API scheduled for removal, visible to end users | DeprecationWarning |
| Deprecation only relevant to other library authors | PendingDeprecationWarning |
| Code likely has a bug | RuntimeWarning |
| Syntax is deprecated/questionable | SyntaxWarning |
| Use of obsolete feature that still works | FutureWarning (user-facing, unlike DeprecationWarning) |
| Import-related issue | ImportWarning |
| Unicode-related issue | UnicodeWarning |
| Bytes/bytearray related issue | BytesWarning |
| Resource not cleaned up (e.g., unclosed file) | ResourceWarning |
| Custom category | Subclass Warning |
Recommendation▾
Include a brief example of warning filter configuration at an application entry point
Examples18 / 20
Example 1: Wrapping a low-level error with context Input:
Pythondef load_config(path): with open(path) as f: return parse(f.read())
Output:
Pythondef load_config(path): try: with open(path) as f: return parse(f.read()) except OSError as e: raise RuntimeError(f"could not load config from {path}") from e except ValueError as e: raise RuntimeError(f"config at {path} is malformed") from e
Example 2: Deprecating a function
Input: def send(msg): ... needs to be replaced by send_message(msg).
Output:
Pythonimport warnings def send(msg): warnings.warn( "send() is deprecated since v2.0; use send_message() instead", DeprecationWarning, stacklevel=2, ) return send_message(msg)
Example 3: Custom exception hierarchy Input: A library needs its own errors for a parsing module. Output:
Pythonclass ParserError(Exception): """Base class for all parser errors.""" class SyntaxParseError(ParserError): """Raised when input violates grammar rules.""" class EncodingParseError(ParserError, ValueError): """Raised when input encoding is invalid."""
Example 4: Suppressing exception chaining Input: Re-raising a domain error without leaking internal details. Output:
Pythontry: _internal_lookup(key) except _InternalKeyMiss: raise KeyError(key) from None
Recommendation▾
Consider trimming the two reference tables slightly or merging overlapping rows for conciseness
Best Practices
- Prefer the most specific built-in exception; only create a custom one when none fits.
- Always inherit custom exceptions from
Exception, notBaseException. - Use
raise ... from errto preserve the traceback chain; usefrom Noneto intentionally hide internal implementation exceptions. - Use
stacklevel=2(or higher) inwarnings.warnso the warning points to the caller, not the library internals. - Catch narrow exception types; avoid bare
except:or broadexcept Exception:unless re-raising or logging at a top-level boundary. - Use
elseclause intryblocks for code that should only run when no exception occurred. - Use
finallyfor cleanup that must run regardless of success/failure (or prefer context managers). - Group related exceptions with
except (TypeError, ValueError):rather than duplicating handler bodies. - For libraries, never call
warnings.simplefilteror change global filter state — that's the application's decision.
Common Pitfalls
- Don't catch
BaseException,KeyboardInterrupt, orSystemExitunintentionally with a bareexcept:. - Don't swallow exceptions silently (
except Exception: pass) — at minimum log them. - Don't raise a bare
Exception()orRuntimeError()for everything — it loses semantic meaning for callers. - Don't forget
stacklevelinwarnings.warn; default (1) makes the warning appear to originate inside your own function. - Don't use warnings for conditions that should actually stop execution — that's what exceptions are for.
- Don't mutate
__traceback__or re-raise withoutfromwhen context is genuinely useful for debugging. - Don't subclass multiple unrelated exception bases without checking MRO consistency (e.g., mixing
ValueErrorandTypeErrorbases can create confusing catches).