def METHOD_NAME(src_dir, src_filter=None, src_exts=None, followlinks=True):
    def _add_candidate(items, item, src_dir):
        if not src_exts or path_endswith_ext(item, src_exts):
            items.add(os.path.relpath(item, src_dir))
    def _find_candidates(pattern):
        candidates = set()
        for item in glob.glob(
            os.path.join(glob.escape(src_dir), pattern), recursive=True
        ):
            if not os.path.isdir(item):
                _add_candidate(candidates, item, src_dir)
                continue
            for root, dirs, files in os.walk(item, followlinks=followlinks):
                for d in dirs if not followlinks else []:
                    if os.path.islink(os.path.join(root, d)):
                        _add_candidate(candidates, os.path.join(root, d), src_dir)
                for f in files:
                    _add_candidate(candidates, os.path.join(root, f), src_dir)
        return candidates
    src_filter = src_filter or ""
    if isinstance(src_filter, (list, tuple)):
        src_filter = " ".join(src_filter)
    result = set()
    # correct fs directory separator
    src_filter = src_filter.replace("/", os.sep).replace("\\", os.sep)
    for action, pattern in re.findall(r"(\+|\-)<([^>]+)>", src_filter):
        candidates = _find_candidates(pattern)
        if action == "+":
            result |= candidates
        else:
            result -= candidates
    return sorted(list(result))