ProgrammingPython Backend Developer

How does a context manager work in Python, why is it needed, and how to implement your own using the __enter__ and __exit__ protocols? What nuances are important to consider?

Pass interviews with Hintsage AI assistant

Answer.

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.

Example implementation:

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.

Trick question.

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!')

Examples of real errors due to misunderstanding the nuances of the topic.


Story

Forgot to implement __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

Used a third-party context manager to manage a database, which returned 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.