【问题标题】:How to safely determine the cause of an exception that is caught?如何安全地确定捕获的异常的原因?
【发布时间】:2017-04-18 15:05:38
【问题描述】:

在 Python 中,有许多 built-in exceptions 可以被各种标准库函数(当然还有其他代码)抛出。某个异常可能因多种原因而引发,您可能想了解它是否因特定原因而引发。

例如,在 Windows 中,如果您在文件被另一个进程锁定时尝试移动文件,您很可能会收到 PermissionError

PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Path\\to\\the\\file'

就我而言,我想确定引发PermissionError 异常的原因是否是因为我尝试移动的文件被锁定,我目前通过查看我捕获的异常中的错误消息来做到这一点:

try:
    # Move file
    os.rename(source_path, dest_path)

except PermissionError as e:
    if str(e).find('The process cannot access the file because it is being used by another process') != -1:
        # File not unlocked yet; do something, e.g. wait a moment and try again
    else:
        # Exception thrown for some other reason; do something else

但是,检查str(e) 是否包含作为子字符串的特定错误消息并不完全安全,因为我没有看到任何specification 将分配给os.rename 引发的异常的消息当源文件被锁定时,或者应该抛出什么类型的异常,或者即使应该抛出异常。因此,这种行为可能会在未来的 Python 版本中发生变化,或者在不同的 Python 实现之间有所不同。

那么,如果我们可以假设会引发PermissionError 异常,我如何安全地确定是否由于我试图访问锁定的文件而引发了PermissionError 异常?或者,如果我们不能假设,我怎样才能安全地实现我的应用程序当前实现的相同目标?

【问题讨论】:

  • “安全”是什么意思?
  • @martineau 在这种情况下,我所说的“安全”是指如果我更改为另一个版本的 Python 或另一个 Python 解释器,它不会中断。我并不是说我目前的做法会中断,我只是不确定它不会。
  • 在这种情况下,“最安全”的做法是捕获Exception as e: 并打印一条错误消息,说明无法访问该文件以及str(e)。未来无法保证。下一个最安全(也是最实用)的事情是让它相当具体,并假设未来将是兼容的,至少在可预见的未来是这样。后者通常是这种情况,因为语言创建者/维护者不喜欢破坏大量现有代码。
  • 除了str(e),你可以看看e的实际胆量。例如,vars(e) 可能会显示其他内容,而不仅仅是人类可读的消息。可能在其中有用。例如,我得到的一个 sqlalchemy 异常有一个嵌入的原始异常,我可以比解析 str(e) 更容易地测试它
  • @JLPeyret vars(e) 为我返回一个空字典(e.__dict__ 也是一个空字典,obviously)。有趣的;我认为对象中的__dict__ 属性列出了对象中的所有其他属性,但显然属性可以是不可写的和those are not listed。此外,e 中的属性似乎实现为properties,这是我以前没有听说过的。这可能很有用。

标签: python python-3.x exception-handling


【解决方案1】:

标准python异常使用Cerrno代码,可以用e.errno访问。

在mac/linux上可以看到posix errno values的列表。

在 Windows 上,您可以使用 e.winerror 访问由窗口操作系统提供的附加错误代码。然后您可以通过documentation from microsoft 查找正确的代码。

【讨论】:

  • 谢谢,我使用了winerror 属性。您是否知道是否有一些模块列出了 winerror 值(不仅存在于 32 位 Python),即我可以在其中找到例如 ERROR_SHARING_VIOLATION 而不必将相应的值硬编码到我的代码中(即ERROR_SHARING_VIOLATION 为 32)?
  • 我不知道任何官方模块。但是网上有很多文件列出了它们,比如this one,您可以将相关部分剪切并粘贴到自定义模块中。
【解决方案2】:

在 Windows 上,使用异常字符串是安全的 它总是'该进程无法访问该文件,因为它正在被另一个进程使用'

你总是可以的

if "The process cannot access the file because it is being used by another process" in str(e):
    #file is locked
    pass

【讨论】:

  • 谢谢。 a in b 构造肯定比我使用的构造更方便。
【解决方案3】:

Lærne 是正确的,要正确确定错误原因,您只需检查其errno 属性即可。

可用错误符号列表位于errno 模块中,您可以通过检查其中定义的符号来导入和使用该模块。一个简单的例子:

import os, errno

try:
    os.remove(filename)
except OSError as e:
    if e.errno != errno.ENOENT:
        raise

重新引发异常if no such file/directory exists

【讨论】:

  • 谢谢。所以 posix errno 值列在 errno 模块中。是否有一些模块也列出了winerror 值?对于 pywin32,有 seems to be a winerror module,但我没有,可能是因为我运行的是 64 位 Python。
猜你喜欢
  • 2010-11-26
  • 2012-11-12
  • 2011-01-03
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 2013-05-08
  • 1970-01-01
  • 2013-11-30
相关资源
最近更新 更多