Skip to content

feat(server): add async context manager support to EventQueue#743

Open
cchinchilla-dev wants to merge 2 commits intoa2aproject:mainfrom
cchinchilla-dev:feat/event-queue-async-context-manager-720
Open

feat(server): add async context manager support to EventQueue#743
cchinchilla-dev wants to merge 2 commits intoa2aproject:mainfrom
cchinchilla-dev:feat/event-queue-async-context-manager-720

Conversation

@cchinchilla-dev
Copy link
Contributor

Description

Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Follow the CONTRIBUTING Guide.
  • Make your Pull Request title in the https://www.conventionalcommits.org/ specification.
    • Important Prefixes for release-please:
      • fix: which represents bug fixes, and correlates to a SemVer patch.
      • feat: represents a new feature, and correlates to a SemVer minor.
      • feat!:, or fix!:, refactor!:, etc., which represent a breaking change (indicated by the !) and will result in a SemVer major.
  • Ensure the tests and linter pass (Run bash scripts/format.sh from the repository root to format)
  • Appropriate docs were updated (if necessary)

Fixes #720 🦕

Problem

EventQueue has a sophisticated close() with graceful/immediate modes, child propagation, and cross-version handling — but doesn't support async with. Server-side code must use explicit try/finally or risk leaking resources on exceptions:

queue = EventQueue()
try:
    await queue.enqueue_event(event)
    ...
finally:
    await queue.close()

Fix

Add __aenter__ and __aexit__ as concrete methods on EventQueue:

  • __aenter__ returns Self (via typing_extensions).
  • __aexit__ delegates to close() with default immediate=False (graceful). Code needing immediate shutdown can still call await queue.close(immediate=True) explicitly.

This enables the idiomatic pattern:

async with EventQueue() as queue:
    await queue.enqueue_event(event)
    ...
# close() called automatically, even on exceptions

Unlike the client-side hierarchy where __aenter__/__aexit__ were lifted to the abstract Client (#719), EventQueue is a concrete class with no abstract base above it — QueueManager manages queue lifecycles by task ID but does not wrap or extend EventQueue. This is the correct and only place for these methods.

Non-breaking, additive change. Manual close() and try/finally continue to work as before.

Follows the pattern established in ClientTransport (#682), BaseClient (#688), and Client (#719).

Tests

Two tests added to tests/server/events/test_event_queue.py, following the same approach as ClientTransport and BaseClient.

@cchinchilla-dev cchinchilla-dev requested a review from a team as a code owner February 26, 2026 18:23
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @cchinchilla-dev, 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 introduces asynchronous context manager support to the EventQueue class, enabling developers to manage event queue lifecycles more robustly and idiomatically. By implementing __aenter__ and __aexit__, the EventQueue can now be used with async with statements, ensuring that its close() method is automatically invoked upon exiting the context, even in the presence of exceptions. This enhancement simplifies resource management and promotes cleaner, more reliable asynchronous code.

Highlights

  • Async Context Manager Support: The EventQueue class now supports the async with statement by implementing __aenter__ and __aexit__ methods.
  • Improved Resource Management: This change ensures that EventQueue instances are automatically and gracefully closed upon exiting an async with block, even if exceptions occur, thereby preventing resource leaks and simplifying cleanup logic.
  • Idiomatic Asynchronous Code: The addition of async context manager capabilities aligns EventQueue with modern asynchronous Python patterns, promoting cleaner and more robust code, consistent with other client-side components.
  • New Test Coverage: Two dedicated asynchronous tests were added to verify the correct functionality of the EventQueue context manager, covering both normal execution and scenarios with exceptions.
Changelog
  • src/a2a/server/events/event_queue.py
    • Imported TracebackType from types and Self from typing_extensions for type hinting.
    • Implemented async def __aenter__(self) -> Self: to return the EventQueue instance itself upon entering the context.
    • Implemented async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: to ensure await self.close() is called when exiting the context.
  • tests/server/events/test_event_queue.py
    • Added test_event_queue_async_context_manager to verify that EventQueue correctly enters and exits an async with block and closes itself.
    • Added test_event_queue_async_context_manager_on_exception to confirm that close() is invoked even when an exception is raised within the async with block.
Activity
  • No human activity has been recorded on this pull request yet.
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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

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 adds async context manager support to the EventQueue class by implementing __aenter__ and __aexit__. This is a great improvement, as it allows for more idiomatic and safer resource management using async with. The implementation is clean, and the accompanying tests correctly verify the new functionality, including the happy path and exception handling. I've left a few minor stylistic suggestions in the test code to align with common Python practices for boolean assertions.

"""Test that EventQueue can be used as an async context manager."""
async with event_queue as q:
assert q is event_queue
assert event_queue.is_closed() is False
Copy link
Contributor

Choose a reason for hiding this comment

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

low

For boolean comparisons, it's more idiomatic and generally preferred to use assert not ... instead of assert ... is False.

Suggested change
assert event_queue.is_closed() is False
assert not event_queue.is_closed()

async with event_queue as q:
assert q is event_queue
assert event_queue.is_closed() is False
assert event_queue.is_closed() is True
Copy link
Contributor

Choose a reason for hiding this comment

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

low

For boolean comparisons, it's more idiomatic and generally preferred to use assert ... directly instead of assert ... is True. This is also flagged by ruff under rule SIM103.

Suggested change
assert event_queue.is_closed() is True
assert event_queue.is_closed()

with pytest.raises(RuntimeError, match='boom'):
async with event_queue:
raise RuntimeError('boom')
assert event_queue.is_closed() is True
Copy link
Contributor

Choose a reason for hiding this comment

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

low

For boolean comparisons, it's more idiomatic and generally preferred to use assert ... directly instead of assert ... is True. This is also flagged by ruff under rule SIM103.

Suggested change
assert event_queue.is_closed() is True
assert event_queue.is_closed()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion! I kept is True/is False for consistency with the existing assertions in this file (e.g., test_close_idempotent, test_is_closed_reflects_state, etc.), which all use the same style. Happy to change if maintainers prefer updating.

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.

[Feat]: Add async context manager support to EventQueue

1 participant