Context manager is an object that defines the behavior of entering and exiting a with block, providing automatic resource management (files, connections, etc.). It is implemented using the __enter__ and __exit__ methods in a class, or through the @contextmanager decorator from the contextlib module.
class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __enter__(self): self.file = open(self.filename, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() with FileManager('test.txt', 'w') as f: f.write('Hello')
Nuances: It is important to properly handle exceptions in __exit__, returning True to suppress errors, and to be cautious with resources to avoid leaving them open in case of errors.
Question: If an exception occurs in the with block, does Python call the __exit__ method? How are error parameters passed?
Answer: Yes, the __exit__ method is always called, even when an exception occurs. It receives the exception type, its value, and the traceback. If __exit__ returns True, the exception is suppressed.
class Simple: def __enter__(self): print('Enter') def __exit__(self, exc_type, exc_val, exc_tb): print('Exit') print(exc_type, exc_val) return True # the error does not "escape" outward with Simple(): raise ValueError('boom!')
Story
__exit__ in a custom file descriptor manager — in case of an exception the file was not closed, leading to file descriptor leaks and failures when handling a large number of files.Story
True in __exit__ for all exceptions. This "suppressed" errors and led to unnoticed failures and data integrity issues, as the logic deemed the transaction successful.Story
Utilized the @contextmanager decorator from the contextlib module, but forgot to handle exceptions inside the yield, causing socket connections to remain open when the code failed and the server "hung" with open ports.