Closed
Description
Bug Report
I hope I got the title approximately right. In the example below, mypy
does not seem to properly bind the return type of the decorated function (int
) to T
(or the other way around).
To Reproduce
Example 1: decorator factory
from typing import Callable, ParamSpec, TypeVar, reveal_type
T = TypeVar("T")
P = ParamSpec("P")
def decorator_factory(
decorator_: Callable[[Callable[P, T]], Callable[P, T]]
) -> Callable[[Callable[P, T]], Callable[P, T]]:
return decorator_
@decorator_factory
def decorator(fun_: Callable[P, T]) -> Callable[P, T]:
return fun_
@decorator
def fun() -> int:
return 0
var = fun()
print(type(var)) # int
reveal_type(decorator_factory)
reveal_type(decorator)
reveal_type(fun)
reveal_type(var) # expected 'int', got 'T`-2' (Pylance reports 'int')
Example 2: decorator.decorator
-style decorator without arguments
from typing import Callable, TypeVar, reveal_type
T = TypeVar("T")
# Trivial implementation of decorator.decorator[x]
# (without arguments) to investigate proper typing
def decorator(
caller_: Callable[[Callable[[], T]], T]
) -> Callable[[Callable[[], T]], Callable[[], T]]:
def decorator_(function_: Callable[[], T]) -> Callable[[], T]:
def decorated_function() -> T:
return caller_(function_)
return decorated_function
return decorator_
@decorator
def caller(function_: Callable[[], T]) -> T:
return function_()
@caller
def function() -> int:
return 0
var = function()
reveal_type(decorator)
reveal_type(caller)
reveal_type(function)
reveal_type(var) # expected 'int', got 'T`-1' (Pylance reports 'int')
Example 3: decorator.decorator
-style decorator with arguments (crashes with 1.4.1, see #15824; does not crash with #15837)
from typing import Callable, Concatenate, ParamSpec, TypeVar, reveal_type
T = TypeVar("T")
P = ParamSpec("P")
# Trivial implementation of decorator.decorator[x]
# (with arguments) to investigate proper typing
def decorator(
caller_: Callable[Concatenate[Callable[P, T], P], T]
) -> Callable[[Callable[P, T]], Callable[P, T]]:
def decorator_(function_: Callable[P, T]) -> Callable[P, T]:
def decorated_function(*args: P.args, **kwargs: P.kwargs) -> T:
return caller_(function_, *args, **kwargs)
return decorated_function
return decorator_
@decorator
def caller(function_: Callable[P, T], /, *args: P.args, **kwargs: P.kwargs) -> T:
return function_(*args, **kwargs)
@caller
def function() -> int:
return 0
var = function()
reveal_type(decorator)
reveal_type(caller)
reveal_type(function)
reveal_type(var) # expected 'int', got 'T`-2' (Pylance reports 'int')
Expected Behavior
- int
Actual Behavior
- T-2 or T-1
Your Environment
- Mypy version used: 1.4.1
- Mypy command-line flags: none
- Python version used: 3.11.4