models/link: reuse pyparsing + requirements combinators for egg fragment
This should now be consistent with existing tests (without establishing
that those tests are actually well-specified).
@@ -18,6 +18,10 @@ from typing import (
Union,
)
+from pip._vendor.packaging.requirements import EXTRAS, NAME
+from pip._vendor.pyparsing import Optional as Maybe
+from pip._vendor.pyparsing import ParseException, stringEnd, stringStart
+
from pip._internal.exceptions import InvalidEggFragment
from pip._internal.utils.filetypes import WHEEL_EXTENSION
from pip._internal.utils.hashes import Hashes
@@ -359,10 +363,7 @@ class Link(KeyBasedCompareMixin):
_egg_fragment_re = re.compile(r"[#&]egg=([^&]*)")
- # Per PEP 508.
- _project_name_re = re.compile(
- r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE
- )
+ _fragment_parser = stringStart + NAME + Maybe(EXTRAS) + stringEnd
@property
def egg_fragment(self) -> Optional[str]:
@@ -370,11 +371,12 @@ class Link(KeyBasedCompareMixin):
if not match:
return None
- # The egg fragment must look like a project name, and only
- # a project name. In particular, it can't contain version constraints
- # or anything else like that.
+ # An egg fragment looks like a PEP 508 project name, along with
+ # an optional extras specifier. Anything else is invalid.
project_name = match.group(1)
- if not self._project_name_re.match(project_name):
+ try:
+ self._fragment_parser.parseString(project_name)
+ except ParseException:
raise InvalidEggFragment(project_name)
return project_name
