ProgrammingPython backend developer

Tell us about working with context managers and resource managers using the @contextmanager decorator from the contextlib module: why it is needed, how it works, and what pitfalls there are?

Pass interviews with Hintsage AI assistant

Answer.

The history is that in Python, resource management (files, connections, transactions) uses the with statement based on the context manager protocol (enter, exit). For simple cases, writing a whole class is excessive, so the @contextmanager decorator (from the contextlib module) was proposed, allowing resource managers to be defined as generators.

Problem — manually releasing or closing resources is inconvenient, and the code becomes error-prone (for example, forgetting to close a file). Also, you don't want to write a separate class with two methods just for simple tasks (like temporarily changing the directory or stdout).

Solution — use @contextmanager to succinctly describe the "beginning" and "end" of using a resource, ensuring exceptions and releases are handled properly.

Example code:

from contextlib import contextmanager @contextmanager def open_file(filename, mode): f = open(filename, mode) try: yield f finally: f.close() with open_file('test.txt', 'w') as f: f.write('Hello')

Key features:

  • The start of the block — everything before yield, which returns the resource.
  • The end of the block — after yield; must include error handling and closure/release.
  • Guarantees the resource is closed even when an exception occurs.

Trick questions.

Can you make it so that yield from @contextmanager returns multiple objects (for example, through a tuple)?

Yes, you can, and it is even convenient for returning a "group" of related resources.

Example code:

@contextmanager def managed_two(): a, b = [], {} try: yield a, b finally: a.clear(); b.clear()

What happens if an exception is raised after yield — will the resource close?

Yes, the finally block will execute in any case, even if an error/exception occurs in the code inside the with statement.

Can @contextmanager replace a full-fledged context manager class with enter/exit?

In most trivial cases — yes, for more complex cases with nested states or inheritance, it’s more convenient to work through a class.

Common mistakes and anti-patterns

  • Skipping the finally block, which leads to resource leaks in case of errors.
  • Yield not being unique; if there are two yields in a function — this will lead to a RuntimeError.
  • Attempts to use @contextmanager with non-generator functions (no yield).

Real-life example

Negative case

Manually opening and closing a file:

f = open('test.txt', 'w') try: f.write('Hello') finally: f.close()

Pros:

  • Full control over resource lifecycle management.

Cons:

  • Lots of boilerplate code, higher chance of making mistakes by forgetting finally.

Positive case

Using @contextmanager for temporarily changing the working directory or opening a file (or environment settings):

@contextmanager def work_in(dirname): import os prev = os.getcwd() os.chdir(dirname) try: yield finally: os.chdir(prev)

Pros:

  • Conciseness, guarantee to return to the original state.

Cons:

  • High level of abstraction may hide the details of resource management.