{"id":"028e80a25aff2c0e","repo":"pypa/pip","slug":"invalid-project-name-filename-r","errorCode":null,"errorMessage":"Invalid project name: {filename!r}","messagePattern":"Invalid project name: (.+?)","errorType":"validation","errorClass":"InvalidWheelFilename","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/utils.py","lineNumber":218,"sourceCode":"       The *validate_order* parameter.\n    \"\"\"\n    if not filename.endswith(\".whl\"):\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (extension must be '.whl'): {filename!r}\"\n        )\n\n    filename = filename[:-4]\n    dashes = filename.count(\"-\")\n    if dashes not in (4, 5):\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (wrong number of parts): {filename!r}\"\n        )\n\n    parts = filename.split(\"-\", dashes - 2)\n    name_part = parts[0]\n    # See PEP 427 for the rules on escaping the project name.\n    if \"__\" in name_part or re.match(r\"^[\\w\\d._]*$\", name_part, re.UNICODE) is None:\n        raise InvalidWheelFilename(f\"Invalid project name: {filename!r}\")\n    name = canonicalize_name(name_part)\n\n    try:\n        version = Version(parts[1])\n    except InvalidVersion as e:\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (invalid version): {filename!r}\"\n        ) from e\n\n    if dashes == 5:\n        build_part = parts[2]\n        build_match = _build_tag_regex.match(build_part)\n        if build_match is None:\n            raise InvalidWheelFilename(\n                f\"Invalid build number: {build_part} in {filename!r}\"\n            )\n        build = cast(\"BuildTag\", (int(build_match.group(1)), build_match.group(2)))\n    else:","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/utils.py#L200-L236","documentation":"Raised by `parse_wheel_filename` when the name segment of the wheel filename is itself invalid: it contains a literal `__` (double underscore, which is reserved for escaping runs of `_-` in project names) or does not match `^[\\w\\d._]*$` (alphanumerics, underscore, dot, hyphen). This guards PEP 427's name-escaping rules before canonicalization.","triggerScenarios":"A wheel whose name segment contains `__` (e.g. from a name with consecutive `-`/`_` not properly escaped), or characters outside `[A-Za-z0-9_.-]` such as spaces, parentheses, or plus signs. The check is `'__' in name_part or re.match(r\"^[\\w\\d._]*$\", name_part) is None`.","commonSituations":"A project name with unusual characters; a misescaped double dash; filenames produced by a non-compliant or outdated build tool; manual edits to the wheel filename.","solutions":["Rebuild the wheel with a current, PEP 427-compliant backend that escapes project names correctly.","Rename the name segment to use only `[A-Za-z0-9._-]` and no `__` runs.","Validate the upstream distribution name (use `canonicalize_name(name, validate=True)`).","If the artifact came from an external index, report/fallback and re-fetch a valid wheel."],"exampleFix":"// before\nparse_wheel_filename('foo__bar-1.0-py3-none-any.whl')  # '__' in name -> raises\n\n# after\nparse_wheel_filename('foo_bar-1.0-py3-none-any.whl')","handlingStrategy":"try-catch","validationCode":"import re\n\ndef has_valid_wheel_name_segment(filename: str) -> bool:\n    if not filename.endswith('.whl'):\n        return False\n    stem = filename[:-4]\n    name_part = stem.split('-', stem.count('-') - 2)[0]\n    return '__' not in name_part and bool(re.match(r'^[\\w\\d._]*$', name_part, re.UNICODE))","typeGuard":"import re\n\ndef is_valid_wheel_name_segment(filename: object) -> bool:\n    if not isinstance(filename, str) or not filename.endswith('.whl'):\n        return False\n    stem = filename[:-4]\n    name_part = stem.split('-', max(1, stem.count('-') - 2))[0]\n    return '__' not in name_part and bool(re.match(r'^[\\w\\d._]*$', name_part, re.UNICODE))","tryCatchPattern":"from pip._vendor.packaging.utils import parse_wheel_filename, InvalidWheelFilename\n\ntry:\n    parse_wheel_filename(filename)\nexcept InvalidWheelFilename as e:\n    if 'Invalid project name' in str(e):\n        # rename the name segment to alphanumerics + _ . - with no __\n        ...\n    raise","preventionTips":["Ensure distribution names contain only `[A-Za-z0-9._-]` and no double-underscore runs.","Let a compliant backend escape names; avoid manual renaming.","Pre-validate the upstream name with `canonicalize_name(name, validate=True)`."],"tags":["python","packaging","utils","wheel","pep427","filename","metadata"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}