I don't know if it will work in JetBrains or not, but as suggested in a separate related answer, you can use a decorator with type annotations to achieve this, and since it is decorator function, you could also handle the annotations there for documentation.
import asyncio
from typing import TypeVar, Callable, Any
from typing_extensions import ParamSpec
class Stuff:
name = 3
age = 5
T = TypeVar('T')
V = TypeVar('V')
P = ParamSpec('P')
def wraps(x: Callable[P, Any]):
def decorator(f: Callable[..., V]) -> Callable[P, V]:
f.__annotations__ = {**x.__annotations__, "return": f.__annotations__["return"]}
return f
return decorator
async def do_stuff(param1: str, param2: int, param3: int = 14) -> Stuff:
pass
@wraps(do_stuff)
def run_async_thing(*args, **kwargs):
return asyncio.get_event_loop().run_until_complete(do_stuff(*args, **kwargs))
result = run_async_thing(param1="hello", param2="world")
When I tried this with PyLance, the result is that it picks up that run_async_thing
accepts the arguments of do_stuff
, and it also picks up that the return value is a Stuff
object.
The above uses the typing_extensions
module, which may not be ideal. If you want to avoid that and don't care about the return type being accurate, you could just use the whole function as the generic.
def wraps(x: T):
def decorator(f) -> T:
f.__annotations__ = x.__annotations__
return f
return decorator
With this, the return value comes through as being a coroutine, although it is not.
Another thing I've noticed is that setting f.__annotations__
doesn't seem to do much from what I can tell - at least pydoc still shows the method parameters as defined directly on the function.
Another note (your question didn't as much ask about this) is that there doesn't seem to be a way to annotate a Callable as accepting both parameters from itself as well as from another method that it wraps. Using Union[Callable[P, T], Callable[R,T]]
where P
and R
are the ParamSpec
types of the two methods and T
is the return type of the wrapper seems to get close, except that positional arguments, if any, are off, and keyword args either match one set or the other but not both at the same time.