简体   繁体   English

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

[英]Why does asyncio.CancelledError need to be re-raised?

I have the following aiohttp WebSocket handler:我有以下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

According to the documentation , " In almost all situations the exception [asyncio.exceptions.CancelledError] must be re-raised "根据文档,“在几乎所有情况下,必须重新引发异常 [asyncio.exceptions.CancelledError]

Do I need to re-raise the exception in the location marked in the code?我需要在代码中标记的位置重新引发异常吗? This would require me to rewrite the code which removes the client from the client list.这将需要我重写从客户端列表中删除客户端的代码。 Would I also need to re-raise the catch-all except block which comes after the asyncio.exceptions.CancelledError block?我是否还需要重新引发asyncio.exceptions.CancelledError块之后的全部except块?

Why do I need to re-raise asyncio.exceptions.CancelledError in the first place, if I should need to do that in this case?如果在这种情况下我需要这样做,为什么我首先需要重新引发asyncio.exceptions.CancelledError In which situations wouldn't I need to re-raise that exception?在哪些情况下我不需要重新提出该异常?

In case of catching CancelledError , you need to be very careful.在捕获CancelledError情况下,您需要非常小心。

Prior to Python 3.8 , it was easy to unintentionally suppress this error with code like this:Python 3.8之前,很容易用这样的代码无意中抑制这个错误:

try:
    await operation()
except Exception:
    log.error('Operataion failed. Will retry later.')

Since Python 3.8 CancelledError is subclass of BaseException and it is necessary to always explicitly handle this error:由于 Python 3.8 CancelledErrorBaseException子类,因此有必要始终显式处理此错误:

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

The main issue here is that you cannot cancel a task that suppress CancelledError .这里的主要问题是您无法取消取消CancelledError的任务

But, in any case, the recommendation to re-raise is not absolute and is given for the general case.但是,无论如何,重新加注的建议不是绝对的,而是针对一般情况给出的。 If you know what you are doing, you can handle the CancelledError and finish the coroutine without throwing it again.如果您知道自己在做什么,则可以处理CancelledError并完成协程,而不会再次抛出它。 It should be minded that when a task is cancelled, its cancellation will generally look like a chain of CancelledError s, which will be thrown from the innermost await call and thrown up the chain of await s, and in this case, we break this chain, and must correctly handle this situation.需要注意的是,当一个任务被取消时,它的取消通常看起来像一个CancelledError链,它会从最里面的await调用抛出并向上抛出await链,在这种情况下,我们打破这个链,并且必须正确处理这种情况。

In the case of the aiohttp websocket handler, I think it is acceptable not to re-raise the CancelledError if you ensure that the resources are cleaned up correctly and the handler exits.aiohttp websocket 处理程序的情况下,如果您确保正确清理资源并且处理程序退出,我认为不重新引发CancelledError是可以接受的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM