Decodes data to the Python 3.x str (Python 2.x unicode) type.

    Decodes `data` to a Python 3.x ``str`` (Python 2.x ``unicode``). If
    it can't be decoded, it is returned as is. Unsigned integers, Python
    ``bytes``, and Numpy strings (``numpy.unicode_`` and
    ``numpy.bytes_``). Python 3.x ``bytes``, Python 2.x ``str``, and
    ``numpy.bytes_`` are assumed to be encoded in UTF-8.

    Parameters
    ----------
    data : some type
        Data decode into an ``str`` string.

    Returns
    -------
    s : str or data
        If `data` can be decoded into a ``str``, the decoded version is
        returned. Otherwise, `data` is returned unchanged.

    See Also
    --------
    convert_to_numpy_str
    convert_to_numpy_bytes

def convert_to_str(data):
    """ Decodes data to the Python 3.x str (Python 2.x unicode) type.

    Decodes `data` to a Python 3.x ``str`` (Python 2.x ``unicode``). If
    it can't be decoded, it is returned as is. Unsigned integers, Python
    ``bytes``, and Numpy strings (``numpy.unicode_`` and
    ``numpy.bytes_``). Python 3.x ``bytes``, Python 2.x ``str``, and
    ``numpy.bytes_`` are assumed to be encoded in UTF-8.

    Parameters
    ----------
    data : some type
        Data decode into an ``str`` string.

    Returns
    -------
    s : str or data
        If `data` can be decoded into a ``str``, the decoded version is
        returned. Otherwise, `data` is returned unchanged.

    See Also
    --------
    convert_to_numpy_str
    convert_to_numpy_bytes

    """
    # How the conversion is done depends on the exact  underlying
    # type. Numpy types are handled separately. For uint types, it is
    # assumed to be stored as UTF-8, UTF-16, or UTF-32 depending on the
    # size when converting to an str. numpy.string_ is just like
    # converting a bytes. numpy.unicode has to be encoded into bytes
    # before it can be decoded back into an str. bytes is decoded
    # assuming it is in UTF-8. Otherwise, data has to be returned as is.

    if isinstance(data, (np.ndarray, np.uint8, np.uint16, np.uint32,
                  np.bytes_, np.unicode_)):
        if data.dtype.name == 'uint8':
            return data.tostring().decode('UTF-8')
        elif data.dtype.name == 'uint16':
            return data.tostring().decode('UTF-16')
        elif data.dtype.name == 'uint32':
            return data.tostring().decode('UTF-32')
        elif data.dtype.char == 'S':
            return data.decode('UTF-8')
        else:
            if isinstance(data, np.ndarray):
                return data.tostring().decode('UTF-32')
            else:
                return data.encode('UTF-32').decode('UTF-32')

    if isinstance(data, bytes):
        return data.decode('UTF-8')
    else:
        return data