Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python引发异常时回滚操作的最佳方法_Python_Exception Handling_Rollback_Undo_Ctrl - Fatal编程技术网

Python引发异常时回滚操作的最佳方法

Python引发异常时回滚操作的最佳方法,python,exception-handling,rollback,undo,ctrl,Python,Exception Handling,Rollback,Undo,Ctrl,我有一个如下代码: # step 1 remove from switch for server in server_list: remove_server_from_switch(server) logger.info("OK : Removed %s", server) # step 2 remove port for port in port_list: remove_ports_from_switch(port) logger.info("OK

我有一个如下代码:

# step 1 remove from switch    
for server in server_list:
    remove_server_from_switch(server)
    logger.info("OK : Removed %s", server)

# step 2 remove port
for port in port_list:
    remove_ports_from_switch(port)
    logger.info("OK : Removed port %s", port)

# step 3 execute the other operations
for descr in pairs:
    move_descr(descr)

# step 4 add server back to switch    
for server in server_list:
    add_server_to_switch(server)
    logger.info("OK : server added %s", server)

# step 5 add back port 
for port in port_list:
    add_ports_to_switch(port)
    logger.info("OK : Added port %s", port)
for循环中的函数可以引发异常,或者用户可以使用Ctrl+C中断脚本。 但是,如果在执行过程中引发异常,我希望通过撤消之前已经做过的更改来进入回滚模式。 我的意思是,如果在步骤3中引发异常,我必须回滚步骤1和2(通过执行步骤4和5中的操作)。 或者,如果用户试图在步骤1中以Ctrl +C停止脚本,那么我想回滚动作并将服务器删除。
请问,如何通过使用异常以良好的pythonic方式完成此操作?:)

也许像这样的方法会奏效:

undo_dict = {remove_server_from_switch: add_server_to_switch,
             remove_ports_from_switch: add_ports_to_switch,
             add_server_to_switch: remove_server_from_switch,
             add_ports_to_switch: remove_ports_from_switch}

def undo_action(action):
    args = action[1:]
    func = action[0]
    undo_dict[func](*args)

try:

    #keep track of all successfully executed actions
    action_list = []

    # step 1 remove from switch    
    for server in server_list:
        remove_server_from_switch(server)
        logger.info("OK : Removed %s", server)
        action_list.append((remove_server_from_switch, server))

    # step 2 remove port
    for port in port_list:
        remove_ports_from_switch(port)
        logger.info("OK : Removed port %s", port)
        action_list.append((remove_ports_from_switch, port))

    # step 3 execute the other operations
    for descr in pairs:
        move_descr(descr)

    # step 4 add server back to switch    
    for server in server_list:
        add_server_to_switch(server)
        logger.info("OK : server added %s", server)
        action_list.append((add_server_to_switch, server))

    # step 5 add back port 
    for port in port_list:
        add_ports_to_switch(port)
        logger.info("OK : Added port %s", port)
        action_list.append((add_ports_to_switch, port))

except Exception:
    for action in reverse(action_list):
        undo_action(action)
        logger.info("ERROR Recovery : undoing {func}({args})",func = action[0], args = action[1:])

finally:
    del action_list
EDIT:,在这种情况下,最好将整个内容包装到上下文管理器中,并使用
with
语句。然后,无论是否遇到错误,您的所有操作都将在
with
块的末尾撤消

下面是它可能的样子:

class ActionManager():
    def __init__(self, undo_dict):
        self.action_list = []
        self.undo_dict = undo_dict
    def action_pop(self):
        yield self.action_list.pop()
    def action_add(self, *args):
        self.action_list.append(args)
    def undo_action(self, action):
        args = action[1:]
        func = action[0]
        self.undo_dict[func](*args)
    def __enter__(self):
        return self
    def __exit__(self, type, value, traceback):
        for action in self.action_stack:
            undo_action(action)
            logger.info("Action Manager Cleanup : undoing {func}({args})",func = action[0], args = action[1:])
现在您可以这样做:

#same undo_dict as before
with ActionManager(undo_dict) as am:

    # step 1 remove from switch    
    for server in server_list:
        remove_server_from_switch(server)
        logger.info("OK : Removed %s", server)
        am.action_add(remove_server_from_switch, server)

    # step 2 remove port
    for port in port_list:
        remove_ports_from_switch(port)
        logger.info("OK : Removed port %s", port)
        am.action_add(remove_ports_from_switch, port)

    # step 3 execute the other operations
    for descr in pairs:
        move_descr(descr)

    # steps 4 and 5 occur automatically    
另一种方法是在
\uuuuu enter\uuuu
方法中添加服务器/端口,这可能更好。您可以将上面的
ActionManager
子类化,并在其中添加端口添加和删除逻辑

\uuuu enter\uuuu
方法甚至不必返回
ActionManager
类的实例-如果这样做有意义,您甚至可以编写它,以便
使用SwitchManager(服务器、端口)
返回您的pairs对象,您可以这样做:

with SwitchManager(servers, ports) as pairs:
    for descr in pairs:
        move_descr(descr)

这就是上下文管理器的用途。有关详细信息,请阅读,但总体思路是您需要编写上下文管理器类,其中
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuuuuuuuuu。然后,您的代码结构将类似于:

with RemoveServers(server_list):
    with RemovePorts(port_list):
        do_stuff
# exiting the with blocks will undo the actions