user209974
user209974

Reputation: 1927

Why is the context manager closed after it is yielded?

I am trying to yield a context manager that is created in a with statement. However, something that I don't understand happens: the context manager is closed before it is yielded, even though the execution in the generator did not exit the scope of the with. For example:

class CM:
  def __enter__(self):
    print('enter cm')
    return self
  def __exit__(self, exc_type, exc_val, exc_tb):
    print('exit cm')


def make_foo():
  with CM() as cm:
    print('before yield')
    yield cm
  print('after yield')


print('before make_foo')
foo = next(make_foo())
print('after make_foo')

outputs

before make_foo
enter cm
before yield
exit cm
after make_foo

I saw this thread on a related topic, and the answer is about time when the object is garbage collected -- however why would cm be garbage collected before it is returned to be used by the caller here?

EDIT

When writing instead

foo_maker = make_foo()
foo = next(foo_maker)

then the CM is not closed -- so it seems that the CM is indeed GC because the generator is GC. But shouldn't it left alone since it is returned and potentially used after?

Upvotes: 1

Views: 625

Answers (1)

Pouria Nikvand
Pouria Nikvand

Reputation: 370

I think this is because of your method definition and how you call it.

if you instead of foo = next(make_foo()) use this :

for i in make_foo():
  print('thanks I saw cm')

the result would be this :

enter cm
before yield
thanks I saw cm
exit cm
after yield
after make_foo

Upvotes: 1

Related Questions