8. Error Handling

Catch only what you expect, clean up reliably, and preserve useful error context.

Question: How does Python handle errors?

Answer: Python uses try...except blocks. You try a block of code, and if an error (exception) occurs, the except block for that specific error type is executed. The finally block runs no matter what, which is useful for cleanup operations.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero!")
finally:
    print("This always runs.")

Question: What is a context manager?

Answer: A context manager is an object designed to be used with the with statement. It guarantees that setup and teardown operations (like opening and closing a file or a database connection) are always performed correctly.

Question: What is try/except/else/finally flow?

Answer: else runs only if no exception occurred in the try block; finally runs always. Use else for code that should not run if an exception happened.

try:
    x = parse(data)
except ValueError:
    x = default
else:
    cache.save(x)
finally:
    cleanup()

Question: How do you define custom exceptions and chain exceptions?

Answer: Subclass Exception for domain errors; use raise NewError(...) from err to preserve the original cause.

class ConfigError(Exception):
    pass

try:
    load()
except FileNotFoundError as e:
    raise ConfigError("config missing") from e

Question: How to create your own context manager?

Answer: Implement __enter__/__exit__ or use contextlib.contextmanager for a generator-based approach.

from contextlib import contextmanager

@contextmanager
def opened(path, mode):
    f = open(path, mode, encoding="utf-8")
    try:
        yield f
    finally:
        f.close()

Question: How do you re-raise or suppress exceptions?

Answer: Use raise inside except to re-raise; use contextlib.suppress to intentionally ignore specific exceptions.

try:
    risky()
except ValueError:
    # log... then re-raise
    raise

from contextlib import suppress
with suppress(FileNotFoundError):
    Path("maybe.txt").unlink()