python -c "import utool as ut; print(ut.hashstr('abcd'))"

    Args:
        data (hashable):
        hashlen (int): (default = 16)
        alphabet (list): list of characters:

    Returns:
        str: hashstr

    CommandLine:
        python -m utool.util_hash --test-hashstr
        python3 -m utool.util_hash --test-hashstr
        python3 -m utool.util_hash --test-hashstr:2
        python -m utool.util_hash hashstr:3
        python3 -m utool.util_hash hashstr:3


    Example0:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> data = 'foobar'
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr(data, hashlen, alphabet)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)
        text = mi5yum60mbxhyp+x

    Example1:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> data = ''
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr(data, hashlen, alphabet)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)
        text = 0000000000000000

    Example2:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> import numpy as np
        >>> data = np.array([1, 2, 3])
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr(data, hashlen, alphabet)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)
        text = z5lqw0bzt4dmb9yy

    Example2:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> import numpy as np
        >>> from uuid import UUID
        >>> data = (UUID('7cd0197b-1394-9d16-b1eb-0d8d7a60aedc'), UUID('c76b54a5-adb6-7f16-f0fb-190ab99409f8'))
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr_arr(data, 'label')
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)


    Example3:
        >>> # DISABLE_DOCTEST
        >>> # UNSTABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> import numpy as np
        >>> data = np.array(['a', 'b'], dtype=object)
        >>> text = hashstr(data, alphabet=ALPHABET_27)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)

    Ignore:
        data = np.array(['a', 'b'], dtype=object)
        data.tobytes()
        data = np.array(['a', 'b'])
        data = ['a', 'b']
        data = np.array([1, 2, 3])
        import hashlib
        from six.moves import cPickle as pickle
        pickle.dumps(data, protocol=2)

        python2 -c "import hashlib, numpy; print(hashlib.sha1('ab').hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1('ab'.encode('utf8')).hexdigest())"

        python2 -c "import hashlib, numpy; print(hashlib.sha1('ab').hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1(b'ab').hexdigest())"

        python2 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array([1, 2])).hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array([1, 2])).hexdigest())"

        # TODO: numpy arrays of strings must be encoded to bytes first in python3
        python2 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array(['a', 'b'])).hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array([b'a', b'b'])).hexdigest())"

        python -c "import hashlib, numpy; print(hashlib.sha1(numpy.array(['a', 'b'], dtype=object)).hexdigest())"
        python -c "import hashlib, numpy; print(hashlib.sha1(numpy.array(['a', 'b'], dtype=object)).hexdigest())"

def hashstr(data, hashlen=HASH_LEN, alphabet=ALPHABET):
    """
    python -c "import utool as ut; print(ut.hashstr('abcd'))"

    Args:
        data (hashable):
        hashlen (int): (default = 16)
        alphabet (list): list of characters:

    Returns:
        str: hashstr

    CommandLine:
        python -m utool.util_hash --test-hashstr
        python3 -m utool.util_hash --test-hashstr
        python3 -m utool.util_hash --test-hashstr:2
        python -m utool.util_hash hashstr:3
        python3 -m utool.util_hash hashstr:3


    Example0:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> data = 'foobar'
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr(data, hashlen, alphabet)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)
        text = mi5yum60mbxhyp+x

    Example1:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> data = ''
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr(data, hashlen, alphabet)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)
        text = 0000000000000000

    Example2:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> import numpy as np
        >>> data = np.array([1, 2, 3])
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr(data, hashlen, alphabet)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)
        text = z5lqw0bzt4dmb9yy

    Example2:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> import numpy as np
        >>> from uuid import UUID
        >>> data = (UUID('7cd0197b-1394-9d16-b1eb-0d8d7a60aedc'), UUID('c76b54a5-adb6-7f16-f0fb-190ab99409f8'))
        >>> hashlen = 16
        >>> alphabet = ALPHABET_41
        >>> text = hashstr_arr(data, 'label')
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)


    Example3:
        >>> # DISABLE_DOCTEST
        >>> # UNSTABLE_DOCTEST
        >>> from utool.util_hash import *  # NOQA
        >>> import numpy as np
        >>> data = np.array(['a', 'b'], dtype=object)
        >>> text = hashstr(data, alphabet=ALPHABET_27)
        >>> result = ('text = %s' % (str(text),))
        >>> print(result)

    Ignore:
        data = np.array(['a', 'b'], dtype=object)
        data.tobytes()
        data = np.array(['a', 'b'])
        data = ['a', 'b']
        data = np.array([1, 2, 3])
        import hashlib
        from six.moves import cPickle as pickle
        pickle.dumps(data, protocol=2)

        python2 -c "import hashlib, numpy; print(hashlib.sha1('ab').hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1('ab'.encode('utf8')).hexdigest())"

        python2 -c "import hashlib, numpy; print(hashlib.sha1('ab').hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1(b'ab').hexdigest())"

        python2 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array([1, 2])).hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array([1, 2])).hexdigest())"

        # TODO: numpy arrays of strings must be encoded to bytes first in python3
        python2 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array(['a', 'b'])).hexdigest())"
        python3 -c "import hashlib, numpy; print(hashlib.sha1(numpy.array([b'a', b'b'])).hexdigest())"

        python -c "import hashlib, numpy; print(hashlib.sha1(numpy.array(['a', 'b'], dtype=object)).hexdigest())"
        python -c "import hashlib, numpy; print(hashlib.sha1(numpy.array(['a', 'b'], dtype=object)).hexdigest())"
    """
    if util_type.HAVE_NUMPY and isinstance(data, np.ndarray):
        if data.dtype.kind == 'O':
            msg = '[ut] hashing ndarrays with dtype=object is unstable'
            warnings.warn(msg, RuntimeWarning)
            # but tobytes is ok, but differs between python 2 and 3 for objects
            data = data.dumps()
            # data = data.tobytes()
    if isinstance(data, tuple):
        # should instead do
        if False:
            hasher = hashlib.sha512()
            items = data
            for item in items:
                if isinstance(item, uuid.UUID):
                    hasher.update(item.bytes)
                else:
                    hasher.update(item)
            text = hasher.hexdigest()
            hashstr2 = convert_hexstr_to_bigbase(text, alphabet, bigbase=len(alphabet))
            # Truncate
            text = hashstr2[:hashlen]
            return text
        else:
            msg = '[ut] hashing tuples with repr is not a good idea. FIXME'
            # warnings.warn(msg, RuntimeWarning)
            data = repr(data)  # Hack?

    # convert unicode into raw bytes
    if isinstance(data, six.text_type):
        data = data.encode('utf-8')

    if isinstance(data, stringlike) and len(data) == 0:
        # Make a special hash for empty data
        text = (alphabet[0] * hashlen)
    else:
        # Get a 128 character hex string
        text = hashlib.sha512(data).hexdigest()
        # Shorten length of string (by increasing base)
        hashstr2 = convert_hexstr_to_bigbase(text, alphabet, bigbase=len(alphabet))
        # Truncate
        text = hashstr2[:hashlen]
    return text