Skip to content

fix: fix python 3.10 workflow asyncio.timeout usage#4877

Open
sasha-gitg wants to merge 6 commits intogoogle:v2from
sasha-gitg:fix-python-3.10-workflow
Open

fix: fix python 3.10 workflow asyncio.timeout usage#4877
sasha-gitg wants to merge 6 commits intogoogle:v2from
sasha-gitg:fix-python-3.10-workflow

Conversation

@sasha-gitg
Copy link
Collaborator

No description provided.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily addresses an incompatibility issue with asyncio.timeout across different Python versions, particularly affecting Python 3.10 workflows. It introduces a robust, version-conditional timeout mechanism to ensure that asynchronous operations can be reliably timed out, enhancing the stability and cross-version compatibility of the workflow system. Minor, unrelated test skips were also included to prevent failures on known, temporary issues.

Highlights

  • Python Version Compatibility for Asyncio Timeout: Implemented a version-aware _timeout_context to ensure consistent asyncio.timeout behavior across different Python versions. For Python 3.11+, it uses the built-in asyncio.timeout. For older versions (e.g., Python 3.10), it attempts to use async_timeout and falls back to a custom implementation if async_timeout is not installed.
  • Robust Timeout Exception Handling: Updated exception handling to catch both TimeoutError (raised by the custom fallback) and asyncio.TimeoutError (raised by asyncio.timeout and async_timeout), ensuring that node timeouts are consistently handled regardless of the underlying timeout mechanism.
  • Temporary Test Skips: Two tests in test_llm_agent.py were temporarily skipped due to an issue where event.output is not set correctly for task mode with an output schema. Additionally, one test in test_litellm.py was skipped due to a change in LiteLLM's finish_reason mapping behavior.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/python-unit-tests.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@adk-bot adk-bot added the core [Component] This issue is related to the core interface and implementation label Mar 18, 2026
@adk-bot
Copy link
Collaborator

adk-bot commented Mar 18, 2026

Response from ADK Triaging Agent

Hello @sasha-gitg, thank you for creating this PR!

This PR is a bug fix, could you please associate the github issue with this PR? If there is no existing issue, could you please create one?

In addition, could you please provide logs or screenshot after the fix is applied?

This information will help reviewers to review your PR more efficiently. Thanks!

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a compatibility layer for asyncio.timeout to support Python versions older than 3.11, which is a good improvement. My review focuses on the correctness of this fallback implementation. I've identified a significant issue where the fallback incorrectly handles task cancellations, potentially masking the true cause of errors. I've provided a detailed comment with a suggested fix to make the implementation more robust and align it with the behavior of the standard library's asyncio.timeout.

Comment on lines +62 to +76
@contextlib.asynccontextmanager
async def _timeout_context(delay):
"""Fallback for Python <3.11 without async_timeout."""
if delay is None:
yield
return
task = asyncio.current_task()
loop = asyncio.get_running_loop()
handle = loop.call_later(delay, task.cancel)
try:
yield
except asyncio.CancelledError:
raise TimeoutError(f'Node timed out after {delay}s') from None
finally:
handle.cancel()
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The current fallback implementation for _timeout_context incorrectly treats all asyncio.CancelledError exceptions as timeouts. This can mask the true cause of cancellation if the task is cancelled externally, leading to incorrect error handling.

The standard asyncio.timeout context manager correctly distinguishes between a timeout and an external cancellation. This fallback should mimic that behavior by re-raising asyncio.CancelledError if it was not caused by the timeout.

I suggest using a flag to track whether the timeout was triggered, and only raise TimeoutError in that case.

    @contextlib.asynccontextmanager
    async def _timeout_context(delay):
      """Fallback for Python <3.11 without async_timeout."""
      if delay is None:
        yield
        return
      task = asyncio.current_task()
      loop = asyncio.get_running_loop()
      timed_out = False

      def on_timeout():
        nonlocal timed_out
        timed_out = True
        task.cancel()

      handle = loop.call_later(delay, on_timeout)
      try:
        yield
      except asyncio.CancelledError:
        if timed_out:
          raise TimeoutError(f'Node timed out after {delay}s') from None
        raise  # Re-raise external cancellations
      finally:
        handle.cancel()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core [Component] This issue is related to the core interface and implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants