python / python/cpython

asyncio eager task inherits the creators exception state

オープン
#155,575 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

stdlib topic-asyncio type-bug
主要言語
Python
スター
77.2k
フォーク
36k
PR マージ指標
PR 指標を取得中

説明

Bug report

Bug description:

Eager tasks created from an except block inherits the creator's exception state, a bare raise re-raises it, and any other exception gets it as __context__.

Repro:

import asyncio


async def bare():
    raise


async def explicit():
    raise ValueError("child's own")


async def main(coro):
    try:
        raise ConnectionError("primary failed")
    except ConnectionError:
        t = asyncio.create_task(coro)
    try:
        await t
    except BaseException as e:
        print(f"  {type(e).__name__}: {e} | __context__: {e.__context__!r}")


for eager in (False, True):
    print("eager_task_factory:", eager)
    for coro_fn in (bare, explicit):
        loop = asyncio.new_event_loop()
        if eager:
            loop.set_task_factory(asyncio.eager_task_factory)
        try:
            loop.run_until_complete(main(coro_fn()))
        finally:
            loop.close()

expected:

eager_task_factory: False
  RuntimeError: No active exception to reraise | __context__: None
  ValueError: child's own | __context__: None
eager_task_factory: True
  RuntimeError: No active exception to reraise | __context__: None
  ValueError: child's own | __context__: None

actually:

eager_task_factory: False
  RuntimeError: No active exception to reraise | __context__: None
  ValueError: child's own | __context__: None
eager_task_factory: True
  ConnectionError: primary failed | __context__: None
  ValueError: child's own | __context__: ConnectionError('primary failed')

proposed fix - run the eager first step under an empty exception state in task_eager_start

    _PyErr_StackItem exc_state = { .exc_value = NULL, .previous_item = NULL };
    _PyErr_StackItem *prev_exc_info = ts->base.exc_info;
    ts->base.exc_info = &exc_state;
    Py_BEGIN_CRITICAL_SECTION(task);
    stepres = task_step_impl(state, task, NULL);
    Py_END_CRITICAL_SECTION();
    ts->base.exc_info = prev_exc_info;

This error also affects python implementation of task, but i think this needs a discussion in a separate issue

Have a fix ready

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs
  • gh-155578

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

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

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、issue の asyncio の例で eager-task の動作を再現し、task_eager_start エントリーポイントを調べます。eager と non-eager の例外処理を比較し、その後、別個の Python タスク実装を変更せずに、報告されている期待される出力が生成されることを確認します。

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

評価

技術スタック
c, python
領域
backend
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
30/100

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

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