我在客户回访前跑一个小脚本,根据三项指标给每个客户的网站打分,每个客户的权重从他们的分析接口获取。获取是通过 asyncio.gather 做的,并且设了 return_exceptions=True,所以一个客户出问题不会取消其他的,对吧?响应为空时 fetch_weights 会抛出 ValueError,我把 gather 包在 try/except ValueError 里,打一条警告日志,然后回退到默认权重。
第一个客户能打印出分数,然后脚本在 eastside-cycles 上挂掉了,它的响应是空的。完全没有 “falling back to defaults” 的警告。我把它改成了 except Exception,还用 python3 -X dev main.py 跑过,都没有任何变化。
我不明白的是,TypeError 里提到的是一个 ValueError 对象,可 ValueError 明明是在 fetch_weights 里抛出的。
juniper-florals: 83.0
Traceback (most recent call last):
File "main.py", line 59, in <module>
main()
File "main.py", line 55, in main
asyncio.run(score_all(clients))
...
File "main.py", line 40, in score_all
value = score(client["metrics"], weights)
File "main.py", line 26, in score
return round(sum(metrics[k] * weights[k] for k in weights), 1)
TypeError: 'ValueError' object is not iterable
main.py 里相关的代码在下面。
async def score_all(clients):
try:
all_weights = await asyncio.gather(
*(fetch_weights(c) for c in clients),
return_exceptions=True,
)
except ValueError:
log.warning("weights fetch failed, falling back to defaults")
all_weights = [DEFAULT_WEIGHTS for _ in clients]
for client, weights in zip(clients, all_weights):
value = score(client["metrics"], weights)
print(client["slug"] + ": " + str(value))
为什么这个异常处理被跳过了?
从英语翻译·查看原文I run a small script before client check-ins that scores each client's site from three metrics, with per-client weights fetched from their analytics endpoint. The fetches go through asyncio.gather with return_exceptions=True, so one broken client doesn't cancel the rest, right? fetch_weights raises ValueError when the response is empty, and I wrapped the gather in try/except ValueError to log a warning and fall back to the default weights.
The first client prints a score, then the script dies on eastside-cycles, which has an empty response. There's no "falling back to defaults" warning at all. I changed it to except Exception, and I ran it with python3 -X dev main.py, and neither changed anything.
The part I don't get is that the TypeError mentions a ValueError object, even though the ValueError is clearly raised in fetch_weights.
juniper-florals: 83.0
Traceback (most recent call last):
File "main.py", line 59, in <module>
main()
File "main.py", line 55, in main
asyncio.run(score_all(clients))
...
File "main.py", line 40, in score_all
value = score(client["metrics"], weights)
File "main.py", line 26, in score
return round(sum(metrics[k] * weights[k] for k in weights), 1)
TypeError: 'ValueError' object is not iterable
The relevant part of main.py:
async def score_all(clients):
try:
all_weights = await asyncio.gather(
*(fetch_weights(c) for c in clients),
return_exceptions=True,
)
except ValueError:
log.warning("weights fetch failed, falling back to defaults")
all_weights = [DEFAULT_WEIGHTS for _ in clients]
for client, weights in zip(clients, all_weights):
value = score(client["metrics"], weights)
print(client["slug"] + ": " + str(value))
Why is the handler skipped?