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

时间:2018-09-10 11:46:24

标签: python python-asyncio

我在使用此代码时遇到了小麻烦:

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

发生超时时,它仅打印“网络问题:”。这是由附加到引发的asyncio.TimeoutError的空值引起的。

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

分别对TimeoutError进行处理很容易,但是我发现原始结构很惯用,现在一个核心库将其破坏了。是否有充分的理由呢?我的假设是-打印异常应该给我们一个出了什么错的线索-正确吗?

2 个答案:

答案 0 :(得分:3)

  

有充分的理由吗?

是的,您希望收到TimeoutError什么样的消息? “发生超时”?异常本身是不言自明的,不需要这种冗余。

  

我的假设是-打印异常应该给我们一个出了什么错的线索-正确吗?

是,不是。线索?是。完整信息?否。异常消息不是强制性的。异常的类型也是重要的信息。在许多情况下,消息本身甚至更多。

因此,首先:首先使用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')

答案 1 :(得分:1)

期望异常将提供解释该问题的消息不是Python中常规异常合同的一部分。对于诸如OSError之类的系统异常,程序必须能够获取操作系统提供的错误消息,这是事实,因为程序没有资格根据代码或异常子类型猜测消息。

但是更多的基本语言异常不能那样工作。以KeyError提出的dict.__getitem__为例:

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

从这个意义上讲,TimeoutError更像KeyError而不是OSError。当您抓到TimeoutError时,您确切地知道发生了什么-超时。您通常希望基于发生超时的事实来做某事,而不是仅向用户显示一条消息。即使您确实想提供一条消息,也要使用对您的应用程序有意义的消息,而不是Python提供的通用消息。与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") 子句中捕获了两个根本不同的异常,这使您陷入麻烦。我将这样重组代码:

For i = 1 To lRow Step 10
    Range("B" & i & ":B" & i + 9).Value = Range("A1:A10").Value
Next i