Skip to content

refactor: annotate contextmanager returns as Generator, not Iterator - #3176

Merged
Lancetnik merged 2 commits into
mainfrom
refactor/contextmanager-generator-annotations
Aug 18, 2026
Merged

Lancetnik merged 2 commits into
mainfrom
refactor/contextmanager-generator-annotations

Conversation

@vvlrff

@vvlrff vvlrff commented Aug 17, 2026 •

Copy link
Copy Markdown
Collaborator

Why are these changes needed?

In typeshed's stdlib/contextlib.pyi, contextmanager and asynccontextmanager each have two overloads, and the one accepting Iterator[T] / AsyncIterator[T] carries a @deprecated marker. pyright 1.1.409+ and Pylance therefore flag 31 sites across ag2/ and test/ with The function "asynccontextmanager" is deprecated — wording that overstates the case, since the decorator itself is not going anywhere.

The diagnostic is still worth acting on rather than silencing, because the underlying point is real: _AsyncGeneratorContextManager.__aexit__ calls gen.athrow(), so the decorator requires an actual generator. AsyncIterator is an under-specification — it type-checks, while a plain non-generator async iterator handed to the decorator fails at runtime.

The codebase was already split on this: of the 43 decorated context managers in tracked code, 12 used the generator form before this PR. This makes the convention uniform and writes it down.

What changed

  • 31 return annotations on decorated functions: 28 × AsyncIterator[T] → AsyncGenerator[T], 3 × Iterator[T] → Generator[T].
  • 21 from collections.abc import … lines adjusted. The old name is dropped only where nothing else in the file still uses it — ag2/stream.py keeps AsyncIterator because there it is nested: Generator[AsyncIterator[BaseEvent]].
  • Undecorated functions are deliberately untouched — 24 AsyncIterator return annotations remain on undecorated functions in ag2/ (27 AsyncIterator[...] occurrences in total). Outside the decorator, AsyncIterator is the correct consumer-facing type and narrowing it to AsyncGenerator would over-promise.
  • The single-argument form is used throughout: AsyncGenerator[None], not AsyncGenerator[None, None].
  • The rule is recorded in AGENTS.md under Code Style Guidelines, next to the existing from __future__ import annotations rule, so the pattern does not creep back in.

One file is knowingly left alone: docs/superpowers/plans/2026-07-23-acp-tool-gateway.md shows the old form in a historical plan snapshot, and the code that plan produced already uses AsyncGenerator. Plan documents record what was planned at the time and are not retrofitted.

Why the single-argument form is safe on Python 3.10

AGENTS.md forbids from __future__ import annotations, so on every version before 3.14 (PEP 649) these return annotations are evaluated at definition time — arity is a runtime concern, not just a type-checker one. Every site imports from collections.abc, whose types.GenericAlias does not enforce the number of type arguments, so AsyncGenerator[None] builds fine on 3.10. typing.AsyncGenerator[None] would raise TypeError: Too few arguments, but no site imports from typing. pyright with pythonVersion = "3.10" accepts the single-argument form as well, since typeshed gives the send type a PEP 696 default.

Related issue number

None — this came out of the Pylance diagnostic showing up across the repository rather than a filed issue.

