在Python中封装异常



如何将应用程序与从已使用的库依赖项中爬进来的异常解耦?

[app] --uses--> [lib] --dependson--> [dependency]
                                           / /
  x- <-propagates--o <---throwsexception--' /
                                          /
    `-----needstohandle,soimports-----> --'

问题来自真实的pip代码:

  1. 模块A(req/req_set.py)取决于模块B
  2. 模块B(下载)使用模块C(请求)
  3. 模块A导入模块C以处理来自C的异常

如何在模块B中封装异常?从模块A中删除对C的依赖?如何确保原始异常的原因和细节不会丢失?换句话说,我如何用不同的名称重新评估异常?

下面的代码段做了需要的事情,但它只是Python 3:

 try:
     dependency_call()
 except DependencyError as exc:
     raise LibraryError from exc

UPDATE:我正在寻找与Python 2兼容的解决方案,Python 3添加了raise ... from ...,这几乎很好。

UPDATE 2:封装异常的目标是在[lib]中捕获它,并向[app]重新抛出一个新的异常,保留堆栈跟踪,以便调试工具仍然可以遍历代码(对于纯人工解决方案,Alex Thornton的答案应该是好的)。

您可以通过引用Exception基类来捕获任意异常:

except Exception as exc:
    raise ApplicationError from exc

要使from习惯用法在Python2中工作,您必须使用自定义异常:

class ApplicationError(Exception):
    def __init__(self, cause, trace):
        self.cause = cause
        self.trace = trace
    def __str__(self):
        return '{origin}nFrom {parent}'.format(origin=self.trace, 
                                                parent=self.cause)

然后像这样提出:

 except Exception, exc:
     raise ApplicationError(exc)

当它被提升时,它将打印cause,如果您决定也捕获ApplicationError,这也是一个可以访问的属性。

如果我说得对,你想更强地解耦并消除:

from pip._vendor import requests

和:

except requests.HTTPError as exc:

您可以通过引入一个最后的回退处理程序作为除以下子句外的所有子句的最后一个except

try:...
except A:
...   # here all your other exceptions
except Exception as exc:    # the fall-back handler
    if "HTTPError" in repr(exc):
       # do whatever you want to do with the (assumed) request.HTTPError
       # any other Exception with HTTPError in it's repr-string will be
       # caught here

退一步说,这仍然是一个紧密的耦合,明显违反了"德米特定律",因为你需要知道一个物体的一些内部甚至不在对象组合中。所以从某种意义上说,现在情况更糟了。

最新更新