Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I retrieve the return value of __exit__ from a python context manager?

I use context manager in python. In want to get some logs back from my __exit__ method. So my code logs something like this:

class MyContextManager:
    def __init__(self, value1, value2)
        self.value1 = value1
        self.value2 = value2

    def __enter__(self)
        # Do some other stuff
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Do some tear down action, process some data that is 
        # created in __enter__ and log those results
        return my_results

with MyContextManager(value1=my_value1, value2=my_value2) as manager:
     # Do some stuff

So how could I access my_results which is returned from __exit__ after (or at the end) of my with block. Is this even legit to return something other the True in the __exit__ method?

like image 370
Lennart Weitzenberger Avatar asked Mar 09 '23 03:03

Lennart Weitzenberger


1 Answers

Is this even legit to return something other the True in the __exit__ method?

No, not really, but Python will just test for the truth value, so you can get away with it. In other words, if you return a truthy object here, any exceptions will be suppressed. If there was no exception, returning a truthy value is just a no-op.

how could I access my_results which is returned from __exit__ after (or at the end) of my with block.

You can't. The with expression machinery consumed it.

You should make it available in some other way; set it as an attribute on the context manager object itself:

class MyContextManager:
    def __init__(self, value1, value2)
        self.value1 = value1
        self.value2 = value2

    def __enter__(self)
        # Do some other stuff
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Do some tear down action, process some data that is 
        # created in __enter__ and log those results
        self.my_results = my_results
        # returning None, we don't want to suppress exceptions
        return None

with MyContextManager(value1=my_value1, value2=my_value2) as manager:
     # Do some stuff

results = manager.my_results

The manager name is available after the with block has completed.

This is how the unittest.TestCase.assertRaises() context manager shares the captured exception, for example.

like image 128
Martijn Pieters Avatar answered Mar 10 '23 15:03

Martijn Pieters