diff --git a/python_appimage/appimage/relocate.py b/python_appimage/appimage/relocate.py index c7f8939..735e955 100644 --- a/python_appimage/appimage/relocate.py +++ b/python_appimage/appimage/relocate.py @@ -106,6 +106,32 @@ 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 + ''' + + 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 + else: + return + + with open(patch) as f: + patch = f.read() + + lines.insert(i, patch) + lines.insert(i + 1, '') + + source = os.linesep.join(lines) + with open(path, 'w') as f: + f.write(source) + + def relocate_python(python=None, appdir=None): '''Bundle a Python install inside an AppDir ''' @@ -258,8 +284,14 @@ def relocate_python(python=None, appdir=None): os.symlink('pip3', pip) + # Patch the site.py module + log('PATCH', '%s site.py', PYTHON_X_Y) + + 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) + log('HOOK', '%s site-packages', PYTHON_X_Y) sitepkgs = PYTHON_PKG + '/site-packages' make_tree(sitepkgs) diff --git a/python_appimage/data/apprun.sh b/python_appimage/data/apprun.sh index b732bcf..96ffdac 100755 --- a/python_appimage/data/apprun.sh +++ b/python_appimage/data/apprun.sh @@ -1,9 +1,16 @@ {{ shebang }} -# Export APPRUN if running from an extracted image -self="$(readlink -f -- $0)" -here="${self%/*}" -APPDIR="${APPDIR:-${here}}" +# If running from an extracted image, then export ARGV0 and APPDIR +if [ -z "${APPIMAGE}" ]; then + export ARGV0=$0 + + self="$(readlink -f -- $0)" + here="${self%/*}" + export APPDIR="${APPDIR:-${here}}" +fi + +# Resolve the calling command (preserving symbolic links). +export APPIMAGE_COMMAND="$(command -v -- $ARGV0)" {{ tcltk-env }} {{ cert-file }} diff --git a/python_appimage/data/entrypoint.sh b/python_appimage/data/entrypoint.sh index 05e8a48..e2fa09e 100755 --- a/python_appimage/data/entrypoint.sh +++ b/python_appimage/data/entrypoint.sh @@ -1,25 +1 @@ -for opt in "$@" -do - [ "${opt:0:1}" != "-" ] && break - if [[ "${opt}" =~ "I" ]] || [[ "${opt}" =~ "E" ]]; then - # Environment variables are disabled ($PYTHONHOME). Let's run in a safe - # mode from the raw Python binary inside the AppImage - "$APPDIR/opt/{{ python }}/bin/{{ python }}" "$@" - exit "$?" - fi -done - -# Get the executable name, i.e. the AppImage or the python binary if running from an -# extracted image -executable="${APPDIR}/opt/{{ python }}/bin/{{ python }}" -if [[ "${ARGV0}" =~ "/" ]]; then - executable="$(cd $(dirname ${ARGV0}) && pwd)/$(basename ${ARGV0})" -elif [[ "${ARGV0}" != "" ]]; then - executable=$(which "${ARGV0}") -fi - -# Wrap the call to Python in order to mimic a call from the source -# executable ($ARGV0), but potentially located outside of the Python -# install ($PYTHONHOME) -(PYTHONHOME="${APPDIR}/opt/{{ python }}" exec -a "${executable}" "$APPDIR/opt/{{ python }}/bin/{{ python }}" "$@") -exit "$?" +"$APPDIR/opt/{{ python }}/bin/{{ python }}" "$@" diff --git a/python_appimage/data/site-patch.py b/python_appimage/data/site-patch.py new file mode 100644 index 0000000..1e335f9 --- /dev/null +++ b/python_appimage/data/site-patch.py @@ -0,0 +1,16 @@ +# This is a site.py patch when calling Python from an AppImage. +def _initappimage(): + """Initialise executable name for running from an AppImage.""" + env = os.environ + try: + command = env["APPIMAGE_COMMAND"] + except KeyError: + return + + if command and ("APPDIR" in env): + command = os.path.abspath(command) + sys.executable = command + sys._base_executable = command + +_initappimage() +del _initappimage diff --git a/python_appimage/data/sitecustomize.py b/python_appimage/data/sitecustomize.py index 79cd773..a9c4702 100644 --- a/python_appimage/data/sitecustomize.py +++ b/python_appimage/data/sitecustomize.py @@ -1,34 +1,10 @@ -'''Hooks for isloating the AppImage Python and making it relocatable +'''Python AppImage hooks ''' import atexit import os import sys -def clean_path(): - '''Remove system locations from the packages search path - ''' - site_packages = '/usr/local/lib/python{:}.{:}/site-packages'.format( - *sys.version_info[:2]) - binaries_path = '/usr/local/bin' - env_path = os.getenv("PYTHONPATH") - if env_path is None: - env_path = [] - else: - env_path = [os.path.realpath(path) for path in env_path.split(':')] - - if ((os.path.dirname(sys.executable) != binaries_path) and - (site_packages not in env_path)): - # Remove the builtin site-packages from the path - try: - sys.path.remove(site_packages) - except ValueError: - pass - - -clean_path() - - _bin_at_start = os.listdir(sys.prefix + '/bin') '''Initial content of the bin/ directory ''' @@ -108,4 +84,8 @@ def patch_pip_install(): os.remove(path) -atexit.register(patch_pip_install) +if os.getenv('VIRTUAL_ENV') is None: + atexit.register(patch_pip_install) +else: + del _bin_at_start + del patch_pip_install diff --git a/python_appimage/utils/docker.py b/python_appimage/utils/docker.py index c9a0fe7..862357d 100644 --- a/python_appimage/utils/docker.py +++ b/python_appimage/utils/docker.py @@ -46,4 +46,6 @@ def docker_run(image, extra_cmds): p = subprocess.Popen(cmd, shell=True) p.communicate() if p.returncode != 0: + if p.returncode == 139: + sys.stderr.write("segmentation fault when running Docker (139)\n") sys.exit(p.returncode)