def index_str(text, sub, start=None, end=None):
    """
    Finds the lowest index of the substring ``sub`` within ``text`` in the range [``start``, ``end``].
    
    Optional arguments ``start`` and ``end`` are interpreted as in slice notation. However,
    the index returned is relative to the original string ``text`` and not the slice
    ``text[start:end]``.
    
    This function is like :func:`find_str`, except that it raises a ``ValueError`` when the
    substring is not found.
    
    :param text: The string to search
    :type text:  ``str``
    
    :param sub: The substring to search for
    :type sub:  ``str``
    
    :param start: The start of the search range
    :type start:  ``int``
    
    :param end: The end of the search range
    :type end:  ``int``
    
    :return: The lowest index of the substring ``sub`` within ``text`` in the range [``start``, ``end``].
    :rtype:  ``int``
    """
    assert isinstance(text, str), '%s is not a string' % text
    return text.index(sub, start, end)
