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