Skip to content

Attribute compatibility checks should take compatibility into consideration #2454

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

Closed
refi64 opened this issue Nov 14, 2016 · 5 comments
Closed

Comments

@refi64
Copy link
Contributor

refi64 commented Nov 14, 2016

class A: pass
class B(A): pass

class X:
    attr: A

class Y:
    attr: B

class Z(X, Y):
    pass

IMO this should be valid; Z.attr should be of type A, since it's the "lowest common denominator" in this case at retains type safety.

Similar thing goes for unions, like this:

class A: pass
class B: pass
class C: pass

class X:
    attr: Union[A, B]

class Y:
    attr: Union[B, C]

class Z(X, Y):
    # attr should be Union[A, B, C]
    pass

and this:

class A: pass
class B(A): pass
class C: pass

class X:
    attr: Union[A, C]

class Y:
    attr: Union[B, C]

class Z(X, Y):
    # attr should be Union[A, C]
    pass
@refi64 refi64 changed the title Attribute compatibility checks should take inheritance into consideration Attribute compatibility checks should take compatibility into consideration Nov 14, 2016
@refi64
Copy link
Contributor Author

refi64 commented Nov 14, 2016

As an extension to this, maybe also attributes declared in the derived class could override it:

class A: pass
class B(A): pass

class X:
    attr: A

class Y:
    attr: B

class Z(X, Y):
    attr: B  # It's "overrides" the inferred type.

@gvanrossum
Copy link
Member

In the first example Y might have a method that expects attr to be a B, and if that method is called on a Z instance it might be disappointed. So maybe Z.attr should have type B?

FWIW I tried with X inheriting from Y or vice versa, and both are allowed. That seems strange?

@refi64
Copy link
Contributor Author

refi64 commented Nov 14, 2016

@gvanrossum

In the first example Y might have a method that expects attr to be a B, and if that method is called on a Z instance it might be disappointed. So maybe Z.attr should have type B?

True, but then you lose some type safety:

class A: pass
class B(A): pass
class C(A): pass

class X:
    attr: A
    def make_c(self): self.attr = C()

class Y:
    attr: B
    def ensure_b(self):
        assert isinstance(self.attr, B)

class Z(X, Y):
    pass

z = Z()
z.make_c()
z.ensure_b()  # BOOM!!!

Maybe then it should just be like I said in the second comment, that the child class can explicitly set it to a type to silence the error? That way, it's up to the user to figure out what they want to do.

@gvanrossum
Copy link
Member

gvanrossum commented Nov 15, 2016 via email

@JukkaL
Copy link
Collaborator

JukkaL commented Jan 16, 2017

Attribute compatibility across a class hierarchy is only safe if it's invariant, since attributes are mutable. This is similar to why List[T] is invariant. As pointed out by Guido, You can use # type: ignore to force mypy to accept covariant overriding.

It's a separate issue that mypy only detects attribute incompatibility when it's caused by multiple inheritance (see #2510).

Closing this as the generated errors for the original examples seem correct, though perhaps we should document this more clearly.

@JukkaL JukkaL closed this as completed Jan 16, 2017
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

3 participants