diff --git a/changelog.d/1616.change.md b/changelog.d/1616.change.md new file mode 100644 index 000000000..a7b4e981f --- /dev/null +++ b/changelog.d/1616.change.md @@ -0,0 +1 @@ +Fixed a bug where `include()`/`exclude()` filters compared `Attribute` instances by value; they are now compared by identity so distinct attributes with equal fields are no longer conflated. diff --git a/src/attr/filters.py b/src/attr/filters.py index 689b1705a..ff8f57581 100644 --- a/src/attr/filters.py +++ b/src/attr/filters.py @@ -36,10 +36,11 @@ def include(*what): cls, names, attrs = _split_what(what) def include_(attribute, value): + """Include.""" return ( value.__class__ in cls or attribute.name in names - or attribute in attrs + or any(attribute is a for a in attrs) ) return include_ @@ -63,10 +64,11 @@ def exclude(*what): cls, names, attrs = _split_what(what) def exclude_(attribute, value): + """Exclude.""" return not ( value.__class__ in cls or attribute.name in names - or attribute in attrs + or any(attribute is a for a in attrs) ) return exclude_ diff --git a/tests/test_filters.py b/tests/test_filters.py index 08314fa88..0aea99101 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -14,6 +14,8 @@ @attr.s class C: + """C.""" + a = attr.ib() b = attr.ib() @@ -124,3 +126,39 @@ def test_drop_class(self, excl, value): """ e = exclude(*excl) assert e(fields(C).a, value) is False + + +@attr.s +class C2: + """C2.""" + + a = attr.ib() + repeated = attr.ib() + + +@attr.s +class D2: + """D2.""" + + b = attr.ib() + repeated = attr.ib() + + +class TestSameNameAcrossClasses: + """ + Two classes may define attributes with the same name but they are + different `Attribute` instances; excluding one must not exclude the + other (https://github.com/python-attrs/attrs/issues/864). + """ + + def test_exclude_identity_not_equality(self): + """Test Exclude identity not equality.""" + e = exclude(fields(C2).repeated) + assert e(fields(C2).repeated, "x") is False + assert e(fields(D2).repeated, "x") is True + + def test_include_identity_not_equality(self): + """Test Include identity not equality.""" + i = include(fields(C2).repeated) + assert i(fields(C2).repeated, "x") is True + assert i(fields(D2).repeated, "x") is False