| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
Thanks for picking this up — I filed #21869. I applied this patch to a v2.3.0 checkout and tested it. It fixes the issue, and the output is not just error-free but type-equivalent to the source. Running the same consumer against the generated stub and against the original module:
So dropping async from the emitted signature preserves the decorated type exactly, which I think settles that this spelling is the right one of the two the typing docs allow. I also checked the failure mode this design could have — _has_async_contextmanager is instance state, so the risk is it leaking into a following function and stripping a legitimate async. It doesn't. Interleaving the cases: @asynccontextmanager
async def ctx() -> AsyncIterator[int]: ... # -> async dropped
async def plain_coro() -> int: ... # -> async kept
@contextmanager
def sync_ctx() -> Iterator[int]: ... # -> unchanged
class A:
@asynccontextmanager
async def actx(self) -> AsyncGenerator[str, None]: ... # -> dropped
async def method_coro(self) -> int: ... # -> kept
@staticmethod
@asynccontextmanager
async def sctx() -> AsyncIterator[int]: ... # -> dropped, both decorators keptAll correct. That holds because the flag is set in process_decorator and cleared in clear_decorators, i.e. exactly the lifecycle _decorators already has. One suggestion: that non-interference isn't pinned by a test. Since it's the single property this approach could regress on, and the harness compares whole-file output, it costs four lines folded into the case you already added rather than a new one: [case testAsyncContextManager]
from collections.abc import AsyncGenerator, AsyncIterator
from contextlib import asynccontextmanager
@asynccontextmanager
async def ctx() -> AsyncIterator[int]:
yield 1
+async def coro() -> int:
+ return 1
+
class A:
@asynccontextmanager
async def ctx(self) -> AsyncGenerator[str, None]:
yield "value"
+
+ async def coro(self) -> int:
+ return 1
[out]
from collections.abc import AsyncGenerator, AsyncIterator
from contextlib import asynccontextmanager
@asynccontextmanager
def ctx() -> AsyncIterator[int]: ...
+async def coro() -> int: ...
class A:
@asynccontextmanager
def ctx(self) -> AsyncGenerator[str, None]: ...
+ async def coro(self) -> int: ...I generated that [out] block from the patched stubgen rather than writing it by hand, but worth re-running pytest mypy/test/teststubgen.py to confirm it matches under the harness. |
Sorry, something went wrong.
|
Thanks for the thorough validation and suggestion. Added the top-level and method coroutine non-interference cases in 2fe117a, preserving async for both generated outputs. Validation with the repository's pinned test requirements: python3 -m pytest mypy/test/teststubgen.py (374 passed, 1 skipped, 2 xfailed); git diff --check passed. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Fixes #21869
Stubgen preserved both the asynccontextmanager decorator and the async keyword, producing a stub that mypy rejects because a stub body cannot establish an async generator.
This tracks when contextlib.asynccontextmanager is present and omits async only from the emitted stub signature. The decorator, return annotation, and source AST behavior remain otherwise unchanged. Regression coverage includes both a module function and a method.
Tests: