Keeping context-manager object alive through function calls

Chris Angelico rosuav at gmail.com
Tue Nov 10 17:53:43 EST 2015


On Wed, Nov 11, 2015 at 9:36 AM, Pablo Lucena <plucena24 at gmail.com> wrote:
> And calling it as such:
>
> def do_more_stuff(device):
>     gen = do_stuff(device, return_handle=True)
>     data, conn = next(gen)
>     output = conn.send_command("show users")
>     #process output...
>     return processed_output

The first question needs to be: When *is* conn to be closed? What
you've done here is abandon all of the benefit of the context manager,
because it's no longer clear when you're done with the connection.
Here's an alternative: Turn your generator into another context
manager.

from contextlib import contextmanager

@contextmanager
def do_stuff(device):
    with manager(device) as conn:
        output = conn.send_command("show ip route")
        #process output...
        yield (processed_output, conn)

def do_stuff_now(device):
    with do_stuff(device) as (out, conn):
        return out

This forces you to separate the return_handle=True and
return_handle=False modes into two separate functions, but since one
is defined in terms of the other, you don't have a massive maintenance
burden. (Backward compatibility might have you call the first one
do_stuff_with_handle and the second one gets the name do_stuff. Or
whatever names make sense. Handwave.)

So now you can use do_stuff_now() the way you were using do_stuff(),
and if you want to keep using the handle afterward, you use it like
this:

def do_more_stuff(device):
    with do_stuff(device) as (data, conn):
        output = conn.send_command("show users")
        #process output...
        return processed_output

This guarantees that the device will be closed as you depart do_more_stuff.

There are other ways, but this is an easy way to build context
managers on top of context managers.

ChrisA



More information about the Python-list mailing list