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

type signature in generic subclass #5664

Closed
oraluben opened this issue Sep 24, 2018 · 2 comments · Fixed by #6418
Closed

type signature in generic subclass #5664

oraluben opened this issue Sep 24, 2018 · 2 comments · Fixed by #6418

Comments

@oraluben
Copy link

from typing import Generic, Callable, TypeVar, Iterator

T = TypeVar('T')


class Box(Generic[T]):
    @classmethod
    def wrap(cls, generator: Callable[[], T]) -> 'Box[T]':
        pass


class IteratorBox(Box[Iterator[T]],
                  Generic[T]):
    pass


def g() -> int:
    return 0


r = IteratorBox.wrap(g)

b: IteratorBox = IteratorBox
reveal_type(b.wrap)
reveal_type(IteratorBox.wrap)
reveal_type(IteratorBox.wrap(g))

output of mypy is

as type "IteratorBox[Any]")
test.py:24: error: Revealed type is 'def (generator: def () -> typing.Iterator*[Any]) -> test.Box[typing.Iter
ator*[Any]]'
test.py:25: error: Revealed type is 'def [T] (generator: def () -> T`1) -> test.Box[T`1]'
test.py:26: error: Revealed type is 'test.Box[builtins.int*]'

I was expecting that mypy will find type mismatch to call IteratorBox.wrap(g), which it didn't.
And reveal_type(IteratorBox.wrap) just return the type of the method in base class.

@ilevkivskyi
Copy link
Member

This is a known issue, see #3645, but I don't want to close this because I want to collect all the use cases for type variables in class methods, so that we can understand what is actually desired and test our solution properly.

@oraluben
Copy link
Author

oraluben commented Sep 24, 2018

Thanks for your reply! I have tried the PR and found a code may get wrong checking result. If it's ok to put my repro in the PR?

ilevkivskyi added a commit that referenced this issue Feb 24, 2019
Fixes #3645
Fixes #1337
Fixes #5664

The fix is straightforward, I just add/propagate the bound type variable values by mapping to supertype.

I didn't find any corner cases with class methods, and essentially follow the same logic as when we generate the callable from `__init__` for generic classes in calls like `C()` or `C[int]()`.

For class attributes there are two things I fixed. First we used to prohibit ambiguous access:
```python
class C(Generic[T]):
    x: T
C.x  # Error!
C[int].x  # Error!
```
but the type variables were leaking after an error, now they are erased to `Any`. Second, I now make an exception and allow accessing attributes on `Type[C]`, this is very similar to how we allow instantiation of `Type[C]` even if it is abstract (because we expect concrete subclasses there), plus this allows accessing variables on `cls` (first argument in class methods), for example:
```python
class C(Generic[T]):
    x: T
    def get(cls) -> T:
        return cls.x  # OK
```

(I also added a bunch of more detailed comments in this part of code.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants