diff --git a/boltons/funcutils.py b/boltons/funcutils.py index d047830..58fe0b4 100644 --- a/boltons/funcutils.py +++ b/boltons/funcutils.py @@ -569,4 +569,57 @@ def _indent(text, margin, newline='\n', key=bool): return newline.join(indented_lines) +try: + from functools import total_ordering # 2.7+ +except ImportError: + # python 2.6 + def total_ordering(cls): + """Class decorator that fills in missing comparators/ordering + methods. Backport of :func:`functools.total_ordering` to work + with Python 2.6. + + Code from http://code.activestate.com/recipes/576685/ + """ + convert = { + '__lt__': [ + ('__gt__', + lambda self, other: not (self < other or self == other)), + ('__le__', + lambda self, other: self < other or self == other), + ('__ge__', + lambda self, other: not self < other)], + '__le__': [ + ('__ge__', + lambda self, other: not self <= other or self == other), + ('__lt__', + lambda self, other: self <= other and not self == other), + ('__gt__', + lambda self, other: not self <= other)], + '__gt__': [ + ('__lt__', + lambda self, other: not (self > other or self == other)), + ('__ge__', + lambda self, other: self > other or self == other), + ('__le__', + lambda self, other: not self > other)], + '__ge__': [ + ('__le__', + lambda self, other: (not self >= other) or self == other), + ('__gt__', + lambda self, other: self >= other and not self == other), + ('__lt__', + lambda self, other: not self >= other)] + } + roots = set(dir(cls)) & set(convert) + if not roots: + raise ValueError('must define at least one ordering operation:' + ' < > <= >=') + root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ + for opname, opfunc in convert[root]: + if opname not in roots: + opfunc.__name__ = opname + opfunc.__doc__ = getattr(int, opname).__doc__ + setattr(cls, opname, opfunc) + return cls + # end funcutils.py diff --git a/tests/test_funcutils.py b/tests/test_funcutils.py index 27bad76..fe00c83 100644 --- a/tests/test_funcutils.py +++ b/tests/test_funcutils.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from boltons.funcutils import (copy_function, + total_ordering, InstancePartial, CachedInstancePartial) @@ -39,3 +40,24 @@ def test_copy_function(): callee_copy = copy_function(callee) assert callee is not callee_copy assert callee() == callee_copy() + + +class test_total_ordering(): + @total_ordering + class Number(object): + def __init__(self, val): + self.val = int(val) + + def __gt__(self, other): + return self.val > other + + def __eq__(self, other): + return self.val == other + + num = Number(3) + assert num > 0 + assert num == 3 + + assert num < 5 + assert num >= 2 + assert num != 1