Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Using a Context Manager in Hello, World

00:00 In the previous two lessons, I demonstrated context managers from the standard library. In this lesson, I’ll show you how to write your own. There are two approaches for writing a context manager.

00:11 The first is to implement the .__enter__() and .__exit__() methods on a class, and the second is to use the context manager decorator and yield.

00:22 In this lesson, I’ll get you started with two variations on Hello, World for context managers—using the class based approach, and then I’ll show you the function style in the next lesson.

00:35 Here is my poor traveler’s French attempt at Hello, World in context manager form. First off, you need a class, and that class has to implement the two context manager protocol methods, .__enter__() and .__exit__().

00:50 .__enter__() is called when the context block starts. For my Hello, World example, it prints out some debug info then returns an object.

01:00 Whatever is returned by .__enter__() is what gets populated by the as keyword. In this case, it will be a string. .__exit__() is a bit more complicated.

01:11 Remember that context managers will handle exceptions that could occur within the code block. The arguments to .__exit__() provide information about any exceptions that happen. If there’s no exception, these values will be None.

01:25 exc_type is the class of the exception, exc_value is the instance of the exception, and exc_tb is a reference to the traceback object.

01:37 If an exception occurs in BonjourLeMonde, I print out some extra info. Then the last thing .__exit__() does is print some debug, reporting that the block is being exited. Let’s go run this.

01:55 Importing … starting a block … printing something …

02:07 Man, that sucks. Did you catch why that happened? This is an easy mistake to make. Context managers are instances of an object. In the with statement, I made a reference to the class when I should have instantiated it.

02:23 The error message is kind of helpful, but that’s a Python 3.11 thing. In earlier versions, you’ll get the more cryptic AttributeError: __enter__ and nothing else.

02:32 That’s because the with statement is attempting to call a .__enter__() method on the class, and there isn’t one on the class. There’s only one on the object.

02:40 So when it finds it missing, you get an AttributeError. Yay for improved error messages in Python 3.11! Anyhow, let’s try this again the right way.

02:53 That’s what you’re supposed to do.

02:59 Same block content … and there’s the result. There’s some debug from entering, the print() from the block, and the debug from exiting. I’m going to do this again, this time using the as part of the with statement.

03:26 Similar contents, but adding the target attribute to the print(). And there you go. The string returned by BonjourLeMonde’s .__enter__() is available in the message variable. Okay, one more time.

03:45 Déjà vu. And this time, I’ll raise an exception.

03:55 And here’s the result. You get the enter context debug like before, then the first line of the block like before, and then the exception debug info from inside of .__exit__().

04:09 Then you get the debug for leaving the context, and because this context didn’t handle the exception, the ValueError is still raised upon leaving.

04:19 What if you don’t still want to raise the exception? Well, there’s a way of handling that. Let’s try this again with a small change.

04:30 My Spanish is even worse than my French although, having recently rewatched Narcos, I’ve got the swear words down pat. The only real difference between Bonjour and Hola is this line. Returning True indicates that the exception was handled, meaning it won’t be raised after the block.

04:49 Let’s go see the difference in practice.

05:06 This time, you get the debug from the if statement in .__exit__(), but because it returned True, you don’t get the exception afterwards.

05:14 This gives you complete control over how exceptions are handled in your custom context managers. Now that you’ve got the idea how these work, let’s do some practice. Next up, I’ll show you two real-world examples of custom context managers, one written using the function style.

Become a Member to join the conversation.