Mypy Error with Decorator Handling Both Sync and Async Functions #1930
-
Hi everyone, I have a somewhat complex decorator setup, and I'm running into a Mypy type-checking issue when using it on async functions. What the Decorator Does
Here’s the implementation: from typing import Callable, ParamSpec, TypeVar, Awaitable, overload
import asyncio
import functools
Param = ParamSpec("Param")
RetType = TypeVar("RetType")
class Logger:
@overload
@classmethod
def catch_errors(
cls, func: Callable[Param, RetType]
) -> Callable[Param, RetType | None]: ...
@overload
@classmethod
def catch_errors(
cls, func: Callable[Param, Awaitable[RetType]]
) -> Callable[Param, Awaitable[RetType | None]]: ...
@classmethod
def catch_errors(cls, func):
if asyncio.iscoroutinefunction(func):
@functools.wraps(func)
async def async_wrapper(*args: Param.args, **kwargs: Param.kwargs) -> RetType | None:
try:
return await func(*args, **kwargs)
except Exception as err:
# do some error logging
return None
return async_wrapper
else:
@functools.wraps(func)
def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> RetType | None:
try:
return func(*args, **kwargs)
except Exception as err:
# do some error logging
return None
return wrapper The Problem When I decorate an async function like this: @Logger.catch_errors
async def foo() -> int:
return 1 And then call it like this: var = await foo() Mypy gives me the following error:
What I Tried
Question How can I fix this type issue so that Mypy correctly understands the return type when decorating async functions? Any insights would be greatly appreciated! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
You need to swap the two overloads, the non-async overload is broader than the async one and will match all async functions as well. So you always end up with |
Beta Was this translation helpful? Give feedback.
You need to swap the two overloads, the non-async overload is broader than the async one and will match all async functions as well. So you always end up with
RetType | None
as the return type, which in case of your async function will beCoroutine[Any, Any, int] | None
.