Checks

  • I've included any doc changes needed for https://docs.ag2.ai/. See https://docs.ag2.ai/latest/docs/contributor-guide/documentation/ to build and test documentation locally.
    • No pages under website/ reference the pattern, so nothing on the docs site needed updating. The contributor-facing rule went into AGENTS.md.
  • I've added tests (if relevant) corresponding to the changes introduced in this PR.
    • No tests added: this changes annotations only, with no runtime behaviour change. The existing suites are the regression check instead. Locally, test/mcp test/a2a test/live test/tools test/network test/agent test/extensions test/*.py gives 1727 passed, 1 skipped, 1 xfailed.
  • I've made sure all auto checks have passed.

Beyond CI, one check worth calling out because the pipeline does not run it: pyright with pythonVersion = "3.10" and reportDeprecated enabled reports 0 remaining contextmanager deprecations (down from 31) and 0 type-argument arity errors. The 14 diagnostics that remain are pre-existing and unrelated (absent optional dependencies, a deprecation inside the exa SDK, bool.__invert__ in tests). ruff check and ruff format --check are clean across 932 files.

The arity question is the one that genuinely needed the matrix rather than a type checker, since these annotations are evaluated at definition time on everything below 3.14 — and test-ubuntu (3.10) passes, alongside 3.11 through 3.14, macOS and Windows.

AI assistance

  • I understand the changes in this PR and can explain them in my own words.
  • I have verified that the PR description accurately reflects the actual diff.
  • If AI assistance was used, I reviewed, tested, and validated the generated code/text before submitting.

typeshed marks the `@contextmanager` / `@asynccontextmanager` overload
that accepts `Iterator[T]` / `AsyncIterator[T]` as deprecated, so pyright
1.1.409+ flags 31 sites in this repo. The warning is worth acting on
rather than silencing: `_AsyncGeneratorContextManager.__aexit__` calls
`gen.athrow()`, so the decorator needs a real generator. The iterator
annotation is an under-specification — it type-checks, while a
non-generator async iterator would fail at runtime.

Rewrites the return annotation at all 31 decorated functions (27 async,
4 sync) and trims the `collections.abc` import lines. Functions that are
not decorated keep `AsyncIterator`; there it is the correct
consumer-facing type. Uses the single-argument form
(`AsyncGenerator[None]`, not `[None, None]`), which is valid on Python
3.10 because every site imports from `collections.abc`, whose
`GenericAlias` does not enforce arity — unlike `typing.AsyncGenerator`,
which would need both parameters. Records the rule in AGENTS.md.

Verified: ruff check and format clean; pyright with `pythonVersion=3.10`
and `reportDeprecated` reports 0 remaining contextmanager deprecations
and 0 arity errors (was 31); 18 of the 19 touched modules import under
3.10, the 19th needing an unavailable optional dependency; 1397 tests
pass across the touched areas.
@github-actions github-actions Bot added area:a2a A2A protocol support (ag2/a2a) area:acp ACP support for CLI coding agents (ag2/acp) area:mcp MCP integration (ag2/mcp) area:network Agent network (ag2/network) area:extensions Extensions (ag2/extensions): owned by named maintainers area:core Agent runtime core: ag2/*.py, ag2/events, ag2/response, ag2/streams area:tools Tool system, builtin tools, subagents (ag2/tools) area:providers LLM provider clients and mappers (ag2/config) labels Aug 17, 2026
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
ag2/a2a/_session.py 91.30% <100.00%> (ø)
ag2/acp/sessions.py 95.06% <100.00%> (-0.45%) ⬇️
ag2/agent.py 93.11% <100.00%> (ø)
ag2/extensions/daytona/environment.py 81.48% <100.00%> (ø)
ag2/extensions/docker/environment.py 96.22% <100.00%> (ø)
ag2/extensions/tools/search/tinyfish.py 100.00% <100.00%> (ø)
ag2/live/cascade.py 84.61% <100.00%> (ø)
ag2/live/gemini.py 29.44% <100.00%> (ø)
ag2/live/openai.py 36.07% <100.00%> (ø)
ag2/live/realtime.py 80.00% <100.00%> (ø)
... and 9 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Lancetnik
Lancetnik added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit f5cf8c5 Aug 18, 2026
26 checks passed
@Lancetnik
Lancetnik deleted the refactor/contextmanager-generator-annotations branch August 18, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:a2a A2A protocol support (ag2/a2a) area:acp ACP support for CLI coding agents (ag2/acp) area:core Agent runtime core: ag2/*.py, ag2/events, ag2/response, ag2/streams area:extensions Extensions (ag2/extensions): owned by named maintainers area:mcp MCP integration (ag2/mcp) area:network Agent network (ag2/network) area:providers LLM provider clients and mappers (ag2/config) area:tools Tool system, builtin tools, subagents (ag2/tools)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants