diff --git a/python_appimage/appimage/relocate.py b/python_appimage/appimage/relocate.py index 735e955..8510e6e 100644 --- a/python_appimage/appimage/relocate.py +++ b/python_appimage/appimage/relocate.py @@ -106,20 +106,36 @@ def patch_binary(path, libdir, recursive=True): patch_binary(target, libdir, recursive=True) -def patch_site(path, patch): - '''Patch the site.py module for running Python from an AppImage +def set_executable_patch(version, pkgpath, patch): + '''Set a runtime patch for sys.executable name ''' + # This patch needs to be executed before site.main() is called. A natural + # option is to apply it directy to the site module. But, starting with + # Python 3.11, the site module is frozen within Python executable. Then, + # doing so would require to recompile Python. Thus, starting with 3.11 we + # instead apply the patch to the encodings package. Indeed, the latter is + # loaded before the site module, and it is not frozen (as for now). + major, minor = [int(v) for v in version.split('.')] + if (major >= 3) and (minor >= 11): + path = os.path.join(pkgpath, 'encodings', '__init__.py') + else: + path = os.path.join(pkgpath, 'site.py') + with open(path) as f: source = f.read() if '_initappimage' in source: return lines = source.split(os.linesep) - for i, line in enumerate(lines): - if line.startswith('def main('): break + + if path.endswith('site.py'): + # Insert the patch before the main function + for i, line in enumerate(lines): + if line.startswith('def main('): break else: - return + # Append the patch at end of file + i = len(lines) with open(patch) as f: patch = f.read() @@ -284,14 +300,12 @@ def relocate_python(python=None, appdir=None): os.symlink('pip3', pip) - # Patch the site.py module - log('PATCH', '%s site.py', PYTHON_X_Y) + # Add a runtime patch for sys.executable, before site.main() execution + log('PATCH', '%s sys.executable', PYTHON_X_Y) + set_executable_patch(VERSION, PYTHON_PKG, PREFIX + '/data/_initappimage.py') - sitepath = PYTHON_PKG + '/site.py' - patch_site(sitepath, PREFIX + '/data/site-patch.py') - - # Set a hook in Python for cleaning the path detection - log('HOOK', '%s site-packages', PYTHON_X_Y) + # Set a hook for cleaning sys.path, after site.main() execution + log('HOOK', '%s sys.path', PYTHON_X_Y) sitepkgs = PYTHON_PKG + '/site-packages' make_tree(sitepkgs) diff --git a/python_appimage/data/site-patch.py b/python_appimage/data/_initappimage.py similarity index 64% rename from python_appimage/data/site-patch.py rename to python_appimage/data/_initappimage.py index 1e335f9..ec3ea52 100644 --- a/python_appimage/data/site-patch.py +++ b/python_appimage/data/_initappimage.py @@ -1,6 +1,9 @@ -# This is a site.py patch when calling Python from an AppImage. +# This is a patch to getpath. It must execute before site.main() is called def _initappimage(): - """Initialise executable name for running from an AppImage.""" + """Initialise executable name when running from an AppImage.""" + import os + import sys + env = os.environ try: command = env["APPIMAGE_COMMAND"]