# ABCs for containers were moved to their own module
try:
    from collections.abc import Mapping, Set, Sequence
except ImportError:
    from collections import Mapping, Set, Sequence


# Python 3 has no basestring, lets test it.
try:
    basestring  # attempt to evaluate basestring

    def isstr(s):
        return isinstance(s, basestring)

    def to_unicode(s):
        return unicode(s)

except NameError:

    def isstr(s):
        return isinstance(s, str)

    def to_unicode(s):
        return s


def is_list(obj):
    return isinstance(obj, Sequence) and not isstr(obj)


def is_map(obj):
    return isinstance(obj, Mapping)


def get_keys(obj):
    if is_map(obj):
        return obj.keys()
    elif is_list(obj):
        return range(len(obj))


def get_iter(iterable):
    if isinstance(iterable, Mapping):
        return iterable.items()
    else:
        return enumerate(iterable)


def get_subclasses(cls, _subclasses_yielded=None):
    """
    Generator recursively yielding all subclasses of the passed class (in
    depth-first order).

    Parameters
    ----------
    cls : type
        Class to find all subclasses of.
    _subclasses_yielded : set
        Private parameter intended to be passed only by recursive invocations of
        this function, containing all previously yielded classes.
    """

    if _subclasses_yielded is None:
        _subclasses_yielded = set()

    # If the passed class is old- rather than new-style, raise an exception.
    if not hasattr(cls, "__subclasses__"):
        raise TypeError('Old-style class "%s" unsupported.' % cls.__name__)

    # For each direct subclass of this class
    for subclass in cls.__subclasses__():
        # If this subclass has already been yielded, skip to the next.
        if subclass in _subclasses_yielded:
            continue

        # Yield this subclass and record having done so before recursing.
        yield subclass
        _subclasses_yielded.add(subclass)

        # Yield all direct subclasses of this class as well.
        for subclass_subclass in get_subclasses(subclass, _subclasses_yielded):
            yield subclass_subclass
