Class decorator to generate all six rich comparison methods, based on a
    ``__key__`` method.

    Many simple classes are wrappers for very simple data, and want to defer
    comparisons to that data.  Rich comparison is very flexible and powerful,
    but makes this simple case tedious to set up.  There's the standard
    library's ``total_ordering`` decorator, but it still requires you to write
    essentially the same method twice, and doesn't correctly handle
    ``NotImplemented`` before 3.4.  It also doesn't automatically generate
    ``__ne__`` from ``__eq__``, which is a common gotcha.

    With this decorator, comparisons will be done on the return value of
    ``__key__``, in much the same way as the ``key`` argument to ``sorted``.
    For example, if you have a class representing a span of time:

    .. code-block:: python

        @keyed_ordering
        class TimeSpan(object):
            def __init__(self, start, end):
                self.start = start
                self.end = end

            def __key__(self):
                return (self.start, self.end)

    This is equivalent to the following, assuming 3.4's ``total_ordering``:

    .. code-block:: python

        @total_ordering
        class TimeSpan(object):
            def __init__(self, start, end):
                self.start = start
                self.end = end

            def __eq__(self, other):
                if not isinstance(other, TimeSpan):
                    return NotImplemented
                return (self.start, self.end) == (other.start, other.end)

            def __ne__(self, other):
                if not isinstance(other, TimeSpan):
                    return NotImplemented
                return (self.start, self.end) != (other.start, other.end)

            def __lt__(self, other):
                if not isinstance(other, TimeSpan):
                    return NotImplemented
                return (self.start, self.end) < (other.start, other.end)

    The ``NotImplemented`` check is based on the class being decorated, so
    subclassses can still be correctly compared.

    You may also implement some of the rich comparison methods in the decorated
    class, in which case they'll be left alone.

def keyed_ordering(cls):
    """Class decorator to generate all six rich comparison methods, based on a
    ``__key__`` method.

    Many simple classes are wrappers for very simple data, and want to defer
    comparisons to that data.  Rich comparison is very flexible and powerful,
    but makes this simple case tedious to set up.  There's the standard
    library's ``total_ordering`` decorator, but it still requires you to write
    essentially the same method twice, and doesn't correctly handle
    ``NotImplemented`` before 3.4.  It also doesn't automatically generate
    ``__ne__`` from ``__eq__``, which is a common gotcha.

    With this decorator, comparisons will be done on the return value of
    ``__key__``, in much the same way as the ``key`` argument to ``sorted``.
    For example, if you have a class representing a span of time:

    .. code-block:: python

        @keyed_ordering
        class TimeSpan(object):
            def __init__(self, start, end):
                self.start = start
                self.end = end

            def __key__(self):
                return (self.start, self.end)

    This is equivalent to the following, assuming 3.4's ``total_ordering``:

    .. code-block:: python

        @total_ordering
        class TimeSpan(object):
            def __init__(self, start, end):
                self.start = start
                self.end = end

            def __eq__(self, other):
                if not isinstance(other, TimeSpan):
                    return NotImplemented
                return (self.start, self.end) == (other.start, other.end)

            def __ne__(self, other):
                if not isinstance(other, TimeSpan):
                    return NotImplemented
                return (self.start, self.end) != (other.start, other.end)

            def __lt__(self, other):
                if not isinstance(other, TimeSpan):
                    return NotImplemented
                return (self.start, self.end) < (other.start, other.end)

    The ``NotImplemented`` check is based on the class being decorated, so
    subclassses can still be correctly compared.

    You may also implement some of the rich comparison methods in the decorated
    class, in which case they'll be left alone.
    """
    if '__key__' not in cls.__dict__:
        raise TypeError("keyed_ordering requires a __key__ method")

    for name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'):
        if name in cls.__dict__:
            continue
        method = _keyed_ordering_impl(name, cls)
        setattr(cls, name, method)

    return cls