I do not understand how and when a context manager in a non-finished generator is closed. Consider the following context manager and function:
from contextlib import contextmanager
@contextmanager
def ctx():
print('enter ctx')
yield
print('exit ctx')
def gen_nums(n):
with ctx():
yield from range(n)
My first intuition was that if I call gen_nums but do not consume the generator fully, ctx will never be closed, which was rather concerning. For example:
for i, j in zip(range(5), gen_nums(10)):
print(f'{i}, {j}')
Here exit ctx is not printed at the end. As I saw it, that meant that if I had a file context in the generator it would be left open; however I then realized that doing the same with files would actually close the file properly. After some tests, I found out that if I did:
from contextlib import contextmanager
@contextmanager
def ctx():
print('enter ctx')
try:
yield
finally:
print('exit ctx')
Now exit ctx was printed at the end. So I suppose some exception would be triggered at some point, but I don't know which, where or when (I tried to print the exception with except BaseException as e but it did not work). It seems it happens when the generator is deleted, because if I do:
g = gen_nums(10)
for i, j in zip(range(5), g):
print(f'{i}, {j}')
del g
Then exit ctx only happens after del g. However, I would like to have a better understanding of what is happening here and who is triggering what.