078. Try/Except
Handle expected errors gracefully instead of crashing
078. Try/Except
He reads /proc/*/status to get RSS for each process. Some entries vanish mid-read (the process exited). Some fields are not integers. Without error handling, one bad line crashes the whole collection loop.
# Dangerous: crashes on any bad value
def parse_rss_kb(line):
return int(line.split()[1])
# Safe: returns None for unparseable lines
def parse_rss_kb(line):
try:
return int(line.split()[1])
except (ValueError, IndexError):
return NoneThe safe version collects stats for every healthy process and skips bad entries β exactly what a production monitoring tool does.
π‘ Design principle: Functions that can legitimately fail (parsing, I/O, network) should use try/except and return a sentinel value (
None,[],{}) so the caller can continue working. Functions that should never fail (logic bugs) should let exceptions propagate.
Learning objectives
- Wrap risky code in try/except blocks
- Catch specific exception types
- Return sensible values when exceptions occur
Key concepts
- try
- except
- exception handling
- ValueError
- KeyError
Try it
Concept detail
try/except catches exceptions so the program continues instead of crashing.
try:
result = risky_operation()
except SomeError:
result = fallback_valueExecution flow:
- Python runs the code inside try
- If no exception β skips the except block, continues normally
- If the specified exception is raised β jumps to except block
- After except β execution continues as normal
Be specific β always name the exception type: except ValueError: # catches value errors only except (ValueError, TypeError): # catches either
NEVER do bare βexcept:β β it catches KeyboardInterrupt and SystemExit, making your program impossible to Ctrl-C out of.
Access the exception object: except ValueError as e: print(fβBad value: {e}β) # shows the error message
WHY catch exceptions instead of pre-checking: # Pre-check (LBYL β Look Before You Leap): if key in d: value = d[key] # try/except (EAFP β Easier to Ask Forgiveness than Permission): try: value = d[key] except KeyError: value = default
EAFP is the Pythonic style β itβs faster when errors are rare and handles race conditions that pre-checks canβt.
Solution
def safe_int(s):
try:
return int(s)
except ValueError:
return None
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
def safe_get(d, key, default=None):
try:
return d[key]
except KeyError:
return defaultTests
def test_safe_int_valid():
assert safe_int("42") == 42
assert safe_int("-7") == -7
assert safe_int("0") == 0
def test_safe_int_invalid():
assert safe_int("abc") is None
assert safe_int("3.14") is None
assert safe_int("") is None
def test_safe_divide_normal():
assert safe_divide(10, 2) == 5.0
assert safe_divide(7, 2) == 3.5
def test_safe_divide_zero():
assert safe_divide(5, 0) is None
assert safe_divide(0, 0) is None
def test_safe_get_exists():
d = {"a": 1, "b": 2}
assert safe_get(d, "a") == 1
assert safe_get(d, "b") == 2
def test_safe_get_missing():
d = {"a": 1}
assert safe_get(d, "z") is None
assert safe_get(d, "z", 99) == 99
assert safe_get(d, "z", "fallback") == "fallback"