danielgtaylor / danielgtaylor/python-betterproto

AsyncChannel produces unexpected exception on cancel

オープン
#233 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
bug
主要言語
Python
スター
1.8k
フォーク
234
PR マージ指標
30日以内にマージされた PR はありません

説明

Hey there,

I'm using the AsyncChannel to send messages to a GRPC server. When receiving a grpc error the client prints some non-catched errors of the sending task in the console (related to: https://github.com/danielgtaylor/python-betterproto/issues/188).

```
future: exception=ValueError('task_done() called too many times')>
Traceback (most recent call last):
File "/home/PycharmProjects/Worker/venv/lib/python3.8/site-packages/betterproto/grpc/util/async_channel.py", line 87, in __anext__
result = await self._queue.get()
File "/usr/lib/python3.8/asyncio/queues.py", line 163, in get
await getter
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/PycharmProjects/Worker/venv/lib/python3.8/site-packages/betterproto/grpc/grpclib_client.py", line 166, in _send_messages
async for message in messages:
File "/home/PycharmProjects/Worker/venv/lib/python3.8/site-packages/betterproto/grpc/util/async_channel.py", line 93, in __anext__
self._queue.task_done()
File "/usr/lib/python3.8/asyncio/queues.py", line 206, in task_done
raise ValueError('task_done() called too many times')
ValueError: task_done() called too many times

```

When the server sends a grpc-error the error is handled in this except-block, which cancels the sending task:
https://github.com/danielgtaylor/python-betterproto/blob/master/src/betterproto/grpc/grpclib_client.py#L159-L161

This cancel-method results in throwing a CancelledError inside the current await of the sending task, which is the `get`-method on the queue of the AsyncChannel:
https://github.com/danielgtaylor/python-betterproto/blob/master/src/betterproto/grpc/util/async_channel.py#L87

In this case the `get`-method is raising a CancelledError even if the queue is empty. If this happens the `task_done`-method is called on an empty queue, and the above error message is shown.

A quick fix would be the following, however I haven't checked if this fix has any side effects:
```
try:
...
finally:
self._waiting_receivers -= 1
if not self._queue.empty():
self._queue.task_done()
```

コントリビューションガイド

コントリビューションガイドを開く

調査の方向性

src/betterproto/grpc/util/async_channel.py から始め、特に queue get と task_done の呼び出し付近にある __anext__ を確認してから、_send_messages のキャンセル処理付近にある src/betterproto/grpc/grpclib_client.py を読みます。queue が空の状態でキャンセルを再現し、送信タスクが task_done() を呼び出しすぎたことによる例外を報告しなくなっていることを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
api, backend
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。