Python:在 try/except/else 块中引发异常,处理顺序问题

Posted

技术标签:

【中文标题】Python:在 try/except/else 块中引发异常,处理顺序问题【英文标题】:Python: raising exception in try/except/else block, processing order problem 【发布时间】:2011-07-10 01:16:19 【问题描述】:

我是 Python 新手,已经开始编写我的第一个模块,该模块将执行备份到外部存储(通常是一个或多个 USB 磁盘)。

期望的行为是:

    检查目标(备份磁盘)是否已经挂载。 destination_was_mounted 变为 True 或 False 如果 destination_was_mounted = False 则挂载目标。 如果 mountDestination 失败,引发异常并重新启动循环。 如果 mountDestination 正常,请检查目标上是否存在 checkfile(和 backup_dir)。 如果 check_destination 失败引发异常并重新启动循环。 继续处理(尚未编码)。 在任何情况下,如果destination_was_mounted = False,则卸载目标。

问题是,如果 check_destination 部分引发异常,即使我在 finally 部分中有该目标,它也无法卸载目标。就好像destination_was_mounted 变成了True,即使它本来应该是False。或者好像 check_destination 在 mount_destination 之前运行,即使它在它之后。

我的参考资料(在查看 python 文档和我的学习 Python 书籍中):

Python: How to tell the for loop to continue from a function?

How to retry after exception in python?

How to get back to the for loop after exception handling

#!/usr/bin/env python3.1

import sys
import os
import os.path
import subprocess
import configparser
CONFIGFILE = 'backup.ini'
# do i need this?
config = 

config = configparser.ConfigParser()
config.read(CONFIGFILE)
backup_sources = sorted(config.sections()[1:])

class NoCheckFile(Exception):
    pass

def mountDestination(destination):
    return subprocess.check_call(['mount', destination])

def unMountDestination(destination):
    return subprocess.check_call(['umount', destination])

def checkDestination(destination, backup_dir, checkfile):
    return os.path.exists(destination + '/' + backup_dir + '/' + checkfile)

''' exception handlers '''
# perhaps add another arg like 0 or 1 for success/failure
def handleCalledProcessError(ex):
    print('Exception: ' + str(ex))

def handleNoCheckFile(ex):
    print('Exception: ' + str(ex))

# rename me once I work out logging
def logExecute(result):
    print('Info: ' + str(result))
# can I pass logging output here

def main():
    for section in backup_sources:
        item = dict(config.items(section))
        destination = item['destination']
        destination_was_mounted = os.path.ismount(destination)
        backup_dir = item['backup_dir']
        checkfile = item['checkfile']
        try:
            ''' check destination_was_mounted and mount destination if required '''
            mount_destination = None
            unmount_destination = None
            if not destination_was_mounted:
                mount_destination = mountDestination(destination)

            ''' check that checkfile exists in backup_dir '''
            check_destination = checkDestination(destination, backup_dir, checkfile)
            if not check_destination:
                raise NoCheckFile('no checkfile found')

            ''' lvm snapshot, mount and source path update '''

            ''' backup engine code here '''

            ''' check destination_was_mounted and um-mount destination if required '''
            if not destination_was_mounted:
                unmount_destination = unMountDestination(destination)
        except subprocess.CalledProcessError as ex:
            print(destination, 'mounted before loop start: ', destination_was_mounted)
            handleCalledProcessError(ex)
        except NoCheckFile as ex:
            handleNoCheckFile(ex)
        else:
            print(destination, 'mounted before loop start: ', destination_was_mounted)
            logExecute(mount_destination)
            logExecute(check_destination)
        finally:
            print('should always see me')
            logExecute(unmount_destination)
            # return to say True or False

    # this should be where email reports etc. go

if __name__ == '__main__':
    main()

backup.ini 文件的相关部分是:

[general]

[1]
DESTINATION = /mnt/backup2
BACKUP_DIR = BACKUP2
CHECKFILE = .checkfile

[2]
DESTINATION = /mnt/backup1
BACKUP_DIR = BACKUP1
CHECKFILE = .checkfile

输出如下所示 - 我在 [1] 和 [2] 中指定的挂载点附加了 2 个备份磁盘,并且故意没有为 [1] 创建检查文件以进行测试。

> umount /mnt/backup1
umount: /mnt/backup1: not mounted
> umount /mnt/backup2 
umount: /mnt/backup2: not mounted
> mugsyback.py 
Exception: no checkfile found
should always see me
Info: None
/mnt/backup1 mounted before loop start:  False
Info: 0
Info: True
should always see me
Info: 0
> mount
...
/dev/sdc1 on /mnt/backup2 type ext3 (rw)

【问题讨论】:

你能把你的问题浓缩一下吗?我完全迷路了...... “然后重启循环”...为什么?如果第一次没有工作,你为什么期望它第二次工作? @eryksun,感谢您的澄清,是的,这就是我的意思。 【参考方案1】:

您在 try 块中有卸载代码。最后,您只是在登录。

【讨论】:

谢谢,这是正确的,并且在将卸载代码移动到 finally: 后有效。我还在“除了 NoCheckFile as ex:”中添加了一个打印,这有助于我遵循该过程并将 logExecute 函数重命名为 logInfo。从我正在做的阅读中,我对 try: 部分的执行方式感到困惑,并认为:在出现 except - newbie 错误后仍然调用对象。

以上是关于Python:在 try/except/else 块中引发异常,处理顺序问题的主要内容,如果未能解决你的问题,请参考以下文章

python 中的 try/except/else/finally语句

python try/except/else/finally执行顺序

Python入门教程第54篇 异常处理之try…except…else

python中的try/except/else/finally语句

python中的try/except/else/finally语句

Python:在 try/except/else 块中引发异常,处理顺序问题