From 1c9b7e476b0a79514e0f33b23daf8fb0249b578a Mon Sep 17 00:00:00 2001 From: Harsh Kumar Date: Thu, 7 May 2026 19:23:43 +0530 Subject: [PATCH] fix(checker): handle unannotated coroutines in AwaitableGenerator wrapping - Combine type guard with isinstance check to avoid assertion on potentially None type - Add deferred AwaitableGenerator wrapping for unannotated functions after type inference - Apply generator type transformations (yield, receive, return) after check_func_item infers the function type - Ensures coroutines decorated with @types.coroutine or @asyncio.coroutine are properly wrapped even when type annotations are missing --- mypy/checker.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7c787d9a42c9..b42e682e0a4f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5720,8 +5720,7 @@ def visit_decorator_inner( ) -> None: # Fix the type if decorated with `@types.coroutine` or `@asyncio.coroutine`. defn = e.func - if defn.is_awaitable_coroutine: - assert isinstance(defn.type, CallableType) + if defn.is_awaitable_coroutine and isinstance(defn.type, CallableType): # Update the return type to AwaitableGenerator (unless we already did). # Note, this doesn't exist in typing.py, only in typing.pyi. if not is_named_instance(defn.type.ret_type, "typing.AwaitableGenerator"): @@ -5745,6 +5744,22 @@ def visit_decorator_inner( with self.tscope.function_scope(e.func), self.set_recurse_into_functions(): self.check_func_item(e.func, name=e.func.name, allow_empty=allow_empty) + # For unannotated functions, defn.type is only available after check_func_item + # infers it. Apply the AwaitableGenerator wrapping now if it wasn't done above. + if defn.is_awaitable_coroutine and isinstance(defn.type, CallableType): + if not is_named_instance(defn.type.ret_type, "typing.AwaitableGenerator"): + t = defn.type.ret_type + c = defn.is_coroutine + ty = self.get_generator_yield_type(t, c) + tc = self.get_generator_receive_type(t, c) + if c: + tr = self.get_coroutine_return_type(t) + else: + tr = self.get_generator_return_type(t, c) + ret_type = self.named_generic_type("typing.AwaitableGenerator", [ty, tc, tr, t]) + typ = defn.type.copy_modified(ret_type=ret_type) + defn.type = typ + # Process decorators from the inside out to determine decorated signature, which # may be different from the declared signature. sig: Type = self.function_type(e.func)