From 15d9408ef9d5e5550470df7d5d16f2532ddb83b7 Mon Sep 17 00:00:00 2001
From: Paul Moore
Date: Thu, 2 May 2024 21:08:26 +0100
Subject: [PATCH 1/2] Preload script wrappers at import time
---
distlib/scripts.py | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/distlib/scripts.py b/distlib/scripts.py
index 01f225c..378f83d 100644
--- a/distlib/scripts.py
+++ b/distlib/scripts.py
@@ -48,6 +48,24 @@
sys.exit(%(func)s())
'''
+# Pre-fetch the contents of all executable wrapper stubs.
+# This is to address https://github.com/pypa/pip/issues/12666.
+# When updating pip, we rename the old pip in place before installing the
+# new version. If we try to fetch a wrapper *after* that rename, the finder
+# machinery will be confused as the package is no longer available at the
+# location where it was imported from. So we load everything into memory in
+# advance.
+
+# Issue 31: don't hardcode an absolute package name, but
+# determine it relative to the current package
+distlib_package = __name__.rsplit('.', 1)[0]
+
+WRAPPERS = {
+ r.name: r.bytes
+ for r in finder(distlib_package).iterator("")
+ if r.name.endswith(".exe")
+}
+
def enquote_executable(executable):
if ' ' in executable:
@@ -386,14 +404,11 @@ def _get_launcher(self, kind):
bits = '32'
platform_suffix = '-arm' if get_platform() == 'win-arm64' else ''
name = '%s%s%s.exe' % (kind, bits, platform_suffix)
- # Issue 31: don't hardcode an absolute package name, but
- # determine it relative to the current package
- distlib_package = __name__.rsplit('.', 1)[0]
- resource = finder(distlib_package).find(name)
- if not resource:
- msg = ('Unable to find resource %s in package %s' % (name, distlib_package))
+ if name not in WRAPPERS:
+ msg = ('Unable to find resource %s in package %s' %
+ (name, distlib_package))
raise ValueError(msg)
- return resource.bytes
+ return WRAPPERS[name]
# Public API follows
From 40c8a807dc963a5ca9c3baa92bdc43b3bc74c341 Mon Sep 17 00:00:00 2001
From: Paul Moore
Date: Thu, 2 May 2024 21:35:40 +0100
Subject: [PATCH 2/2] Only preload on Windows, which is the only place the
wrappers are used
---
distlib/scripts.py | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/distlib/scripts.py b/distlib/scripts.py
index 378f83d..0cf6640 100644
--- a/distlib/scripts.py
+++ b/distlib/scripts.py
@@ -56,15 +56,16 @@
# location where it was imported from. So we load everything into memory in
# advance.
-# Issue 31: don't hardcode an absolute package name, but
-# determine it relative to the current package
-distlib_package = __name__.rsplit('.', 1)[0]
+if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'):
+ # Issue 31: don't hardcode an absolute package name, but
+ # determine it relative to the current package
+ DISTLIB_PACKAGE = __name__.rsplit('.', 1)[0]
-WRAPPERS = {
- r.name: r.bytes
- for r in finder(distlib_package).iterator("")
- if r.name.endswith(".exe")
-}
+ WRAPPERS = {
+ r.name: r.bytes
+ for r in finder(DISTLIB_PACKAGE).iterator("")
+ if r.name.endswith(".exe")
+ }
def enquote_executable(executable):
@@ -406,7 +407,7 @@ def _get_launcher(self, kind):
name = '%s%s%s.exe' % (kind, bits, platform_suffix)
if name not in WRAPPERS:
msg = ('Unable to find resource %s in package %s' %
- (name, distlib_package))
+ (name, DISTLIB_PACKAGE))
raise ValueError(msg)
return WRAPPERS[name]