捕获模块已捕获的Python异常

jckbn6z7  于 2023-01-01  发布在  Python
关注(0)|答案(1)|浏览(170)

我正在使用Python的requests模块通过SOCKS4代理连接到一个网站,当尝试连接到网站时,程序甚至无法连接到SOCKS4,因此PySocks模块抛出TimeoutError异常,该异常被捕获并作为ProxyConnectionError异常重新抛出。
如果这就是故事的结尾,我可以直接捕获ProxyConnectionError,但是底层的urllib3模块捕获异常并重新引发NewConnectionError,您可以在官方源代码中看到这一点。
下面是我从程序中得到的最后一个回溯(为简洁起见,删掉了很多行):

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)

我的目标是捕获所有的PySocks错误(比如本例中引发的ProxyConnectionError),这可以通过捕获基本异常类socks.ProxyError来实现。
由于requests库是一个下载的模块,我没有editing the underlying code的自由(如果我直接编辑源代码,那么如果其他人从PyPI下载我的代码并安装requests库,这些更改将不会更新)。
有没有办法捕获已经在另一个模块中捕获的错误?

l7wslrjt

l7wslrjt1#

在做了一些挖掘之后,我发现了PEP responsible for adding __context__,这是异常对象上的一个属性,它允许隐式链接异常。
这意味着对于每个异常,都有一个__context__属性指向之前捕获的异常,使用一点节点迭代,我们可以到达这个链的底部,它包含一个None(如果它是第一个抛出的异常)。
我把所有这些代码放在一起,编写了一个函数,它使用sys.exc_info()获取当前抛出的异常,然后迭代直到遇到None,如果它找到之前抛出的异常,并且是我们要捕获的异常的子类,那么它返回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

下面是我在代码中的用法:

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

相关问题