我正在清理一个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时,我似乎每天都会发现一个新的例外。这些例外往往很长很难维护。
答案 0 :(得分:3)
如果你的图书馆提出了一个你没有预见过但你没有处理的例外情况,那就这样吧。它可能是库错误的标志,否则将被忽略。如果您可以明确错误的原因,可以捕获并重新启动(例如,捕获socket.error
并在验证方法中重新启动AuthenticationFailedError
),但是掩盖点的不公平是不公平的。库用户(他们自己是程序员)失败了。
用户不应试图处理或消除直接来自库内的错误(即不是您的API的一部分 - 大致,不是由您编写或提出的),因为它们内部到那段代码。如果图书馆作者忘记处理它们或抓住并重新启动更具体的一个,那就是一个bug,应该报告。如果函数对输入做出假设(例如,对于速度),则应在API参考中明确说明,并且任何违规都是用户的错误。
在Python2中,只能引发经典类或继承自BaseException
的新样式类,在Python3中,经典类已经消失,因此您的库可能引发的任何内容都必须从BaseException
继承。用户通常要处理的任何异常(这不包括SystemExit
,GeneratorExit
,KeyboardInterrupt
等,这些都是特殊情况)必须从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方法:它将记录所有未处理的异常,如果错误不重要则继续。我认为这个决定应由最终用户决定。