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

Return signature check corner case for generic types #60

Closed
brentyi opened this issue Apr 24, 2021 · 6 comments
Closed

Return signature check corner case for generic types #60

brentyi opened this issue Apr 24, 2021 · 6 comments

Comments

@brentyi
Copy link
Contributor

brentyi commented Apr 24, 2021

Hello,

A lot of the code I'm currently writing relies pretty heavily on TypeVar definitions. Woke up this morning and found out that some of it stopped running!

It seems like the newest release of overrides adds some stricter signature checking, which throws an error when my parent class's function signatures rely on TypeVars:

import abc
from typing import TypeVar

from overrides import EnforceOverrides, overrides

T = TypeVar("T", bound="ParentClass")


class ParentClass(abc.ABC, EnforceOverrides):
    @abc.abstractmethod
    def copy(self: T) -> T:  # Specify that the output should have the same type as `self`
        """Return a copy of myself."""


class Subclass(ParentClass):
    @overrides
    def copy(self) -> "Subclass":  # TypeError: `Subclass` is not a `~T`.
        return Subclass()

I get a TypeError: 'Subclass' is not a '~T'. message, because typing_utils.issubtype doesn't consider Subclass here to be a subtype of TypeVar("T", bound="ParentClass").

Is this expected behavior? The setup is a little niche, but in my experience it's at least well-supported by mypy.

Thanks!!

@mkorpela
Copy link
Owner

@brentyi thanks for reporting!

@mkorpela
Copy link
Owner

@ashwin153 I think you fix this already.. So just a fast release now should fix this.

@mkorpela
Copy link
Owner

@brentyi I just released 4.1.1 . I tested that locally on your example file and could not reproduce the problem.
Could you verify if it fixes your problem?

@brentyi
Copy link
Contributor Author

brentyi commented Apr 25, 2021

@mkorpela really appreciate the quick release! Looks like this fixed the issue I cited above.

I'm still running into some minor errors with TypeVars.

Even more niche, but I have several instances where I override one generic signature with a more specific generic signature. Something like this:

import abc
from typing import TypeVar

from overrides import EnforceOverrides, final, overrides

BaseType = TypeVar("BaseType", bound="A")
SubType = TypeVar("SubType", bound="B")


class A(abc.ABC, EnforceOverrides):
    @abc.abstractmethod
    def copy(self: BaseType) -> BaseType:
        """Copy an object."""


class B(A):
    @final
    @overrides
    def copy(self: SubType) -> SubType:  # TypeError: `~SubType` is not a `~BaseType`.
        return self.helper()  # For this to be valid, `self` must be typed as a subclass of `B`

    @abc.abstractmethod
    def helper(self: SubType) -> SubType:
        """Helper for copy(), specific to subclasses of `B`."""


class C(B):
    def helper(self) -> "C":
        return C()


C().copy()

This results in a TypeError: '~SubType' is not a '~BaseType'. error. mypy, however, seems to be able to infer that any type that matches SubType's bound will match BaseType's bound.

@mkorpela
Copy link
Owner

mkorpela commented Apr 25, 2021

The error messages should be improved.
From your example. The method param in extending class must be at least whole BaseType.
This is to ensure that instance of A can be replaced with instance of B.
So in the copy self must not be bound to SubType and instead to BaseType or something even more general.

@mkorpela
Copy link
Owner

Then again .. self is a specific python thing that could be handled a bit differently.

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

No branches or pull requests

2 participants