def METHOD_NAME(filename):
    # type: (str) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]
    if not filename.endswith(".whl"):
        raise InvalidWheelFilename(
            "Invalid wheel filename (extension must be '.whl'): {0}".format(filename)
        )
    filename = filename[:-4]
    dashes = filename.count("-")
    if dashes not in (4, 5):
        raise InvalidWheelFilename(
            "Invalid wheel filename (wrong number of parts): {0}".format(filename)
        )
    parts = filename.split("-", dashes - 2)
    name_part = parts[0]
    # See PEP 427 for the rules on escaping the project name
    if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
        raise InvalidWheelFilename("Invalid project name: {0}".format(filename))
    name = canonicalize_name(name_part)
    version = Version(parts[1])
    if dashes == 5:
        build_part = parts[2]
        build_match = _build_tag_regex.match(build_part)
        if build_match is None:
            raise InvalidWheelFilename(
                "Invalid build number: {0} in '{1}'".format(build_part, filename)
            )
        build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
    else:
        build = ()
    tags = parse_tag(parts[-1])
    return (name, version, build, tags)