Python:我怎么知道哪些exception可能会从方法调用中抛出
有没有一种方法知道(在编码时)执行python代码期望的exception? 由于我不知道可能引发哪种exceptiontypes,所以我最终在90%的时间内捕获了基类exception类(并且不要告诉我阅读文档,很多时候exception可以从深处传播出来,次文档不更新或正确)。 有没有某种工具来检查这个? (如通过阅读Python代码和库)?
我想一个解决scheme可能只是因为缺乏静态的input规则而不精确。
我不知道有一些工具可以检查exception,但是你可以拿出自己的工具来满足你的需求(这是一个很好的机会,可以用静态分析)。
作为第一次尝试,您可以编写一个函数来构buildAST,find所有Raise
节点,然后尝试找出引发exception的常见模式(例如直接调用构造函数)
让x
是以下程序:
x = '''\ if f(x): raise IOError(errno.ENOENT, 'not found') else: e = g(x) raise e '''
使用compiler
包构buildAST:
tree = compiler.parse(x)
然后定义一个Raise
访问者类:
class RaiseVisitor(object): def __init__(self): self.nodes = [] def visitRaise(self, n): self.nodes.append(n)
然后走AST收集Raise
节点:
v = RaiseVisitor() compiler.walk(tree, v) >>> print v.nodes [ Raise( CallFunc( Name('IOError'), [Getattr(Name('errno'), 'ENOENT'), Const('not found')], None, None), None, None), Raise(Name('e'), None, None), ]
你可以继续使用编译器符号表来parsing符号,分析数据的依赖关系等。或者你可以推断出, CallFunc(Name('IOError'), ...)
“肯定意味着提高IOError
”,这对于快速的实际效果:)
你应该只捕捉你将要处理的exception。
通过具体的types捕获所有的exception是无稽之谈。 你应该抓住特定的例外,你可以 并将处理。 对于其他的例外情况,你可以编写一个捕获“基本exception”的通用catch,logging它(使用str()
函数)并终止你的程序(或者在崩溃的情况下做其他适当的事情)。
如果你真的要处理所有的exception,并确定没有一个是致命的(例如,如果你在某种沙箱环境下运行代码),那么捕获genericsBaseException的方法就符合你的目的。
您可能也对语言exception引用感兴趣,而不是您正在使用的库的参考。
如果库引用真的很差,并且在捕获系统exception时不重新抛出它自己的exception, 唯一有用的方法是运行testing (也许将其添加到testing套件中,因为如果某些东西没有logging,它可能会改变!) 。 删除对您的代码至关重要的文件,并检查正在抛出什么exception。 提供太多的数据并检查产生的错误。
无论如何,你将不得不运行testing,因为即使通过源代码获取exception的方法存在,也不会告诉你如何处理这些exception 。 也许你应该显示错误信息“File needful.txt is not found!” 当你抓到IndexError
? 只有testing可以告诉。
解决这个问题的正确工具是unittests。 如果你有真正的代码提出exception,单位testing不提高,那么你需要更多的单位testing。
考虑一下
def f(duck): try: duck.quack() except ??? could be anything
鸭子可以是任何物体
如果鸭子没有嘎嘎,显然你可以有一个AttributeError
,如果鸭子有一个嘎嘎,但是它是不可调用的。 你不知道duck.quack()
可能会引发什么,甚至可能是一个DuckError
或什么的
现在假设你有这样的代码
arr[i] = get_something_from_database()
如果它引发了IndexError
你不知道它是来自arr [i]还是来自数据库函数内部。 通常情况下,发生exception的地方并不重要,而是出现了一些问题,而您想要发生的事情也没有发生。
一个方便的技术是捕捉,也许重新这样的例外
except Exception as e #inspect e, decide what to do raise
我在使用套接字时遇到了这个问题,我想找出所有的错误条件(我不想试图创build错误并找出我想要的简洁列表)。 最后,我最终为“raise”grep'ing“/usr/lib64/python2.4/test/test_socket.py”:
$ grep raise test_socket.py Any exceptions raised by the clients during their tests raise TypeError, "test_func must be a callable function" raise NotImplementedError, "clientSetUp must be implemented." def raise_error(*args, **kwargs): raise socket.error def raise_herror(*args, **kwargs): raise socket.herror def raise_gaierror(*args, **kwargs): raise socket.gaierror self.failUnlessRaises(socket.error, raise_error, self.failUnlessRaises(socket.error, raise_herror, self.failUnlessRaises(socket.error, raise_gaierror, raise socket.error # Check that setting it to an invalid value raises ValueError # Check that setting it to an invalid type raises TypeError def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout, def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout,
这是一个非常简洁的错误列表。 现在当然这只适用于个案,取决于testing的准确性(通常是这些testing)。 否则,你需要几乎捕捉所有的exception,logging下来并parsing它们,弄清楚如何处理它们(unit testing不会太难)。
目前还没有人解释,为什么你不能有一个完整的100%正确的例外列表,所以我认为这是值得评论。 其中一个原因就是一stream的function。 假设你有这样的function:
def apl(f,arg): return f(arg)
现在apl
可以引发任何exception。 虽然在核心库中没有太多function,但是使用自定义filter,map,reduce等的列表理解的任何东西都会受到影响。
文档和源代码分析器是这里唯一“严重”的信息来源。 只要记住他们不能做的事情。
通常情况下,您只需要在几行代码中捕获exception。 你不希望把你的整个main
函数放到try except
语句中。 对于你现在总是应该每隔几行(或能够容易地检查)可能引发什么样的exception。
docs有一个内置的例外的详尽清单 。 不要试图去除那些你不期望的exception,它们可能会在调用代码中被处理/预期。
编辑 :什么可能会被抛出显然取决于你在做什么! 访问序列的随机元素: IndexError
,字典的随机元素: KeyError
等
只要尝试在IDLE中运行这些行,并导致exception。 但unit testing自然会是一个更好的解决scheme。
有两种方法,我发现信息。 第一个,运行iPython中的指令,它将显示exceptiontypes。
n = 2 str = 'me ' str + 2 TypeError: unsupported operand type(s) for +: 'int' and 'str'
在第二种方式,我们解决了捕捉太多,并改善它。 在你的代码中包含一个try语句, 除了Exception之外 ,还可以捕获它。 打印足够的数据以知道抛出了什么exception。 抛出exception通过添加更精确的except子句来改善您的代码。 如果您觉得已经caching了所有相关的exception,请删除全部包含的exception。 无论如何,这是件好事,因为它吞噬了编程错误。
try: so something except Exception as err: print "Some message" print err.__class__ print err exit(1)