Skip to content

20250312 2#746

Closed
yuji38kwmt wants to merge 5 commits intomainfrom
20250312-2
Closed

20250312 2#746
yuji38kwmt wants to merge 5 commits intomainfrom
20250312-2

Conversation

@yuji38kwmt
Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings March 12, 2026 03:43
@kci-pr-agent
Copy link
Copy Markdown

kci-pr-agent bot commented Mar 12, 2026

Title

20250312 2


Description

  • download_annotation_archiveにジョブ完了待機機能を追加

  • HTTP 409/WOKER_ALREADY_INVOKED時にリトライ実装

  • 例外キャッチを細分化してエラー処理改善

  • Locationヘッダ参照箇所を整理


Changes walkthrough 📝

Relevant files
Enhancement
wrapper.py
download_annotation_archiveの待機・例外処理強化                                       

annofabapi/wrapper.py

  • download_annotation_archivejob_access_intervalmax_job_access引数を追加
  • HTTP 409エラーでWORKER_ALREADY_INVOKED検知し待機後リトライ
  • wait_until_job_finished呼び出しによる更新ジョブ完了待機
  • 例外キャッチをValueError, KeyError, TypeErrorなどに限定
  • +38/-2   

    Need help?
  • Type /help how to ... in the comments thread for any questions about PR-Agent usage.
  • Check out the documentation for more information.
  • @kci-pr-agent
    Copy link
    Copy Markdown

    kci-pr-agent bot commented Mar 12, 2026

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    例外処理のカバレッジ

    get_annotation_archive が HTTPError 以外のステータスコード 409 を返す場合や、リトライ後にも同様のエラーが発生したときの挙動が不明瞭です。意図した例外が確実に伝播し、無限ループや未処理の状態にならないか確認してください。

    def download_annotation_archive(
        self,
        project_id: str,
        dest_path: str | Path,
        *,
        job_access_interval: int = 60,
        max_job_access: int = 360,
    ) -> str:
        """
        simpleアノテーションZIPをダウンロードする。
    
        アノテーションZIPの更新中(HTTP 409, WORKER_ALREADY_INVOKED)の場合は、
        更新ジョブ(gen-annotation)が完了するまで待ってからダウンロードする。
    
        Args:
            project_id: プロジェクトID
            dest_path: ダウンロード先のファイルパス
            job_access_interval: アノテーションZIPの更新ジョブにアクセスする間隔[秒]
            max_job_access: アノテーションZIPの更新ジョブに最大何回アクセスするか
    
        Returns:
            ダウンロード元のURL
    
        """
        try:
            _, response = self.api.get_annotation_archive(project_id)
        except requests.exceptions.HTTPError as e:
            if e.response is not None and e.response.status_code == requests.codes.conflict:
                try:
                    error_codes = [err["error_code"] for err in e.response.json()["errors"]]
                except (ValueError, KeyError, TypeError):
                    error_codes = []
                if "WORKER_ALREADY_INVOKED" in error_codes:
                    logger.info(
                        "project_id='%s' :: アノテーションZIPの更新中のため、更新ジョブ(gen-annotation)が完了するまで待ちます。",
                        project_id,
                    )
                    self.wait_until_job_finished(
                        project_id,
                        ProjectJobType.GEN_ANNOTATION,
                        job_access_interval=job_access_interval,
                        max_job_access=max_job_access,
                    )
                    _, response = self.api.get_annotation_archive(project_id)
                else:
                    raise
            else:
                raise
    ロガー参照の明示

    メソッド内で logger を使用していますが、モジュールの先頭で適切に定義・インポートされているか確認をお願いします。ローカルスコープでの未定義エラーを防止してください。

    logger.info(
        "project_id='%s' :: アノテーションZIPの更新中のため、更新ジョブ(gen-annotation)が完了するまで待ちます。",
        project_id,
    )

    @yuji38kwmt yuji38kwmt closed this Mar 12, 2026
    Copy link
    Copy Markdown

    Copilot AI left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Copilot wasn't able to review any files in this pull request.


    You can also share your feedback on Copilot code review. Take the survey.

    Comment on lines 299 to 300
    Copy link

    Copilot AI Mar 12, 2026

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    The defaults allow blocking for up to 6 hours (60s * 360) inside a synchronous download method, which can be problematic for CLI/CI usage and services (long-running requests, worker timeouts). Consider using a single timeout_seconds parameter (derived internally), lowering the defaults, and/or documenting prominently that this call may block for a long time by default.

    Copilot uses AI. Check for mistakes.
    Comment on lines 318 to 340
    Copy link

    Copilot AI Mar 12, 2026

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    After waiting, the second get_annotation_archive(project_id) call is not protected by the same 409/WORKER_ALREADY_INVOKED handling. If the worker is still running (or a new run starts), this will raise HTTPError and bypass the intended wait/retry behavior. Consider wrapping the retry in the same handling (or using a small bounded loop that retries on 409/WORKER_ALREADY_INVOKED until the polling budget is exhausted).

    Suggested change
    max_retry_on_conflict = 2
    retry_count = 0
    while True:
    try:
    _, response = self.api.get_annotation_archive(project_id)
    break
    except requests.exceptions.HTTPError as e:
    if e.response is not None and e.response.status_code == requests.codes.conflict:
    try:
    error_codes = [err["error_code"] for err in e.response.json()["errors"]]
    except (ValueError, KeyError, TypeError):
    error_codes = []
    if "WORKER_ALREADY_INVOKED" in error_codes and retry_count < max_retry_on_conflict:
    logger.info(
    "project_id='%s' :: アノテーションZIPの更新中のため、更新ジョブ(gen-annotation)が完了するまで待ちます。",
    project_id,
    )
    self.wait_until_job_finished(
    project_id,
    ProjectJobType.GEN_ANNOTATION,
    job_access_interval=job_access_interval,
    max_job_access=max_job_access,
    )
    retry_count += 1
    continue

    Copilot uses AI. Check for mistakes.
    Comment on lines 344 to 345
    Copy link

    Copilot AI Mar 12, 2026

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Accessing response.headers[\"Location\"] will raise a raw KeyError if the header is missing, which is hard to diagnose. Consider using an explicit check (e.g., get) and raising a clearer exception that includes project_id, status code, and available headers (or a short subset) to aid debugging.

    Copilot uses AI. Check for mistakes.
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    None yet

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants