Stand-in for :func:`~shutil.rmtree` with additional error-handling.

    This version of `rmtree` handles read-only paths, especially in the case of index
    files written by certain source control systems.

    :param str directory: The target directory to remove
    :param bool ignore_errors: Whether to ignore errors, defaults to False
    :param func onerror: An error handling function, defaults to :func:`handle_remove_readonly`

    .. note::

       Setting `ignore_errors=True` may cause this to silently fail to delete the path

def rmtree(directory, ignore_errors=False, onerror=None):
    # type: (str, bool, Optional[Callable]) -> None
    """
    Stand-in for :func:`~shutil.rmtree` with additional error-handling.

    This version of `rmtree` handles read-only paths, especially in the case of index
    files written by certain source control systems.

    :param str directory: The target directory to remove
    :param bool ignore_errors: Whether to ignore errors, defaults to False
    :param func onerror: An error handling function, defaults to :func:`handle_remove_readonly`

    .. note::

       Setting `ignore_errors=True` may cause this to silently fail to delete the path
    """

    directory = fs_encode(directory)
    if onerror is None:
        onerror = handle_remove_readonly
    try:
        shutil.rmtree(directory, ignore_errors=ignore_errors, onerror=onerror)
    except (IOError, OSError, FileNotFoundError, PermissionError) as exc:
        # Ignore removal failures where the file doesn't exist
        if exc.errno != errno.ENOENT:
            raise