How do I write a class that can be used in place of contextmanager
in this example?
from contextlib import contextmanager
@contextmanager
def f():
try:
yield None
except Exception as e:
print(e) # This statement is executed!
with f() as _:
raise Exception("foo")
My attempt:
class MyOwnContextManager:
def __init__(self, f):
self.f = f
def __enter__(self):
return self.g.__next__()
def __exit__(self, type, value, traceback):
pass # What exactly do I put here?
def __call__(self, *args, **kwds):
self.g = self.f(*args, **kwds)
return self
@MyOwnContextManager
def f():
try:
yield None
except Exception as e:
print(e) # This statement is NOT executed!
with f() as _:
raise Exception("foo")
MyOwnContextManager
doesn't let f
handle the raised Exception
, unlike the built-in contextmanager
, which calls print(e)
. How do I handle the exception that is raised inside of the context with the function that is decorated by MyOwnContextManager
?
The magic happens with self.g.throw
where you pass it back to your own block.
class MyOwnContextManager:
def __init__(self, f):
self.f = f
def __enter__(self):
return self.g.__next__()
def __exit__(self, type, value, traceback):
try:
self.g.throw(type, value, traceback) # go back to your function
raise RuntimeError("generator didn't stop after throw()")
except StopIteration as exc:
print("stopping itter returning", exc is not value, value)
return exc is not value
def __call__(self, *args, **kwds):
self.g = self.f(*args, **kwds)
return self
@MyOwnContextManager
def f():
print("init")
try:
print("yielding")
yield None
print("yiedled") # will not happen
except Exception as e:
print("handler", e)
init
yielding
go
handler foo
stopping itter returning True <class 'Exception'> foo