From bfd7bb49b467e7ab586deb475c23974a91df4dab Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Thu, 3 Sep 2020 07:16:58 +0200 Subject: [PATCH] Ignore inherited field when comparing Attributes (#684) * Ignore inherited field when comparing Attributes fixes #682 * add newsfragment --- changelog.d/684.change.rst | 1 + src/attr/_make.py | 9 +++++++-- tests/test_make.py | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 changelog.d/684.change.rst diff --git a/changelog.d/684.change.rst b/changelog.d/684.change.rst new file mode 100644 index 000000000..c8dc8cea6 --- /dev/null +++ b/changelog.d/684.change.rst @@ -0,0 +1 @@ +The ``inherited`` field of ``attr.Attribute`` (introduced in 20.1.0) instances is not considered when hashing and comparing anymore. diff --git a/src/attr/_make.py b/src/attr/_make.py index be7cbd8bf..088f2a5df 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -2185,6 +2185,8 @@ class Attribute(object): .. versionadded:: 20.1.0 *inherited* .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. For the full version history of the fields, see `attr.ib`. """ @@ -2354,8 +2356,11 @@ def _setattrs(self, name_values_pairs): ] Attribute = _add_hash( - _add_eq(_add_repr(Attribute, attrs=_a), attrs=_a), - attrs=[a for a in _a if a.hash], + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], ) diff --git a/tests/test_make.py b/tests/test_make.py index a8e28b32c..d3f4a279e 100644 --- a/tests/test_make.py +++ b/tests/test_make.py @@ -691,6 +691,26 @@ def test_sugar_callable(self): class C(object): x = attr.ib(factory=Factory(list)) + def test_inherited_does_not_affect_hashing_and_equality(self): + """ + Whether or not an Attribute has been inherited doesn't affect how it's + hashed and compared. + """ + + @attr.s + class BaseClass(object): + x = attr.ib() + + @attr.s + class SubClass(BaseClass): + pass + + ba = attr.fields(BaseClass)[0] + sa = attr.fields(SubClass)[0] + + assert ba == sa + assert hash(ba) == hash(sa) + @pytest.mark.skipif(PY2, reason="keyword-only arguments are PY3-only.") class TestKeywordOnlyAttributes(object):