如何处理未处理的异常?

时间:2013-05-07 14:25:45

标签: python exception-handling

我正在清理一个API库,并试图找出处理未处理异常的最佳方法。

现在,该库可以捕获API可能出错的所有内容 - 凭据错误,服务器错误,urllib2错误,httplib错误等。会有边缘情况。

我目前的想法是,99%的图书馆用户不关心异常本身,他们只关心API调用失败。只有开发人员会关心异常。

这引出了我的解决方案:

class ApiError(Exception):
    pass

class ApiUnhandledError(ApiError):
    pass

API的已知问题会引发ApiError或特定的子类。

其他所有内容都会引发ApiUnhandledError,并隐藏原始错误,用户可以捕获或忽略该错误。

try:
    stuff
except urllib2.UrlError , e :
    raise ApiError(raised=e)
except Exception as e :
    raise ApiUnhandledError(raised=e)

这听起来像是确保用户只知道通过/失败的好方法,而开发人员可以维持一种支持方式吗?

更新

根据最佳实践的共识,我不会陷入困境。

最初的目标是让人们这样做:

try:
   stuff_a
   other_stuff
   even_more_stuff

   api.proxy(url)

   again_stuff
   again_others
 except api.ApiUnhandledError , e :
    handle error
 except api.ApiError , e :
    handle error
 except Stuff , e :
     other error
 except:
     raise

在该示例中,用户只需捕获ApiError(以及可选的ApiUnhandledError或任何其他子类)

我认为这对于每个具有自己的块的api交互都很可取:

try:
   stuff_a
   other_stuff
   even_more_stuff

   try:
       api.proxy(url)
     except api.ApiError , e :
        handle error
     except CatchSomething1 , e :
        handle error
     except CatchSomething2 , e :
        handle error
     except CatchSomething3 , e :
        handle error
     except CatchSomething4 , e :
        handle error
     except:
        raise

   again_stuff
   again_others
 except Stuff , e :
     other error
 except:
     raise

在处理urllib2时,我似乎每天都会发现一个新的例外。这些例外往往很长很难维护。

1 个答案:

答案 0 :(得分:3)

如果你的图书馆提出了一个你没有预见过但你没有处理的例外情况,那就这样吧。它可能是库错误的标志,否则将被忽略。如果您可以明确错误的原因,可以捕获并重新启动(例如,捕获socket.error并在验证方法中重新启动AuthenticationFailedError),但是掩盖点的不公平是不公平的。库用户(他们自己是程序员)失败了。

用户不应试图处理或消除直接来自库内的错误(即不是您的API的一部分 - 大致,不是由您编写或提出的),因为它们内部到那段代码。如果图书馆作者忘记处理它们或抓住并重新启动更具体的一个,那就是一个bug,应该报告。如果函数对输入做出假设(例如,对于速度),则应在API参考中明确说明,并且任何违规都是用户的错误。

在Python2中,只能引发经典类或继承自BaseException的新样式类,在Python3中,经典类已经消失,因此您的库可能引发的任何内容都必须从BaseException继承。用户通常要处理的任何异常(这不包括SystemExitGeneratorExitKeyboardInterrupt等,这些都是特殊情况)必须从Exception继承。如果没有这样做,将在报告时报告:

Python3:

>>> class Test:
...     pass
... 
>>> raise Test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: exceptions must derive from BaseException

Python2:

>>> class Test(object):
...     pass
... 
>>> raise Test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: exceptions must be old-style classes or derived from BaseException, not Test
>>> 

也就是说,您不需要在UnhandledException容器中包装异常,只需使代码中的所有异常都继承自Exception。掩盖失败是不好的做法(你不应该鼓励它),但是懒惰的用户仍然可以利用Exception基类的继承并用这个来捕获所有未处理的异常:

try:
    yourapi.call_me()
except APIKnownError as error:
    report(error)
except Exception as error:
    pass
    # Or whatever

值得注意的是,Python提供了一个warnings模块,用于报告“程序中的某些条件,其中该条件(通常)不保证引发异常并终止程序”

说到应用程序框架(而不是库),我非常喜欢Tornado方法:它将记录所有未处理的异常,如果错误不重要则继续。我认为这个决定应由最终用户决定。