website/content/blog/python-refactoring-exceptions-context-manager.md
2024-02-01 21:04:33 -05:00

1.7 KiB

title date draft tags math medium_enabled
Quick Python: Refactoring Exceptions with Context Manager 2024-02-01T20:48:21-05:00 false
Python
false false

I generally find exception syntax a little clunky...

try:
    for _ in range(5):
        sleep(1)
except KeyboardInterrupt:
    # Awesome task 1
    # Awesome task 2...
    pass

Especially if you end up capturing the same exceptions and handling it the same way.

try:
    for _ in range(5):
        sleep(1)
except KeyboardInterrupt:
    # Awesome task 1
    # Awesome task 2...
    pass

try:
    for _ in range(2):
        sleep(1)
except KeyboardInterrupt:
    # Awesome task 1
    # Awesome task 2...
    pass

One way to make our code more DRY (don't-repeat-yourself) is to make use of Python's context managers.

@contextmanager
def handle_sigint():
    try:
        yield
    except KeyboardInterrupt:
        # Awesome task 1
        # Awesome task 2...
        pass

Using the context manager, everything within the indented block gets executed within the try block.

with handle_sigint():
    for _ in range(5):
        sleep(1)

with handle_sigint():
    for _ in range(2):
        sleep(1)

In fact, we can write this in a generic way to give us an alternative syntax for handling exceptions.

@contextmanager
def handle_exception(f, *exceptions):
    try:
        yield
    except exceptions as e:
        f(e)

For example, let's tell the user that we're explicitly ignoring their exception

def ignore(e):
    print("Ignoring", e.__class__.__name__)

with handle_exception(ignore, NotImplementedError, KeyboardInterrupt):
    for _ in range(5):
        sleep(1)