Skip to content

Inherited attributes of subclasses no longer compare equal to the equivalent attributes of base class #682

Closed

Description

@pganssle

A change between 19.3.0 → 20.1.0 which doesn't seem to be deliberate, but may be related to #635 is that inherited attributes no longer compare equal to the attributes of the base class. Here is a MWE:

import attr

@attr.s()
class BaseClass(object):
    x = attr.ib()

@attr.s()
class SubClass(BaseClass):
    pass

assert attr.fields(BaseClass)[0].name == attr.fields(SubClass)[0].name   # Succeeds in all versions
assert attr.fields(BaseClass)[0] == attr.fields(SubClass)[0]  # Fails in 20.1.0

Interestingly, if you shadow the attribute, it works as expected:

import attr

@attr.s()
class BaseClass(object):
    x = attr.ib()

@attr.s()
class SubClass(BaseClass):
    x = attr.ib()

# Works in all versions
assert attr.fields(BaseClass)[0].name == attr.fields(SubClass)[0].name
assert attr.fields(BaseClass)[0] == attr.fields(SubClass)[0]

I think the issue is that inherited is included in the calculation of whether two Attributes are equal or not.

I discovered this when upgrading some code that does something like this:

import attr

@attr.s()
class BaseClass:
    x = attr.ib()

@attr.s()
class SubClass(BaseClass):
    y = attr.ib()

def f(x) -> None:
    print(x)

def my_function(obj : BaseClass) -> None:
    kwargs = attr.asdict(obj, filter=attr.filters.include(*attr.fields(BaseClass)))
    f(**kwargs)

if __name__ == "__main__":
    my_function(SubClass(1, 2))

The idea there is that they want to pull out the subset of the structure that corresponds to the base class.

In this case, I can imagine working around it by pulling out the name attribute for each of the Attributes (since in this case I think they would want the subclass to pass along any attribute with the name of an attribute on the base class, not just ones that are configured the same way). I can imagine there might be other problems, though, if attr.filters.include(*attr.fields(BaseClass)) is a common pattern.

Activity

  1. hynek commented on Sep 2, 2020

    @hynek
    Member

    Yeah for all practical purposes, I think we've got it wrong ATM. And I think hashing should also not take it into account. Technically it's wrong but in practice I think it makes it useless.

  2. added a commit that references this issue on Sep 2, 2020
    9e821a5
  3. hynek commented on Sep 2, 2020

    @hynek
    Member

    OK, pls check #684

  4. added 2 commits that reference this issue on Sep 2, 2020
    d8615d2
    d39fef7
  5. added a commit that references this issue on Sep 3, 2020
    bfd7bb4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions