My goal is that instead of writing
for item in items:
with MyCD():
... # processing logic
I would like to have a single line
for item in MyCD.yield_each_item_wrapped(items):
... # processing logic
However, despite my ContextDecorator suppressing errors by returning True in __exit__ on error, the yielding the item after an item that raised error it immediately gets a GeneratorError.
So it seems that despite having handled the exception raised, the generator's .close() is called.
I'm running python 3.11.4
I would have expected to continue processing the remaining two items, as I deliberately suppressed the raised exception, i.e.: as the below snippet does
def yield2(items):
for i, item in enumerate(items):
with MyCD():
if item:
raise Exception("my error")
yield i # yield success
if __name__ == "__main__":
raising_flags = [True, False, False, True]
expected = [1, 2]
actual = [i for i in yield2(raising_flags)]
assert expected == actual, (expected, actual)
import traceback
from contextlib import ContextDecorator
class MyCD(ContextDecorator):
@classmethod
def yield_each_item_wrapped(cls, items, **kwargs):
for i, item in enumerate(items):
print(f"yield_each_item_wrapped {i=}")
print(f"yield_each_item_wrapped {item=}")
with cls(**kwargs):
yield item
def __enter__(self):
print("__enter__")
def __exit__(self, exc_type, exc, exc_tb):
print("__exit__")
if exc:
traceback.print_exc()
return True # do not propagate
if __name__ == "__main__":
raising_flags = [True, False, False, True]
expected = [1, 2]
actual = []
for i, raising in enumerate(MyCD.yield_each_item_wrapped(raising_flags)):
if raising:
raise Exception("my error")
actual.append(i)
assert expected == actual, (expected, actual)
produces
yield_each_item_wrapped i=0
yield_each_item_wrapped item=True
__enter__
__exit__
Traceback (most recent call last):
File "x.py", line 12, in yield_each_item_wrapped
yield item
GeneratorExit
yield_each_item_wrapped i=1
yield_each_item_wrapped item=False
__enter__
Exception ignored in: <generator object MyCD.yield_each_item_wrapped at 0x7f42e625aac0>
Traceback (most recent call last):
File "x.py", line 30, in <module>
raise Exception("my error")
RuntimeError: generator ignored GeneratorExit
Traceback (most recent call last):
File "x.py", line 30, in <module>
raise Exception("my error")
Exception: my error