def exc_isinstance(exc_info, expected_exception, raise_not_implemented=False):
    """
    Simple helper function as an alternative to calling
    `~.pytest.ExceptionInfo.errisinstance` which will take into account all
    the "causing" exceptions in an exception chain.

    Parameters
    ----------
    exc_info : `pytest.ExceptionInfo` or `Exception`
        The exception info as returned by `pytest.raises`.
    expected_exception : `type`
        The expected exception class
    raise_not_implemented : bool, optional
        Whether to re-raise a `NotImplementedError` – necessary for tests that
        should be skipped with ``@skip_if_not_implemented``. Defaults to
        ``False``.

    Returns
    -------
    correct_exception : bool
        Whether the exception itself or one of the causing exceptions is of the
        expected type.
    """
    if exc_info is None:
        return False
    if hasattr(exc_info, 'value'):
        exc_info = exc_info.value

    if isinstance(exc_info, expected_exception):
        return True
    elif raise_not_implemented and isinstance(exc_info, NotImplementedError):
        raise exc_info

    return exc_isinstance(exc_info.__cause__, expected_exception,
                          raise_not_implemented=raise_not_implemented)