为什么 asyncio.CancelledError 需要重新加注?
Why does asyncio.CancelledError need to be re-raised?
我有以下 aiohttp
WebSocket 处理程序:
async def websocket_handler(request):
ws = None
if 'my-websocket-clients' not in request.app:
request.app['my-websocket-clients'] = []
print('Websocket connection starting', request.path, request.query)
try:
ws = aiohttp.web.WebSocketResponse(autoping=True, heartbeat=10.0, compress=True)
await ws.prepare(request)
request.app['my-websocket-clients'].append(ws)
print('Websocket connection ready', len(request.app['my-websocket-clients']))
async for msg in ws:
await websocket_message(request, ws, msg)
except asyncio.exceptions.CancelledError as e:
print('Websocket connection was closed uncleanly ' + str(e))
# ~~~~~~ re-raise here? ~~~~~~
except:
traceback.print_exc()
finally:
try:
await ws.close()
except:
traceback.print_exc()
if ws in request.app['my-websocket-clients']:
request.app['my-websocket-clients'].remove(ws)
print('Websocket connection closed', len(request.app['my-websocket-clients']))
if ws is None:
ws = aiohttp.web.Response()
return ws
根据 the documentation,“在几乎所有情况下,必须重新引发异常 [asyncio.exceptions.CancelledError]”
是否需要在代码中标记的位置重新抛出异常?这将需要我重写从客户端列表中删除客户端的代码。我是否还需要重新提出 asyncio.exceptions.CancelledError
块之后的包罗万象的 except
块?
为什么我首先需要重新加注 asyncio.exceptions.CancelledError
,如果我在这种情况下需要这样做的话?在哪些情况下我不需要重新引发该异常?
万一被抓到CancelledError
,要格外小心
在 Python 3.8
之前,使用如下代码很容易无意中抑制此错误:
try:
await operation()
except Exception:
log.error('Operataion failed. Will retry later.')
由于 Python 3.8 CancelledError
是 BaseException
的子类,因此必须始终显式处理此错误:
try:
await operation()
except CancelledError:
# cleanup
raise
except Exception:
log.error('Opertaion failed. will retry later.')
这里的主要问题是您无法取消抑制的任务CancelledError
。
但是,无论如何,到re-raise的建议并不是绝对的,而是针对一般情况给出的。如果你知道自己在做什么,你可以处理 CancelledError
并完成协程而不会再次抛出它。应该注意的是,当一个任务被取消时,它的取消通常看起来像一串 CancelledError
s,将从 innermost await
调用中抛出并抛出了await
s的链条,在这种情况下,我们打破了这条链条,必须正确处理这种情况。
在 aiohttp
websocket 处理程序的情况下,如果您确保正确清理资源并且处理程序退出,我认为不 re-raise CancelledError
是可以接受的.
我有以下 aiohttp
WebSocket 处理程序:
async def websocket_handler(request):
ws = None
if 'my-websocket-clients' not in request.app:
request.app['my-websocket-clients'] = []
print('Websocket connection starting', request.path, request.query)
try:
ws = aiohttp.web.WebSocketResponse(autoping=True, heartbeat=10.0, compress=True)
await ws.prepare(request)
request.app['my-websocket-clients'].append(ws)
print('Websocket connection ready', len(request.app['my-websocket-clients']))
async for msg in ws:
await websocket_message(request, ws, msg)
except asyncio.exceptions.CancelledError as e:
print('Websocket connection was closed uncleanly ' + str(e))
# ~~~~~~ re-raise here? ~~~~~~
except:
traceback.print_exc()
finally:
try:
await ws.close()
except:
traceback.print_exc()
if ws in request.app['my-websocket-clients']:
request.app['my-websocket-clients'].remove(ws)
print('Websocket connection closed', len(request.app['my-websocket-clients']))
if ws is None:
ws = aiohttp.web.Response()
return ws
根据 the documentation,“在几乎所有情况下,必须重新引发异常 [asyncio.exceptions.CancelledError]”
是否需要在代码中标记的位置重新抛出异常?这将需要我重写从客户端列表中删除客户端的代码。我是否还需要重新提出 asyncio.exceptions.CancelledError
块之后的包罗万象的 except
块?
为什么我首先需要重新加注 asyncio.exceptions.CancelledError
,如果我在这种情况下需要这样做的话?在哪些情况下我不需要重新引发该异常?
万一被抓到CancelledError
,要格外小心
在 Python 3.8
之前,使用如下代码很容易无意中抑制此错误:
try:
await operation()
except Exception:
log.error('Operataion failed. Will retry later.')
由于 Python 3.8 CancelledError
是 BaseException
的子类,因此必须始终显式处理此错误:
try:
await operation()
except CancelledError:
# cleanup
raise
except Exception:
log.error('Opertaion failed. will retry later.')
这里的主要问题是您无法取消抑制的任务CancelledError
。
但是,无论如何,到re-raise的建议并不是绝对的,而是针对一般情况给出的。如果你知道自己在做什么,你可以处理 CancelledError
并完成协程而不会再次抛出它。应该注意的是,当一个任务被取消时,它的取消通常看起来像一串 CancelledError
s,将从 innermost await
调用中抛出并抛出了await
s的链条,在这种情况下,我们打破了这条链条,必须正确处理这种情况。
在 aiohttp
websocket 处理程序的情况下,如果您确保正确清理资源并且处理程序退出,我认为不 re-raise CancelledError
是可以接受的.