Skip to content

Attribute compatibility checks should take compatibility into consideration #2454

Closed

Description

@refi64
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

Activity

  1. changed the title [-]Attribute compatibility checks should take inheritance into consideration[/-] [+]Attribute compatibility checks should take compatibility into consideration[/+] on Nov 14, 2016
  2. refi64 commented on Nov 14, 2016

    @refi64
    ContributorAuthor

    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.
    
  3. gvanrossum commented on Nov 14, 2016

    @gvanrossum
    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?

  4. refi64 commented on Nov 14, 2016

    @refi64
    ContributorAuthor

    @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.

  5. gvanrossum commented on Nov 15, 2016

    @gvanrossum
    Member
  6. JukkaL commented on Jan 16, 2017

    @JukkaL
    Collaborator

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions