Search code examples
pythoncontextmanager

Is it safe to define a context manager around a nested function?


For a third party library* I have to provide a function which consumes some data. My implementation of the consumption requires the data to be posted to an API. So I came up with this structure below:

def consumer_provider():

    with HttpClient() as http_client:
        
        def consumer(data):
            http_client.post(data)

        return consumer

So I can present the function to the third party lib like so:

third_party_lib.add(consumer=consumer_provider())

In my tests it works quite well, but is this legit? When do I have to expect that the context manager releases the resource (in this case the connection pool)?

* loguru in this case, but it should not really matter for the question


Solution

  • It depends on the context manager. In the code you wrote, the HTTPClient you created stays alive because the function it returns maintains a reference to it, even though the variable http_client defined in consumer_provider goes out of scope.

    However, HTTPClient.__exit__ is still called before consumer_provider returns, so the consumer function may not work as intended.

    You may want to do something like

    def consumer_provider():
        http_client = HttpClient()
        def consumer(data):
            with http_client:
                http_client.post(data)
        return consumer
    

    which ensures that the HttpClient object stays "hidden" inside the closure, but its __enter__ and __exit__ methods aren't called until the function gets called. (Whether the client can be used by multiple function calls also depends on the definition of HttpClient.)