Repository: python-attrs/attrs
Pull request #570 (2019-09-07 06:13:36)

Stop comparing/ordering subclasses
This has been deprecated for a year and was raising a warning.

Done in preparation for splitting cmp in eq/order.


Patch:
diff --git a/changelog.d/570.breaking.rst b/changelog.d/570.breaking.rst
new file mode 100644
index 0000000..21cc779
--- /dev/null
+++ b/changelog.d/570.breaking.rst
@@ -0,0 +1,3 @@
+``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` do not consider subclasses comparable anymore.
+
+This has been deprecated since 18.2.0 and was raising a ``DeprecationWarning`` for over a year.
diff --git a/src/attr/_make.py b/src/attr/_make.py
index 687b0f4..d7dfaaa 100644
--- a/src/attr/_make.py
+++ b/src/attr/_make.py
@@ -5,7 +5,6 @@ import linecache
 import sys
 import threading
 import uuid
-import warnings
 
 from operator import itemgetter
 
@@ -865,6 +864,9 @@ def attrs(
        :class:`DeprecationWarning` if the classes compared are subclasses of
        each other. ``__eq`` and ``__ne__`` never tried to compared subclasses
        to each other.
+    .. versionchanged:: 19.2.0
+       ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider
+       subclasses comparable anymore.
     .. versionadded:: 18.2.0 *kw_only*
     .. versionadded:: 18.2.0 *cache_hash*
     .. versionadded:: 19.1.0 *auto_exc*
@@ -1092,12 +1094,6 @@ def __ne__(self, other):
     return not result
 
 
-WARNING_CMP_ISINSTANCE = (
-    "Comparision of subclasses using __%s__ is deprecated and will be removed "
-    "in 2019."
-)
-
-
 def _make_cmp(cls, attrs):
     attrs = [a for a in attrs if a.cmp]
 
@@ -1147,53 +1143,37 @@ def _make_cmp(cls, attrs):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("lt",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) < attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     def __le__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("le",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) <= attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     def __gt__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("gt",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) > attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     def __ge__(self, other):
         """
         Automatically created by attrs.
         """
-        if isinstance(other, self.__class__):
-            if other.__class__ is not self.__class__:
-                warnings.warn(
-                    WARNING_CMP_ISINSTANCE % ("ge",), DeprecationWarning
-                )
+        if other.__class__ is self.__class__:
             return attrs_to_tuple(self) >= attrs_to_tuple(other)
-        else:
-            return NotImplemented
+
+        return NotImplemented
 
     return eq, ne, __lt__, __le__, __gt__, __ge__
 


Test patch:
diff --git a/tests/test_make.py b/tests/test_make.py
index 34919e3..54e37a9 100644
--- a/tests/test_make.py
+++ b/tests/test_make.py
@@ -1457,13 +1457,11 @@ class TestMakeCmp:
     Tests for _make_cmp().
     """
 
-    @pytest.mark.parametrize(
-        "op", ["__%s__" % (op,) for op in ("lt", "le", "gt", "ge")]
-    )
-    def test_subclasses_deprecated(self, recwarn, op):
+    def test_subclasses_cannot_be_compared(self):
         """
-        Calling comparison methods on subclasses raises a deprecation warning;
-        calling them on identical classes does not..
+        Calling comparison methods on subclasses raises a TypeError.
+
+        We use the actual operation so we get an error raised on Python 3.
         """
 
         @attr.s
@@ -1474,18 +1472,31 @@ class TestMakeCmp:
         class B(A):
             pass
 
-        getattr(A(42), op)(A(42))
-        getattr(B(42), op)(B(42))
+        a = A(42)
+        b = B(42)
 
-        assert [] == recwarn.list
+        assert a <= a
+        assert a >= a
+        assert not a < a
+        assert not a > a
 
-        getattr(A(42), op)(B(42))
+        assert (
+            NotImplemented
+            == a.__lt__(b)
+            == a.__le__(b)
+            == a.__gt__(b)
+            == a.__ge__(b)
+        )
 
-        w = recwarn.pop()
+        if not PY2:
+            with pytest.raises(TypeError):
+                a <= b
 
-        assert [] == recwarn.list
-        assert isinstance(w.message, DeprecationWarning)
-        assert (
-            "Comparision of subclasses using %s is deprecated and will be "
-            "removed in 2019." % (op,)
-        ) == w.message.args[0]
+            with pytest.raises(TypeError):
+                a >= b
+
+            with pytest.raises(TypeError):
+                a < b
+
+            with pytest.raises(TypeError):
+                a > b