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

Binding of Generic decorators on Union variables #11037

Open
twoertwein opened this issue Aug 29, 2021 · 1 comment
Open

Binding of Generic decorators on Union variables #11037

twoertwein opened this issue Aug 29, 2021 · 1 comment
Labels
bug mypy got something wrong

Comments

@twoertwein
Copy link

Bug Report

Mypy seems to bind the TypeVar of a Generic decorator incorrectly when a variable is annotated as a Union.

To Reproduce
This example is based on this untyped cython code.

from typing import Callable, Generic, Optional, TypeVar, Union, overload

F = TypeVar("F")
G = TypeVar("G")


class cache_readonly(Generic[F, G]):
    def __init__(self, func: Callable[[F], G]) -> None:
        self.func = func
        self.name = func.__name__
        self.__doc__ = getattr(func, "__doc__", None)

    @overload
    def __get__(self, obj: F, typ) -> G:
        ...

    @overload
    def __get__(self, obj: None, typ) -> "cache_readonly[F, G]":
        ...

    def __get__(self, obj: Optional[F], typ) -> Union[G, "cache_readonly[F, G]"]:
        if obj is None:
            # accessed on the class, not the instance
            return self

        # Get the cache or set a default one if needed
        cache = getattr(obj, "_cache", None)
        if cache is None:
            cache = obj._cache = {}  # type: ignore

        if self.name in cache:
            val = cache[self.name]
        else:
            val = self.func(obj)
            cache[self.name] = val
        return val

    def __set__(self, obj: F, value: G) -> None:
        raise AttributeError("Can't set attribute")


class TestA:
    @cache_readonly
    def x(self) -> str:
        return "A"


class TestB:
    @cache_readonly
    def x(self) -> str:
        return "B"


def test(obj: Union[TestA, TestB]) -> str:
    return obj.x

Expected Behavior
Mypy and pyright are happy with the code :)

Actual Behavior
mypy

test.py:55: error: Argument 1 to "__get__" of "cache_readonly" has incompatible type "Union[TestA, TestB]"; expected "TestA"
test.py:55: error: Argument 1 to "__get__" of "cache_readonly" has incompatible type "Union[TestA, TestB]"; expected "TestB"

Pyright doesn't complain (except for the __doc__ line).

Your Environment

  • Mypy version used: 0.910
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): -
  • Python version used: 3.9.6
  • Operating system and version: CentOS 3.10.0-693.5.2.el7.x86_64
@twoertwein
Copy link
Author

And for inheritance:

class TestC(TestA):
    @cache_readonly
    def x(self) -> str:
        return "C"

mypy:
error: Signature of "x" incompatible with supertype "TestA" [override]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

1 participant