Is there a way to use the `with`-statement in a closure in python?

I want to use a Context Manager Type inside of a closure such that it’s lifetime spans the lifetime of the enclosed function.

A (not working) version communicating my intent could be:

class CtxMgr:
    def __enter__(self):
        print('enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return False

def closure():
    with CtxMgr() as mgr:
        def enclosed():
            print('called')
        return enclosed


cl = closure()
print('generated')
cl()

But this obviously does not work since the context of the with-statement ends when enclosed is returned. This can be seen in the output:

enter
exit
generated
called

If the CtxMgr were kept alive while the enclosed function exists it should look like this:

enter
generated
called
exit

Is it possible to have the CtxMgr stay alive while the closure exists?

>Solution :

It is not possible to have the CtxMgr stay alive while the closure exists, because the CtxMgr instance is only alive within the scope of the with block.

When the with block ends, the CtxMgr instance is no longer needed, and it is garbage collected. This means that the CtxMgr instance is not available to the closure when it is called later.

If you want the CtxMgr instance to be available to the closure, you can pass it as an argument to the closure when you call it. I’ve modified the code below to fit your needs. Hope this helps!

class CtxMgr:
    def __enter__(self):
        print('enter')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return False

def closure():
    def enclosed(mgr):
        # Use the mgr instance here
        pass
    return enclosed

cl = closure()

# Create a CtxMgr instance
with CtxMgr() as mgr:
    # Pass the mgr instance to the closure
    cl(mgr)

Leave a Reply