From 888c48b56886b03398646be1217508830427bd75 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 3 May 2024 07:49:46 +0100 Subject: [PATCH] Preload script wrappers at import time (#215) --- distlib/scripts.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/distlib/scripts.py b/distlib/scripts.py index 01f225c..0cf6640 100644 --- a/distlib/scripts.py +++ b/distlib/scripts.py @@ -48,6 +48,25 @@ 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. + +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") + } + def enquote_executable(executable): if ' ' in executable: @@ -386,14 +405,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