# Attempt to implement in pure Python a Sequence class which behaves the same
# way as a CPython object that provides tp_as_sequence but not
# tp_as_number (like, for example, the built-in types list, tuple, ...).

def _proper_subinstance(a, b):
    ta = type(a)
    tb = type(b)
    return ta is not tb and issubclass(ta, tb)

class Sequence(object):
    ### -- Overload these --
    ### They should NOT return NotImplemented
    def _sq_repeat(self, count):
        raise NotImplementedError

    def _sq_concat(self, other):
        raise NotImplementedError

    ### -- Leave the rest alone --

    # Rules for 'a * b':
    #   a is a sequence and b is not:
    #     call b.__rmul__
    #     then fall back on a.__mul__
    #   a is not a sequence but b is:
    #     call a.__mul__
    #     then fall back on b.__rmul__
    #   a and b are both sequences:
    #     call a.__mul__
    # The class hierarchy is irrelevant to all of these cases. Of course,
    # Python thinks that the class hierarchy is relevant, so we have to pay
    # attention to it and counteract its attempts to use its idea of the
    # proper dispatch order.

    def __do_sq_repeat(self, other):
        if hasattr(other, "__index__"):
            return self._sq_repeat(other.__index__())
        else:
            # Failed, and we *don't* want to unwind and return NotImplemented
            raise TypeError("can't multiply sequence by non-int of type %s"
                            % (type(other).__name__))

    def __mul__(self, other):
        if (hasattr(other, "__rmul__")
            and not isinstance(other, Sequence)
            and not _proper_subinstance(other, self)):
            result = other.__rmul__(self):
            if result is not NotImplemented:
                return result
        return self.__do_sq_repeat(other)

    def __rmul__(self, other):
        if hasattr(other, "__mul__") and _proper_subinstance(self, other):
            result = other.__mul__(self)
            if result is not NotImplemented:
                return result
        return self.__do_sq_repeat(other)

    # Rules for 'a + b':
    #   a is a sequence and b is not:
    #     call b.__radd__
    #     then fall back on a.__add__
    #   a is not a sequence and b is:
    #     call a.__add__
    #   a and b are both sequences:
    #     call a.__add__
    # Again, we don't care about the class hierarchy, but Python does.

    def __add__(self, other):
        if (hasattr(other, "__radd__")
            and not isinstance(other, Sequence)
            and not _proper_subinstance(other, self)):
            result = other.__radd__(self)
            if result is not NotImplemented:
                return result
        return self._sq_concat(result)

    # __radd__ intentionally omitted

class list(Sequence):
    # ...
    pass

class tuple(Sequence):
    # ...
    pass
