From 045bc012c7fbe2270bcba8e8f1ba97413526e5be Mon Sep 17 00:00:00 2001
From: Damian Shaw <damian.peter.shaw@gmail.com>
Date: Mon, 29 Apr 2024 21:39:06 -0400
Subject: [PATCH] Consistently use get_requirement to cache Requirement
 construction

---
 src/pip/_internal/build_env.py                 | 4 ++--
 src/pip/_internal/metadata/importlib/_dists.py | 3 ++-
 src/pip/_internal/pyproject.py                 | 5 +++--
 src/pip/_internal/req/constructors.py          | 6 +++---
 src/pip/_internal/req/req_install.py           | 5 +++--
 src/pip/_internal/utils/packaging.py           | 2 +-
 6 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/src/pip/_internal/build_env.py b/src/pip/_internal/build_env.py
index 838de86474f..b79db220777 100644
--- a/src/pip/_internal/build_env.py
+++ b/src/pip/_internal/build_env.py
@@ -12,7 +12,6 @@
 from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type, Union
 
 from pip._vendor.certifi import where
-from pip._vendor.packaging.requirements import Requirement
 from pip._vendor.packaging.version import Version
 
 from pip import __file__ as pip_location
@@ -20,6 +19,7 @@
 from pip._internal.locations import get_platlib, get_purelib, get_scheme
 from pip._internal.metadata import get_default_environment, get_environment
 from pip._internal.utils.logging import VERBOSE
+from pip._internal.utils.packaging import get_requirement
 from pip._internal.utils.subprocess import call_subprocess
 from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
 
@@ -184,7 +184,7 @@ def check_requirements(
                 else get_default_environment()
             )
             for req_str in reqs:
-                req = Requirement(req_str)
+                req = get_requirement(req_str)
                 # We're explicitly evaluating with an empty extra value, since build
                 # environments are not provided any mechanism to select specific extras.
                 if req.marker is not None and not req.marker.evaluate({"extra": ""}):
diff --git a/src/pip/_internal/metadata/importlib/_dists.py b/src/pip/_internal/metadata/importlib/_dists.py
index f65ccb1e706..9d3bedd8a10 100644
--- a/src/pip/_internal/metadata/importlib/_dists.py
+++ b/src/pip/_internal/metadata/importlib/_dists.py
@@ -27,6 +27,7 @@
     Wheel,
 )
 from pip._internal.utils.misc import normalize_path
+from pip._internal.utils.packaging import get_requirement
 from pip._internal.utils.temp_dir import TempDirectory
 from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file
 
@@ -219,7 +220,7 @@ def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requiremen
         for req_string in self.metadata.get_all("Requires-Dist", []):
             # strip() because email.message.Message.get_all() may return a leading \n
             # in case a long header was wrapped.
-            req = Requirement(req_string.strip())
+            req = get_requirement(req_string.strip())
             if not req.marker:
                 yield req
             elif not extras and req.marker.evaluate({"extra": ""}):
diff --git a/src/pip/_internal/pyproject.py b/src/pip/_internal/pyproject.py
index 8de36b873ed..9fef1d771e4 100644
--- a/src/pip/_internal/pyproject.py
+++ b/src/pip/_internal/pyproject.py
@@ -4,13 +4,14 @@
 from typing import Any, List, Optional
 
 from pip._vendor import tomli
-from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
+from pip._vendor.packaging.requirements import InvalidRequirement
 
 from pip._internal.exceptions import (
     InstallationError,
     InvalidPyProjectBuildRequires,
     MissingPyProjectBuildRequires,
 )
+from pip._internal.utils.packaging import get_requirement
 
 
 def _is_list_of_str(obj: Any) -> bool:
@@ -151,7 +152,7 @@ def load_pyproject_toml(
     # Each requirement must be valid as per PEP 508
     for requirement in requires:
         try:
-            Requirement(requirement)
+            get_requirement(requirement)
         except InvalidRequirement as error:
             raise InvalidPyProjectBuildRequires(
                 package=req_name,
diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py
index b8e170f2a70..d73236e05c6 100644
--- a/src/pip/_internal/req/constructors.py
+++ b/src/pip/_internal/req/constructors.py
@@ -81,7 +81,7 @@ def _set_requirement_extras(req: Requirement, new_extras: Set[str]) -> Requireme
         pre is not None and post is not None
     ), f"regex group selection for requirement {req} failed, this should never happen"
     extras: str = "[%s]" % ",".join(sorted(new_extras)) if new_extras else ""
-    return Requirement(f"{pre}{extras}{post}")
+    return get_requirement(f"{pre}{extras}{post}")
 
 
 def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
@@ -163,7 +163,7 @@ def check_first_requirement_in_file(filename: str) -> None:
             # If there is a line continuation, drop it, and append the next line.
             if line.endswith("\\"):
                 line = line[:-2].strip() + next(lines, "")
-            Requirement(line)
+            get_requirement(line)
             return
 
 
@@ -205,7 +205,7 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
 
     if name is not None:
         try:
-            req: Optional[Requirement] = Requirement(name)
+            req: Optional[Requirement] = get_requirement(name)
         except InvalidRequirement as exc:
             raise InstallationError(f"Invalid requirement: {name!r}: {exc}")
     else:
diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py
index 213278588d2..e4bca4c03d8 100644
--- a/src/pip/_internal/req/req_install.py
+++ b/src/pip/_internal/req/req_install.py
@@ -52,6 +52,7 @@
     redact_auth_from_requirement,
     redact_auth_from_url,
 )
+from pip._internal.utils.packaging import get_requirement
 from pip._internal.utils.subprocess import runner_with_spinner_message
 from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
 from pip._internal.utils.unpacking import unpack_file
@@ -395,7 +396,7 @@ def _set_requirement(self) -> None:
         else:
             op = "==="
 
-        self.req = Requirement(
+        self.req = get_requirement(
             "".join(
                 [
                     self.metadata["Name"],
@@ -421,7 +422,7 @@ def warn_on_mismatching_name(self) -> None:
             metadata_name,
             self.name,
         )
-        self.req = Requirement(metadata_name)
+        self.req = get_requirement(metadata_name)
 
     def check_if_exists(self, use_user_site: bool) -> None:
         """Find an installed distribution that satisfies or conflicts
diff --git a/src/pip/_internal/utils/packaging.py b/src/pip/_internal/utils/packaging.py
index b9f6af4d174..4b8fa0fe397 100644
--- a/src/pip/_internal/utils/packaging.py
+++ b/src/pip/_internal/utils/packaging.py
@@ -34,7 +34,7 @@ def check_requires_python(
     return python_version in requires_python_specifier
 
 
-@functools.lru_cache(maxsize=512)
+@functools.lru_cache(maxsize=2048)
 def get_requirement(req_string: str) -> Requirement:
     """Construct a packaging.Requirement object with caching"""
     # Parsing requirement strings is expensive, and is also expected to happen