
Photo by Karub on Pexels
We’ve all been there: staring at a sprawling try-finally block in Python like it’s a crime scene, desperately trying to remember if we closed the database connection, flushed the file buffer, or sacrificed a rubber duck to the systems gods. Enter Python context managers—your ticket to cleaner code, fewer memory leaks, and the smug satisfaction of writing fewer lines than your junior dev.
Welcome back to the Old Lane 17 workshop, where we prefer our code lean, our terminals uncluttered, and our enterprise bloat locked safely outside. Today, we’re looking at Python’s contextlib module and the with statement. If you’re still manually opening, writing, and closing resources like it’s 1999, pull up a chair.
The Problem: The try...finally Hangover
Let’s look at the classic, enterprise-grade nightmare of safely handling a file or a database transaction. It usually looks something like this:
python# The "I enjoy typing boilerplate" approach
file_obj = open("ancient_secrets.txt", "w")
try:
file_obj.write("The secret is: more coffee.")
except Exception as e:
print(f"Oops: {e}")
raise
finally:
file_obj.close()
print("Phew, resource closed.")
It works, sure. It’s also exhausting. If you forget that finally block, you’re leaking file descriptors until your OS taps out and throws a Too many open files error at 3:00 AM on a Saturday.
The Clean Solution: Enter the with Statement
Python’s with statement handles the setup and teardown automatically. Under the hood, it invokes the object’s __enter__ and __exit__ magic methods.
Here is that exact same logic, minus the existential dread:
python# The Old Lane 17 clean-code way
with open("ancient_secrets.txt", "w") as file_obj:
file_obj.write("The secret is: more coffee.")
# Boom. Handled. Even if an exception explodes here,
# Python guarantees the file gets closed.
Writing Your Own Custom Context Managers
Using built-in ones is great, but what about real-world scenarios—like timing a block of code or temporarily changing a working directory?
Instead of writing a bulky class with __enter__ and __exit__ methods, Python’s contextlib gives us the contextmanager decorator. It turns a simple generator into a context manager using yield.
Here is a practical, copy-pasteable timer for your slow database queries or sluggish API calls:
pythonimport time
from contextlib import contextmanager
@contextmanager
def time_it(task_name: str):
start_time = time.perf_counter()
try:
yield # Control jumps back to the 'with' block here
finally:
elapsed = time.perf_counter() - start_time
print(f"[Done] '{task_name}' took {elapsed:.4f} seconds.")
# How you use it in the wild:
with time_it("Generating Infinite Wealth"):
time.sleep(1.25) # Simulating hard work
# If an exception happens here, the timer still prints!
A Real-World Gotcha: Swallowing Exceptions
Be careful with your try...finally blocks inside custom generators. If an exception occurs inside the with block, it gets raised at the point of the yield.
If you catch the exception inside your generator without re-raising it, you accidentally swallow errors. Your code will silently fail, leaving you staring at logs wondering why nothing happened.
Keep your teardown logic robust, let exceptions bubble up unless you explicitly mean to handle them, and keep your terminal workflows clean. Until next time, keep your functions small and your coffee black.
Published by the Old Lane Developer Desk.
Disclaimer: This article was generated and formatted with the assistance of Artificial Intelligence.


