When implementing error handling logic, be deliberate about which exception types you catch and where you handle them. 1. **Catch `Exception`, not `BaseException`** - Classes that directly inherit from `BaseException` (like `GeneratorExit` or `KeyboardInterrupt`) are not technical errors and should generally propagate:
When implementing error handling logic, be deliberate about which exception types you catch and where you handle them.
Exception
, not BaseException
- Classes that directly inherit from BaseException
(like GeneratorExit
or KeyboardInterrupt
) are not technical errors and should generally propagate:# Good
try:
# operation that may fail
except Exception as exc: # Only catches errors, not control flow exceptions
record_error(exc)
# Bad
try:
# operation that may fail
except BaseException as exc: # Catches everything including KeyboardInterrupt
record_error(exc)
# Good: Handle file access errors in file-reading functions
def _read_file(file_path: str) -> Optional[bytes]:
try:
with open(file_path, "rb") as file:
return file.read()
except FileNotFoundError as e:
logger.exception(f"Failed to read file: {e.filename}")
return None
# Bad: Force higher-level code to handle file-specific errors
def _read_file(file_path: str) -> bytes:
with open(file_path, "rb") as file:
return file.read()
# FileNotFoundError bubbles up to caller
Preserve valuable error information - When handling exceptions, ensure meaningful error details are preserved in logs or return values.
Balance fail-fast and graceful degradation - Consider the context:
Enter the URL of a public GitHub repository