Repository: jazzband/tablib
Pull request #429 (2019-11-10 21:12:35)

No __cmp__ or cmp in Python 3
Cherry-picked from #428, found by Flake8.

claudep: I wonder if we should replace this by rich comparison methods (https://portingguide.readthedocs.io/en/latest/comparisons.html#rich-comparisons).
hugovk: That might be a good idea. Or do we have anywhere in code that is comparing these fields? How might a user do so, and how is it useful?

claudep: I must admit I'm not more advanced on this question. It's just keeping the functionality without much thinking...
hugovk: I've updated the PR to add rich comparisons, with tests.

I did it by comparing the `repr(self)` with `repr(other)`.

Previously it was comparing `self.name` with `str(other).upper()` which are different ways of looking at the data.

For example, these two fields would be different:

```python
a = fields.DbfCharacterFieldDef("abc", self.length)
b = fields.DbfCharacterFieldDef("abc", self.length)
```

Because `self.name` of `a` is `ABC` and `str(other).upper()` of `b` is `ABC        C  10   0`.
claudep: That's great! Do you think you could simplify a bit by using the `total_ordering` decorator?
hugovk: Done!

Patch:
diff --git a/src/tablib/packages/dbfpy/fields.py b/src/tablib/packages/dbfpy/fields.py
index d6ee84e..8c7d022 100644
--- a/src/tablib/packages/dbfpy/fields.py
+++ b/src/tablib/packages/dbfpy/fields.py
@@ -33,12 +33,14 @@ __all__ = ["lookupFor"]  # field classes added at the end of the module
 import datetime
 import struct
 import sys
+from functools import total_ordering
 
 from . import utils
 
 # abstract definitions
 
 
+@total_ordering
 class DbfFieldDef:
     """Abstract field definition.
 
@@ -101,8 +103,14 @@ class DbfFieldDef:
         self.start = start
         self.end = stop
 
-    def __cmp__(self, other):
-        return cmp(self.name, str(other).upper())
+    def __eq__(self, other):
+        return repr(self) == repr(other)
+
+    def __ne__(self, other):
+        return repr(self) != repr(other)
+
+    def __lt__(self, other):
+        return repr(self) < repr(other)
 
     def __hash__(self):
         return hash(self.name)


Test patch:
diff --git a/tests/test_tablib_dbfpy_packages_fields.py b/tests/test_tablib_dbfpy_packages_fields.py
new file mode 100644
index 0000000..fd39717
--- /dev/null
+++ b/tests/test_tablib_dbfpy_packages_fields.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+"""Tests for tablib.packages.dbfpy."""
+
+import unittest
+
+from tablib.packages.dbfpy import fields
+
+
+class DbfFieldDefTestCompareCase(unittest.TestCase):
+    """dbfpy.fields.DbfFieldDef comparison test cases, via child classes."""
+
+    def setUp(self) -> None:
+        self.length = 10
+        self.a = fields.DbfCharacterFieldDef("abc", self.length)
+        self.z = fields.DbfCharacterFieldDef("xyz", self.length)
+        self.a2 = fields.DbfCharacterFieldDef("abc", self.length)
+
+    def test_compare__eq__(self):
+        # Act / Assert
+        self.assertEqual(self.a, self.a2)
+
+    def test_compare__ne__(self):
+        # Act / Assert
+        self.assertNotEqual(self.a, self.z)
+
+    def test_compare__lt__(self):
+        # Act / Assert
+        self.assertLess(self.a, self.z)
+
+    def test_compare__le__(self):
+        # Act / Assert
+        self.assertLessEqual(self.a, self.a2)
+        self.assertLessEqual(self.a, self.z)
+
+    def test_compare__gt__(self):
+        # Act / Assert
+        self.assertGreater(self.z, self.a)
+
+    def test_compare__ge__(self):
+        # Act / Assert
+        self.assertGreaterEqual(self.a2, self.a)
+        self.assertGreaterEqual(self.z, self.a)