为什么asyncio在没有任何消息的情况下引发TimeoutError?

2024-04-18 05:03:21 发布

您现在位置:Python中文网/ 问答频道 /正文

我在这个代码中遇到了一个小麻烦:

try:
    return await asyncio.wait_for(tcp_command(cmd), timeout=timeout)
except (OSError, asyncio.TimeoutError) as err:
    print(f"Network problem: {err}")

当超时发生时,它只打印“网络问题:”。它是由附加到所引发的异步IO.TimeoutError地址:

# inside wait_for():
raise futures.TimeoutError()

单独对TimeoutError进行hadle很容易,但我发现最初的构造非常惯用,现在一个核心库打破了它。有什么好的理由吗?我的假设——打印一个异常应该给我们一个出错的线索——正确吗?你知道吗


Tags: 代码cmdasyncioforreturntimeoutawaitcommand
2条回答

Is there a good reason for it?

是的,你希望从TimeoutError得到什么样的信息?”“发生超时”?例外本身是不言自明的,不需要这种冗余。你知道吗

Is my assumption - that printing an exception should give us a clue what went wrong - correct?

有没有线索?对。全部信息?否。异常消息不是必需的。异常的类型也是一个重要的信息。在许多情况下甚至比信息本身更重要。你知道吗

所以首先:使用print是错误的。Python有非常丰富的日志支持。例如logger.exception(str(exc))解决了您的问题,因为它除了记录消息外,还记录了整个回溯。至少在默认情况下,它可以自定义。你知道吗

但是如果您仍然想使用print,那么可以考虑记录整个回溯:

import traceback
# traceback.print_exc()
print(traceback.format_exc())

如果整个回溯太大,则始终可以简单地打印异常的类名:

# print(f'[{type(exc).__name__}] {exc}')
print(f'[{type(exc)}] {exc}')

或例外自定义:

try:
    return await asyncio.wait_for(tcp_command(cmd), timeout=timeout)
except OSError as err:
    print(f"Network problem: {err}")
except asyncio.TimeoutError:
    print('Timeout occured')

在Python中,异常将提供解释问题的消息的期望不是一般异常契约的一部分。对于系统异常,例如OSError,程序必须能够获得操作系统提供的错误消息,因为程序没有资格根据代码或异常子类型猜测消息。你知道吗

但更基本的语言例外情况并非如此。以dict.__getitem__引发的KeyError为例:

>>> try:
...   d[123]
... except KeyError as err:
...   print(f"Dict problem: {err}")
... 
Dict problem: 123

从这个意义上说,TimeoutError更像KeyError,而不是OSError。当你抓住TimeoutError的时候,你就知道发生了什么-一个超时。您通常希望根据发生超时的事实来执行某些操作,而不仅仅是向用户显示消息。即使您确实想提供消息,也应该使用对应用程序有意义的消息,而不是Python提供的通用消息。这与OSError不同,在OSError中,您通常只能显示来自操作系统的消息,而该消息对于调查底层问题是非常宝贵的。你知道吗

总而言之,问题是您在同一except子句中捕获了两个根本不同的异常,这给您带来了麻烦。我会像这样重构代码:

try:
    return await asyncio.wait_for(tcp_command(cmd), timeout=timeout)
except OSError as err:
    print(f"Network problem: {err}")
except asyncio.TimeoutError:
    print("Operation timed out")

相关问题 更多 >