Python 通过函数调用使上下文管理器对象保持活动状态

Python 通过函数调用使上下文管理器对象保持活动状态,python,ssh,network-programming,paramiko,contextmanager,Python,Ssh,Network Programming,Paramiko,Contextmanager,我在通过函数调用保持上下文管理器打开方面遇到了一些问题。我的意思是: 在一个模块中定义了一个上下文管理器,我使用它打开到网络设备的SSH连接。“设置”代码处理打开SSH会话和处理任何问题,而拆卸代码处理优雅地关闭SSH会话。我通常使用它如下: from manager import manager def do_stuff(device): with manager(device) as conn: output = conn.send_command("show ip

我在通过函数调用保持上下文管理器打开方面遇到了一些问题。我的意思是:

在一个模块中定义了一个上下文管理器,我使用它打开到网络设备的SSH连接。“设置”代码处理打开SSH会话和处理任何问题,而拆卸代码处理优雅地关闭SSH会话。我通常使用它如下:

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 
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
为了保持SSH会话处于打开状态,并且不必跨函数调用重新建立它,我想在“do_stuff”中添加一个参数,该参数可以选择返回SSH会话以及从SSH会话返回的数据,如下所示:

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 
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
我希望能够从另一个函数调用这个函数“dou-stuff”,如下所示,这样它就可以向“dou-stuff”发出信号,表示SSH句柄应该与输出一起返回

def do_more_stuff(device):
    data, conn = do_stuff(device, return_handle=True)
    output = conn.send_command("show users")
    #process output...
    return processed_output
然而,我遇到的问题是,由于do_stuff函数“返回”并触发上下文管理器中的拆卸代码,SSH会话被关闭(这将优雅地关闭SSH会话)

我已尝试将“do_stuff”转换为生成器,使其状态处于挂起状态,并可能导致上下文管理器保持打开状态:

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
并称之为:

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
然而,在我的例子中,这种方法似乎不起作用,因为上下文管理器关闭了,而我得到了一个关闭的套接字

有没有更好的方法来解决这个问题?也许我的生成器需要做更多的工作……我认为使用生成器保持状态是我想到的最“明显”的方式,但总的来说,我是否应该寻找另一种在函数调用中保持会话打开的方式


谢谢

我发现了这个问题,因为我正在寻找一个类似问题的解决方案,其中我想要保持活动状态的对象是一个包含selenium.webdriver.Firefox实例的pyvirtualdisplay.display.display实例

我还希望,如果在显示/浏览器实例创建过程中引发异常,那么任何打开的资源都会消失

我想同样的方法也可以应用于数据库连接

我认识到这可能只是一个部分解决方案,包含的最佳实践不多。谢谢你的帮助

此答案是临时使用以下资源修补我的解决方案的结果:

(我还没有完全理解这里所描述的内容,尽管我很欣赏其中的潜力。通过提供类似的情况,上面的第二个链接最终证明是最有帮助的。)

我在以下方面取得了半预期的结果:

    kwargs = {
        'rfbport': 5904,
    }
    _desktop_manager = XvncDesktopManager(check_desktop_display_ok=check_desktop_display_ok, **kwargs)
    with ExitStack() as stack:
        # context entered and what is inside the __enter__ method is executed
        # desktop_manager will have an attribute "close_all" that can be called explicitly to unwind the callback stack
        desktop_manager = stack.enter_context(_desktop_manager)


    # I was able to manipulate the browsers inside of the display
    # and outside of the context 
    # before calling desktop_manager.close_all()
    browser, = desktop_manager.browser_resources
    browser.get(url)
    # close everything down when finished with resource
    desktop_manager.close_all()  # does nothing, not in callback stack
    # this functioned as expected
    desktop_manager.release_desktop_display(desktop_manager)

我发现这个问题是因为我正在寻找一个类似问题的解决方案,其中我想要保持活动状态的对象是一个pyvirtualdisplay.display.display实例,其中包含selenium.webdriver.Firefox实例

我还希望,如果在显示/浏览器实例创建过程中引发异常,那么任何打开的资源都会消失

我想同样的方法也可以应用于数据库连接

我认识到这可能只是一个部分解决方案,包含的最佳实践不多。谢谢你的帮助

此答案是临时使用以下资源修补我的解决方案的结果:

(我还没有完全理解这里所描述的内容,尽管我很欣赏其中的潜力。通过提供类似的情况,上面的第二个链接最终证明是最有帮助的。)

我在以下方面取得了半预期的结果:

    kwargs = {
        'rfbport': 5904,
    }
    _desktop_manager = XvncDesktopManager(check_desktop_display_ok=check_desktop_display_ok, **kwargs)
    with ExitStack() as stack:
        # context entered and what is inside the __enter__ method is executed
        # desktop_manager will have an attribute "close_all" that can be called explicitly to unwind the callback stack
        desktop_manager = stack.enter_context(_desktop_manager)


    # I was able to manipulate the browsers inside of the display
    # and outside of the context 
    # before calling desktop_manager.close_all()
    browser, = desktop_manager.browser_resources
    browser.get(url)
    # close everything down when finished with resource
    desktop_manager.close_all()  # does nothing, not in callback stack
    # this functioned as expected
    desktop_manager.release_desktop_display(desktop_manager)