Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decorator factory with generic is not properly bound #15594

Closed
bersbersbers opened this issue Jul 4, 2023 · 4 comments
Closed

Decorator factory with generic is not properly bound #15594

bersbersbers opened this issue Jul 4, 2023 · 4 comments
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate

Comments

@bersbersbers
Copy link

bersbersbers commented Jul 4, 2023

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
@bersbersbers bersbersbers added the bug mypy got something wrong label Jul 4, 2023
@bersbersbers
Copy link
Author

I also posted this at https://stackoverflow.com/q/76844550/880783.

@JelleZijlstra JelleZijlstra added the topic-paramspec PEP 612, ParamSpec, Concatenate label Aug 6, 2023
@ilevkivskyi
Copy link
Member

If you use the PR you mentioned and --new-type-inference all three examples seem to work correctly.

@bersbersbers
Copy link
Author

If you use the PR you mentioned and --new-type-inference all three examples seem to work correctly.

That is great, thank you! I had tried #15837 as well as --new-type-inference, but not at the same time ;) Looking forward to the next release with that PR.

@ilevkivskyi
Copy link
Member

Now that #15837 was merged I am going to close this (even though --new-type-inference is not on by default yet, there is no specific action item in this issue).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate
Projects
None yet
Development

No branches or pull requests

3 participants