From 35501acb0cea6a0c61e8a9e02b6331adbfed6f2f Mon Sep 17 00:00:00 2001 From: Viktor Dick Date: Tue, 2 Oct 2018 11:28:48 +0200 Subject: [PATCH] Handle sorting of broken objects more gracefully. --- src/zope/sequencesort/ssort.py | 4 ++ src/zope/sequencesort/tests/test_ssort.py | 56 +++++++++++++++++++++++ tox.ini | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/zope/sequencesort/ssort.py b/src/zope/sequencesort/ssort.py index 81496c8..3653066 100644 --- a/src/zope/sequencesort/ssort.py +++ b/src/zope/sequencesort/ssort.py @@ -256,6 +256,10 @@ def __call__(self, o1, o2): # if not multsort - i is 0, and the 0th element is the key c1, c2 = o1[i], o2[i] func, multiplier = self.sf_list[i][1:3] + if c1 is _Smallest: + return -1 + elif c2 is _Smallest: + return 1 n = func(c1, c2) if n: return n * multiplier diff --git a/src/zope/sequencesort/tests/test_ssort.py b/src/zope/sequencesort/tests/test_ssort.py index 11f4b49..d4e01a5 100644 --- a/src/zope/sequencesort/tests/test_ssort.py +++ b/src/zope/sequencesort/tests/test_ssort.py @@ -17,6 +17,17 @@ # pylint:disable=blacklisted-name # pylint:disable=protected-access +# for sorting objects that are sortable but do not have any other attributes +# or string-like behavior +class _Broken(object): + def __lt__(self, other): + return True + def __eq__(self, other): + return other is self + +class _OK(object): + def __init__(self, _id): + self.id = _id class Test_sort(unittest.TestCase): @@ -211,6 +222,51 @@ def myCmp(s1, s2): ), RES_W_CUSTOM_COMPARATOR) + def test_w_sort_broken(self): + _broken = _Broken() + _ok = _OK('test') + self.assertEqual( + self._callFUT([_ok, _broken]), + [_broken, _ok] + ) + + def test_w_sort_broken_with_key(self): + _broken = _Broken() + _ok = _OK('test') + self.assertEqual( + self._callFUT([_ok, _broken], [('id',),]), + [_broken, _ok] + ) + + def test_w_sort_broken_with_key_locale(self): + # testing actual unicode literals to be sorted correctly has the + # problem that one can not assume that there is any locale actually + # installed on the system. But we can test if a broken object can be + # compared with a correct one even for a sorting that assumes stringy + # behavior + _broken = _Broken() + v1 = _OK(u'test') + self.assertEqual( + self._callFUT([v1,_broken], [('id','locale',),]), + [_broken, v1] + ) + self.assertEqual( + self._callFUT([_broken,v1], [('id','locale',),]), + [_broken, v1] + ) + + def test_w_sort_broken_with_key_locale_nocase(self): + _broken = _Broken() + v1 = _OK(u'test') + self.assertEqual( + self._callFUT([v1,_broken], [('id','locale_nocase',),]), + [_broken, v1] + ) + self.assertEqual( + self._callFUT([_broken,v1], [('id','locale_nocase',),]), + [_broken, v1] + ) + class Test_make_sortfunctions(unittest.TestCase): diff --git a/tox.ini b/tox.ini index 0651d0d..b704136 100644 --- a/tox.ini +++ b/tox.ini @@ -14,7 +14,7 @@ basepython = python3.6 commands = coverage run -m zope.testrunner --test-path=src - coverage report --fail-under=100 + coverage report --show-missing --fail-under=100 deps = {[testenv]deps} coverage