简体   繁体   English

捕获模块已捕获的 Python 异常

[英]Catch a Python exception that a module already caught

I am using the requests Python module to connect to a website through a SOCKS4 proxy.我正在使用requests Python 模块通过 SOCKS4 代理连接到网站。 While trying to connect to the website, the program fails to even connect to the SOCKS4.在尝试连接到网站时,该程序甚至无法连接到 SOCKS4。 Therefore, the PySocks module throws a TimeoutError exception, which gets caught and rethrown as a ProxyConnectionError exception.因此, PySocks模块抛出一个TimeoutError异常,该异常被捕获并作为ProxyConnectionError异常重新抛出。

If this was the end of the story, I could have just caught the ProxyConnectionError directly.如果这就是故事的结尾,我本可以直接捕获ProxyConnectionError However, the underlying urllib3 module catches the exception and re-raises a NewConnectionError instead.但是,底层urllib3模块会捕获异常并重新引发NewConnectionError You can see this in the official source code .你可以在官方源代码中看到这一点。

Here is the final traceback that I get from my program (cut many lines for brevity):这是我从我的程序中得到的最终回溯(为简洁起见删掉了很多行):

Traceback (most recent call last):
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
socks.ProxyConnectionError: Error connecting to SOCKS4 proxy ...

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
urllib3.exceptions.NewConnectionError: <urllib3.contrib.socks.SOCKSHTTPSConnection object at 0x0000025DA70CCDC0>: Failed to establish a new connection: ...

During handling of the above exception, another exception occurred:

... (eventually raises requests.exceptions.ConnectionError, then terminates the program)

My target is to catch all the PySocks errors (such as the ProxyConnectionError that was raised in this example), which can be done by catching the base exception class socks.ProxyError .我的目标是捕获所有 PySocks 错误(例如本示例中引发的ProxyConnectionError ),这可以通过捕获基本异常 class socks.ProxyError来完成。

As the requests library is a downloaded module, I don't have the liberty of editing the underlying code (if I edit the source code directly, then these changes won't be updated if someone else downloads my code and installs the requests library from PyPI).由于requests库是下载的模块,我没有编辑底层代码的自由(如果我直接编辑源代码,那么如果其他人下载我的代码并安装 requests 库,这些更改将不会更新皮)。

Is there any way to catch an error that was already caught inside another module?有什么方法可以捕获已经在另一个模块中捕获的错误吗?

After doing a little digging, I found the PEP responsible for adding __context__ , an attribute on exception objects which allows implicitly chained exceptions.在做了一些挖掘之后,我发现PEP 负责添加__context__ ,这是异常对象的一个属性,它允许隐式链接异常。

This means that for every exception, there is a __context__ attribute which points to the previously caught exception.这意味着对于每个异常,都有一个__context__属性指向先前捕获的异常。 Using a little bit of node iteration, we can get to the bottom of this chain, which holds a None (if it is the first thrown exception).使用一点节点迭代,我们可以到达这条链的底部,它包含一个None (如果它是第一个抛出的异常)。

Putting this all together as code, I wrote a function which gets the current thrown exception using sys.exc_info() , then iterates until it hits None .将所有这些放在一起作为代码,我写了一个 function ,它使用sys.exc_info()获取当前抛出的异常,然后迭代直到它命中None If it finds a previously thrown exception which is a subclass of the one we want to catch, then it returns True :如果它发现先前抛出的异常是我们要捕获的异常的子类,则它返回True

def contains_exception(target_exc: Type[BaseException]) -> bool:
    # Extract current exception
    exception_obj: Type[BaseException] = sys.exc_info()[1]

    # The final exception will be None
    while exception_obj:
        if issubclass(exception_obj.__class__, target_exc):
            return True

        # Iterate to next exception in the "During handling" chain
        exception_obj: Optional[BaseException] = exception_obj.__context__

    # Target exception wasn't found
    return False

And here is how I used it in my code:这是我在代码中使用它的方式:

try:
    ...
except BaseException:
    if contains_exception(socks.ProxyError):
        # We caught the ProxyConnectionError
        ...
    else:
        # Send the error back up, it's not relevant to the proxy
        raise

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM