Start writing your own context managers

Jordan Edmunds, PhD
3 min readJul 27, 2023

And have more readable, reliable, and succinct code

Your code without context managers

Context managers and the languages that support them are amazing. Why?

Let’s say you want to do something “messy” — like creating a temporary file. Maybe you need it for a bit, but you want to delete it when you no longer need it. You might reasonably write code that looks like this:

create_temporary_file()

# do other stuff

delete_temporary_file()

And this is fine — there’s nothing wrong with it at first blush.

But what if your program crashes between when you created and deleted the temporary file? Then your program will terminate, but will leave around this icky mess that you didn’t intend to leave behind.

You could say — aha! Let me add some exception handling to this, and modify your code to be something like this:

create_temporary_file()

try:
# do other stuff
except(Exception):
# aha! I've got you now
finally:
delete_temporary_file()

But I wanted it to crash…

But is that really what you want? What if something went horribly wrong inside the try() block, and your program should have exited? You’ve just papered over this and potentially created a massive problem, just because you wanted to make sure your temporary file got deleted.

Enter context managers.

They allow you to do something when you enter a code block and when you leave, and if an exception is raised inside the code block, they will still execute the code that you want to execute before re-raising the exception.

In python, there are a couple different ways to create a context manager, but I think it’s more illustrative to use a class to do it. Let’s use the example above, where I want to create a temporary file and then delete it when I leave. The context manager would look like this:

class TemporaryFile:
def __enter__(self):
create_temporary_file()

def __exit__(self, *args):
delete_temporary_file()

(the *args here captures information about the exception and traceback we could use, but I’m just ignoring them here)

Then, we can revise our original code and use the context manager we just created with the keyword with:

with TemporaryFile():
# do other stuff

And that’s it! Our file will now get created when we enter the code block, and will get deleted when we leave, and if an exception is raised, it will still cause the program to crash.

Beautiful.

Using things context managers give you

But what if we wanted to use that temporary file that we created with our context managers? All we need to do is return the file from the context manager.

class TemporaryFile:
def __enter__(self):
file = create_temporary_file()
return file
def __exit__(self, *args):
delete_temporary_file()

Now, we can use the “as” keyword to grab the actual file itself:

with TemporaryFile() as file:
# do some stuff - but now we have access to the file!

While files are a common use for context managers, they can be used for anything where you want to clean up afterwards. Want to connect to a server, and make sure to close the connection at the end? Use a context manager. Want to install a piece of software, and then uninstall it when you leave the code block? Use a context manager.

Closing thoughts

Context managers are a rare concept in software: they make code more readable, more reliable, and more succinct. Very few concepts or tools can claim to do the same. No matter what happens inside the context manager (short of a SIGKILL ) you can be guaranteed that your cleanup code will get called, no matter how you leave that code block.

Don’t leave a mess for the next developer or your future self. Use context managers. They are awesome.

--

--

Jordan Edmunds, PhD

Software Engineer who writes about #software, #diversity, #health, and random musings. Reformed academic, Berkeley EECS PhD. Autistic self-advocate. He/Him.