Printing exceptions isn’t the only way we can handle them in the __exit__
method. An exception that occurs in a context manager can be handled in two ways:
If we want to throw an error when an error occurs, we can either:
- Return
False
after the.close()
method - Do nothing
- Return
If we want to suppress the error, we can:
- Return
True
after the.close()
method
- Return
Using a script similar to our earlier example, we can examine how this works:
class OpenFile: def __init__(self, file, mode): self.file = file self.mode = mode def __enter__(self): self.opened_file = open(self.file, self.mode) return self.opened_file def __exit__(self, exc_type, exc_val, traceback): print(exc_type, exc_val, traceback) print("The exception has been handled") self.file.close() return True
Notice above that nothing changed except for the adding of return True
to implement the suppression of an error. To see this in action, we’ll call two with
statements using this context manager; One that will throw an exception and another that will not. Let’s observe the behavior:
with OpenFile("file.txt", "r") as file: # .see is not a real method print(file.see()) with OpenFile("file.txt", "r") as file: print(file.read())
When we run this code, our output is as follows:
<class 'AttributeError'> '_io.TextIOWrapper' object has no attribute 'see' <traceback object at 0x7fedf822d180> The exception has been handled None None None
Here we see that:
- The error message we manually coded is printed but there is no automatic error message thrown by the program.
- Both
with
statements ran.
If we did not return True
, the second (and all proceeding) with
statements would not have run since an exception would be hit.
Additionally, we can choose to handle a specific exception, while also suppressing it! This is useful if we want our context manager to not block the execution of other code, but also customize the output if a certain exception occurs. Here is an example of working with a TypeError
:
class OpenFile: def __init__(self, file, mode): self.file = file self.mode = mode def __enter__(self): self.opened_file = open(self.file, self.mode) return self.opened_file def __exit__(self, exc_type, exc_val, traceback): if isinstance(exc_val, TypeError): # Handle TypeError here... print("The exception has been handled") return True self.file.close()
Notice the if
statement that compares exc_val
to a specific exception we are trying to catch. Anything we want to happen for this specific exception can occur in the conditional code block. Lastly, we return True
to make sure we suppress the exception from arising and stopping the rest of our code from running.
Let’s return to our poem context manager from earlier and implement some exception handling!
Instructions
We are back with our PoemFiles
context manager!
There are currently two with
calls. Run the code to see what exception occurs. In the next step, we will try to handle it!
Looks like our AttributeError
is back in our first with
call.
Inside of the __exit__
method, write a conditional using the isinstance()
function to check if the exception is an AttributeError
. If it is, close the file and return True
!