11diff --git a/pip/__init__.py b/pip/__init__.py
2- index 3d4b45a..3aa11e8 100644
2+ index 42f6c45..26c9066 100644
33--- a/pip/__init__.py
44+++ b/pip/__init__.py
55@@ -11,3 +11,6 @@ def main(args: Optional[List[str]] = None) -> int:
@@ -10,10 +10,10 @@ index 3d4b45a..3aa11e8 100644
1010+
1111+ __GRAALPY_PATCHED = True
1212diff --git a/pip/_internal/cli/cmdoptions.py b/pip/_internal/cli/cmdoptions.py
13- index 47ed927..1d182de 100644
13+ index 1f80409..df7ebfc 100644
1414--- a/pip/_internal/cli/cmdoptions.py
1515+++ b/pip/_internal/cli/cmdoptions.py
16- @@ -891 ,7 +891 ,7 @@ disable_pip_version_check: Callable[..., Option] = partial(
16+ @@ -879 ,7 +879 ,7 @@ disable_pip_version_check: Callable[..., Option] = partial(
1717 "--disable-pip-version-check",
1818 dest="disable_pip_version_check",
1919 action="store_true",
@@ -23,17 +23,17 @@ index 47ed927..1d182de 100644
2323 "of pip is available for download. Implied with --no-index.",
2424 )
2525diff --git a/pip/_internal/index/package_finder.py b/pip/_internal/index/package_finder.py
26- index 9bf247f..3e8f187 100644
26+ index b6f8d57..6c37e0b 100644
2727--- a/pip/_internal/index/package_finder.py
2828+++ b/pip/_internal/index/package_finder.py
29- @@ -38 ,6 +38 ,7 @@ from pip._internal.utils.logging import indent_log
29+ @@ -35 ,6 +35 ,7 @@ from pip._internal.utils.logging import indent_log
3030 from pip._internal.utils.misc import build_netloc
3131 from pip._internal.utils.packaging import check_requires_python
3232 from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS
3333+ from pip._internal.utils.graalpy import apply_graalpy_sort_order, get_graalpy_candidates
3434
35- __all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"]
36-
35+ if TYPE_CHECKING:
36+ from pip._vendor.typing_extensions import TypeGuard
3737@@ -487,6 +488,7 @@ class CandidateEvaluator:
3838
3939 return sorted(filtered_applicable_candidates, key=self._sort_key)
@@ -42,7 +42,7 @@ index 9bf247f..3e8f187 100644
4242 def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey:
4343 """
4444 Function to pass as the `key` argument to a call to sorted() to sort
45- @@ -851 ,8 +853 ,11 @@ class PackageFinder:
45+ @@ -852 ,8 +854 ,11 @@ class PackageFinder:
4646
4747 logger.debug("Local files found: %s", ", ".join(paths))
4848
@@ -77,10 +77,10 @@ index 79b82a5..1eddfa3 100644
7777 ext: Optional[str] = splitext(filename)[1]
7878 if not ext:
7979diff --git a/pip/_internal/operations/install/wheel.py b/pip/_internal/operations/install/wheel.py
80- index 1af8978..15fee7f 100644
80+ index c799413..bf52f00 100644
8181--- a/pip/_internal/operations/install/wheel.py
8282+++ b/pip/_internal/operations/install/wheel.py
83- @@ -587 ,6 +587 ,9 @@ def _install_wheel(
83+ @@ -589 ,6 +589 ,9 @@ def _install_wheel(
8484 file.save()
8585 record_installed(file.src_record_path, file.dest_path, file.changed)
8686
@@ -92,14 +92,15 @@ index 1af8978..15fee7f 100644
9292 # file in .data maps to same location as file in wheel root).
9393diff --git a/pip/_internal/utils/graalpy.py b/pip/_internal/utils/graalpy.py
9494new file mode 100644
95- index 0000000..b8a506c
95+ index 0000000..f9b8dcc
9696--- /dev/null
9797+++ b/pip/_internal/utils/graalpy.py
98- @@ -0,0 +1,195 @@
98+ @@ -0,0 +1,214 @@
9999+ # ATTENTION: GraalPy uses existence of this module to verify that it is
100100+ # running a patched pip in pip_hook.py
101101+ import os
102102+ import re
103+ + import zipfile
103104+ from pathlib import Path
104105+ from urllib.parse import urlparse
105106+
@@ -113,11 +114,8 @@ index 0000000..b8a506c
113114+ if hasattr(__graalpython__, "tdebug"):
114115+ PATCHES_BASE_DIRS += os.environ.get('PIPLOADER_PATCHES_BASE_DIRS', "").split(",")
115116+
116- + BUNDLED_WHEELS_PATH = None
117- +
118- +
119- + def is_bundled_wheel(location, package_name):
120- + return os.path.exists(os.path.join(location, package_name, '.graalpy_bundled'))
117+ + DISABLE_PATCHING = os.environ.get('PIP_GRAALPY_DISABLE_PATCHING', '').lower() in ('true', '1')
118+ + DISABLE_VERSION_SELECTION = os.environ.get('PIP_GRAALPY_DISABLE_VERSION_SELECTION', '').lower() in ('true', '1')
121119+
122120+
123121+ def normalize_name(name):
@@ -188,19 +186,14 @@ index 0000000..b8a506c
188186+ return __PATCH_REPOSITORY
189187+
190188+
191- + __already_patched = set()
192- +
193- +
194189+ def apply_graalpy_patches(filename, location):
195190+ """
196191+ Applies any GraalPy patches to package extracted from 'filename' into 'location'.
197192+ Note that 'location' must be the parent directory of the package directory itself.
198193+ For example: /path/to/site-package and not /path/to/site-packages/mypackage.
199194+ """
200- + import autopatch_capi
201- + import subprocess
202- +
203- + autopatch_capi.auto_patch_tree(location)
195+ + if DISABLE_PATCHING:
196+ + return
204197+
205198+ # we expect filename to be something like "pytest-5.4.2-py3-none-any.whl"
206199+ archive_name = os.path.basename(filename)
@@ -216,13 +209,14 @@ index 0000000..b8a506c
216209+ suffix = name_ver_match.group('suffix')
217210+ is_wheel = suffix == "whl"
218211+
219- + # Avoid applying patches to bundled wheels, they are already patched
220- + if is_wheel and is_bundled_wheel(location, name):
212+ + if is_wheel and is_wheel_marked(filename):
213+ + # We already processed it when building from source
221214+ return
222215+
223- + # When we patch a sdist, pip may call us again to process the wheel produced from it
224- + if (name, version) in __already_patched:
225- + return
216+ + import autopatch_capi
217+ + import subprocess
218+ +
219+ + autopatch_capi.auto_patch_tree(location)
226220+
227221+ print(f"Looking for GraalPy patches for {name}")
228222+ repository = get_patch_repository()
@@ -251,7 +245,6 @@ index 0000000..b8a506c
251245+ "WARNING: GraalPy needs the 'patch' utility to apply compatibility patches. Please install it using your system's package manager.")
252246+ except subprocess.CalledProcessError:
253247+ print(f"Applying GraalPy patch failed for {name}. The package may still work.")
254- + __already_patched.add((name, version))
255248+ elif version_specs := repository.get_suggested_version_specs(name):
256249+ print("We have patches to make this package work on GraalVM for some version(s).")
257250+ print("If installing or running fails, consider using one of the versions that we have patches for:")
@@ -260,6 +253,9 @@ index 0000000..b8a506c
260253+
261254+
262255+ def apply_graalpy_sort_order(sort_key_func):
256+ + if DISABLE_VERSION_SELECTION:
257+ + return sort_key_func
258+ +
263259+ def wrapper(self, candidate):
264260+ default_sort_key = sort_key_func(self, candidate)
265261+ priority = get_patch_repository().get_priority_for_version(candidate.name, str(candidate.version))
@@ -291,6 +287,29 @@ index 0000000..b8a506c
291287+ link = LinkWithFilename(url, f'{name}-{version}.{suffix}')
292288+ candidates.append(InstallationCandidate(name=name, version=version, link=link))
293289+ return candidates
290+ +
291+ +
292+ + MARKER_NAME = 'GRAALPY_MARKER'
293+ +
294+ +
295+ + def mark_wheel(path):
296+ + if DISABLE_PATCHING:
297+ + return
298+ + with zipfile.ZipFile(path, 'a') as z:
299+ + dist_info = None
300+ + for name in z.namelist():
301+ + if m := re.match(r'([^/]+.dist-info)/', name):
302+ + dist_info = m.group(1)
303+ + break
304+ + assert dist_info, "Cannot find .dist_info in built wheel"
305+ + marker = f'{dist_info}/{MARKER_NAME}'
306+ + with z.open(marker, 'w'):
307+ + pass
308+ +
309+ +
310+ + def is_wheel_marked(path):
311+ + with zipfile.ZipFile(path) as z:
312+ + return any(re.match(rf'[^/]+.dist-info/{MARKER_NAME}$', f) for f in z.namelist())
294313diff --git a/pip/_internal/utils/unpacking.py b/pip/_internal/utils/unpacking.py
295314index 78b5c13..18a184c 100644
296315--- a/pip/_internal/utils/unpacking.py
@@ -301,6 +320,27 @@ index 78b5c13..18a184c 100644
301320 raise InstallationError(f"Cannot determine archive format of {location}")
302321+ from pip._internal.utils.graalpy import apply_graalpy_patches
303322+ apply_graalpy_patches(filename, location)
323+ diff --git a/pip/_internal/wheel_builder.py b/pip/_internal/wheel_builder.py
324+ index 15b30af..f28355d 100644
325+ --- a/pip/_internal/wheel_builder.py
326+ +++ b/pip/_internal/wheel_builder.py
327+ @@ -7,6 +7,7 @@ import re
328+ import shutil
329+ from typing import Callable, Iterable, List, Optional, Tuple
330+
331+ + from pip._internal.utils import graalpy
332+ from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version
333+ from pip._vendor.packaging.version import InvalidVersion, Version
334+
335+ @@ -275,6 +276,8 @@ def _build_one_inside_env(
336+ )
337+
338+ if wheel_path is not None:
339+ + # GraalPy change: watermark wheels that we built so that we don't try to patch them when installing
340+ + graalpy.mark_wheel(wheel_path)
341+ wheel_name = os.path.basename(wheel_path)
342+ dest_path = os.path.join(output_dir, wheel_name)
343+ try:
304344diff --git a/pip/_vendor/packaging/tags.py b/pip/_vendor/packaging/tags.py
305345index 9a3d25a..e0e7b31 100644
306346--- a/pip/_vendor/packaging/tags.py
0 commit comments