为什么需要重新引发 asyncio.CancelledError ?

Dan*_*l F 3 python exception websocket python-asyncio aiohttp

我有以下aiohttpWebSocket 处理程序:

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
Run Code Online (Sandbox Code Playgroud)

根据文档,“在几乎所有情况下,都必须重新引发异常 [asyncio.exceptions.CancelledError]

我是否需要在代码中标记的位置重新引发异常?这需要我重写从客户端列表中删除客户端的代码。我是否还需要重新引发except该块之后的包罗万象的块asyncio.exceptions.CancelledError

如果在这种情况下我需要重新加注asyncio.exceptions.CancelledError,为什么我首先需要重新加注?在哪些情况下我不需要重新引发该异常?

ale*_*ame 5

万一被抓住CancelledError,你需要非常小心。

在此之前Python 3.8,使用如下代码很容易无意中抑制此错误:

try:
    await operation()
except Exception:
    log.error('Operataion failed. Will retry later.')
Run Code Online (Sandbox Code Playgroud)

由于 Python 3.8CancelledError是 的子类BaseException,因此有必要始终显式处理此错误:

try:
    await operation()
except CancelledError:
    # cleanup
    raise
except Exception:
    log.error('Opertaion failed. will retry later.')
Run Code Online (Sandbox Code Playgroud)

这里的主要问题是您无法取消抑制 CancelledError.

但是,无论如何,重新加注的建议并不是绝对的,而是针对一般情况给出的。如果您知道自己在做什么,则可以处理CancelledError并完成协程,而无需再次抛出它。需要注意的是,当一个任务被取消时,它的取消一般会看起来像一个s链,它会从最里面的CancelledError调用中抛出,并沿着s链向上抛出,在这种情况下,我们打破这个链,并且必须正确处理这种情况。 awaitawait

对于websocket 处理程序,我认为如果您确保正确清理资源并且处理程序退出,aiohttp则不重新引发 是可以接受的。CancelledError