Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #98615 > unrolled thread

Keeping context-manager object alive through function calls

Started byPablo Lucena <plucena24@gmail.com>
First post2015-11-10 17:36 -0500
Last post2015-11-11 18:18 +1100
Articles 2 — 2 participants

Back to article view | Back to comp.lang.python


Contents

  Keeping context-manager object alive through function calls Pablo Lucena <plucena24@gmail.com> - 2015-11-10 17:36 -0500
    Re: Keeping context-manager object alive through function calls Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-11-11 18:18 +1100

#98615 — Keeping context-manager object alive through function calls

FromPablo Lucena <plucena24@gmail.com>
Date2015-11-10 17:36 -0500
SubjectKeeping context-manager object alive through function calls
Message-ID<mailman.221.1447194972.16136.python-list@python.org>
I am running into a bit of an issue with keeping a context manager open
through function calls. Here is what I mean:

There is a context-manager defined in a module which I use to open SSH
connections to network devices. The "setup" code handles opening the SSH
sessions and handling any issues, and the teardown code deals with
gracefully closing the SSH session. I normally use it as follows:

from manager import manager
def do_stuff(device):
    with manager(device) as conn:
        output = conn.send_command("show ip route")
        #process output...
    return processed_output

In order to keep the SSH session open and not have to re-establish it
across function calls, I would like to do add an argument to "do_stuff"
which can optionally return the SSH session along with the data returned
from the SSH session, as follows:

def do_stuff(device, return_handle=False):
    with manager(device) as conn:
        output = conn.send_command("show ip route")
        #process output...
        if return_handle:
            return (processed_output, conn)
        else:
            return processed_output


I would like to be able to call this function "do_stuff" from another
function, as follows, such that it signals to "do_stuff" that the SSH
handle should be returned along with the output.

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

However the issue that I am running into is that the SSH session is closed,
due to the do_stuff function "returning" and triggering the teardown code
in the context-manager (which gracefully closes the SSH session).

I have tried converting "do_stuff" into a generator, such that its state is
suspended and perhaps causing the context-manager to stay open:

def do_stuff(device, return_handle=False):
    with manager(device) as conn:
        output = conn.send_command("show ip route")
        #process output...
        if return_handle:
            yield (processed_output, conn)
        else:
            yield processed_output

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

However this approach does not seem to be working in my case, as the
context-manager gets closed, and I get back a closed socket.

Is there a better way to approach this problem? Maybe my generator needs
some more work...I think using a generator to hold state is the most
"obvious" way that comes to mind, but overall should I be looking into
another way of keeping the session open across function calls?

Thanks


-- 
*Pablo Lucena*

[toc] | [next] | [standalone]


#98631

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-11-11 18:18 +1100
Message-ID<5642ebe7$0$1516$c3e8da3$5496439d@news.astraweb.com>
In reply to#98615
On Wednesday 11 November 2015 09:36, Pablo Lucena wrote:

> I am running into a bit of an issue with keeping a context manager open
> through function calls. Here is what I mean:
[...]
> In order to keep the SSH session open and not have to re-establish it
> across function calls, I would like to do add an argument to "do_stuff"
> which can optionally return the SSH session along with the data returned
> from the SSH session, as follows:

That won't work. The whole point of the "with" statement is to automatically 
close the context manager when you leave the with statement. If you don't 
want it automatically closed, *don't use with*.

There's nothing wrong with manually opening and closing the connection, if 
that's what you need. Open the ssh connection, pass it to your various 
functions, and close it only when you are done with it.

Or, and this might be a better solution, move the with block up a level. 
Instead of putting it in "do_stuff" and the assorted other functions, open 
the connection *before* you call these functions:


def main():
    setup()
    with manager(device) as conn:
        do_stuff(conn)
        do_more_stuff(conn)
        do_even_more_stuff(conn)
    # Now we're finally done.
    sys.exit(0)



[...]
> I have tried converting "do_stuff" into a generator, such that its state
> is suspended and perhaps causing the context-manager to stay open:
[...]
> However this approach does not seem to be working in my case, as the
> context-manager gets closed, and I get back a closed socket.
> 
> Is there a better way to approach this problem? Maybe my generator needs
> some more work...I think using a generator to hold state is the most
> "obvious" way that comes to mind, but overall should I be looking into
> another way of keeping the session open across function calls?

You might be able to get some awful hack using generators working, but it 
will be ugly, confusing and fragile. This is a sign you are abusing the 
context manager and using it in a way that goes against the design of the 
feature. Consider some future maintainer (perhaps even yourself, in six 
months or a year), reading the code, and completely puzzled why the SSH 
connection isn't closed when the with block is exited.



-- 
Steve

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web