Relocate python wrapper

This commit is contained in:
Valentin Niess
2022-06-12 18:37:52 +02:00
parent 0097c4d942
commit 61fe3cedf9
7 changed files with 62 additions and 40 deletions

View File

@@ -170,9 +170,9 @@ example, `$APPDIR` points to the AppImage mount point at runtime.
Python specific environment variables, the like `PYTHONPATH`. Depending on
your use case, this can be problematic.
The runtime isolation level can be changed by adding the `-s` and `-E`
The runtime isolation level can be changed by adding the `-E`, `-s` or `-I`
options, when invoking the runtime. For example,
`{{ python-executable }} -sE` starts a fully isolated Python instance.
`{{ python-executable }} -I` starts a fully isolated Python instance.
{% endraw %}
@@ -186,8 +186,8 @@ might rather refer to the initial AppImage [Packaging
Guide][APPIMAGE_PACKAGING], and use alternative tools like
[linuxdeploy][LINUXDEPLOY].
Yet, `python-appimage` can still be of use in more complex cases by extracting
its AppImages to an AppDir, as discussed in the [Advanced
However, `python-appimage` can still be of use in more complex cases by
extracting its AppImages to an AppDir, as discussed in the [Advanced
installation](index.md#advanced-installation) section. The extracted AppImages
contain a relocatable Python runtime, that can be used as a starting base for
building more complex AppImages.

View File

@@ -195,14 +195,16 @@ freely moved around.
Executable scripts are installed under `AppDir/opt/pythonX.Y/bin` where _X_
and _Y_ in _pythonX.Y_ stand for the major and minor version numbers. Site
packages are located under
`AppDir/opt/pythonX.Y/lib/pythonX.Y/site-packages`.
`AppDir/opt/pythonX.Y/lib/pythonX.Y/site-packages`. For convenience, `pip`
installed applications are also mirrored under `AppDir/usr/bin`, using
symbolic links.
!!! Tip
As for Python AppImages, by default the extracted runtime is [not isolated
from the user environment](#isolating-from-the-user-environment). This
behaviour can be changed by editing the `AppDir/AppRun` wrapper script, and
by adding the `-s`, `-E` or `-I` option at the very bottom, where Python is
invoked.
behaviour can be changed by editing the `AppDir/usr/bin/pythonX.Y` wrapper
script, and by adding the `-s`, `-E` or `-I` option at the very bottom,
where Python is invoked.
{{ begin(".capsule") }}

View File

@@ -208,13 +208,10 @@ def relocate_python(python=None, appdir=None):
target = PYTHON_BIN + '/' + PYTHON_X_Y
copy_file(source, target, update=True)
relpath = os.path.relpath(target, APPDIR_BIN)
make_tree(APPDIR_BIN)
os.symlink(relpath, APPDIR_BIN + '/' + PYTHON_X_Y)
copy_tree(HOST_PKG, PYTHON_PKG)
copy_tree(HOST_INC, PYTHON_INC)
make_tree(APPDIR_BIN)
pip_source = HOST_BIN + '/' + PIP_X_Y
if not os.path.exists(pip_source):
pip_source = HOST_BIN + '/' + PIP_X
@@ -228,7 +225,8 @@ def relocate_python(python=None, appdir=None):
f.write('#! /bin/sh\n')
f.write(' '.join((
'"exec"',
'"$(dirname $(readlink -f ${0}))/' + PYTHON_X_Y + '"',
'"$(dirname $(readlink -f ${0}))/../../../usr/bin/' +
PYTHON_X_Y + '"',
'"$0"',
'"$@"\n'
)))
@@ -361,18 +359,25 @@ def relocate_python(python=None, appdir=None):
log('INSTALL', basename)
# Bundle the entry point
apprun = APPDIR + '/AppRun'
if not os.path.exists(apprun):
log('INSTALL', 'AppRun')
# Bundle the python wrapper
wrapper = APPDIR_BIN + '/' + PYTHON_X_Y
if not os.path.exists(wrapper):
log('INSTALL', '%s wrapper', PYTHON_X_Y)
entrypoint_path = PREFIX + '/data/entrypoint.sh'
entrypoint = load_template(entrypoint_path, python=PYTHON_X_Y)
dictionary = {'entrypoint': entrypoint,
'shebang': '#! /bin/bash',
'tcltk-env': tcltk_env_string(PYTHON_PKG),
'cert-file': cert_file_env_string(cert_file)}
_copy_template('apprun.sh', apprun, **dictionary)
_copy_template('python-wrapper.sh', wrapper, **dictionary)
# Bundle the entry point
apprun = APPDIR + '/AppRun'
if not os.path.exists(apprun):
log('INSTALL', 'AppRun')
relpath = os.path.relpath(wrapper, APPDIR)
os.symlink(relpath, APPDIR + '/AppRun')
# Bundle the desktop file
desktop_name = 'python{:}.desktop'.format(FULLVERSION)

View File

@@ -7,7 +7,7 @@ import shutil
import stat
import struct
from ...appimage import build_appimage, cert_file_env_string, tcltk_env_string
from ...appimage import build_appimage
from ...utils.compat import decode
from ...utils.deps import PREFIX
from ...utils.fs import copy_file, make_tree, remove_file, remove_tree
@@ -280,13 +280,10 @@ def execute(appdir, name=None, python_version=None, linux_tag=None,
entrypoint = load_template(entrypoint_path, **dictionary)
python_pkg = 'AppDir/opt/python{0:}/lib/python{0:}'.format(
python_version)
cert_file = '/opt/_internal/certs.pem'
if not os.path.exists('AppDir' + cert_file):
cert_file = None
dictionary = {'entrypoint': entrypoint,
'shebang': shebang,
'tcltk-env': tcltk_env_string(python_pkg),
'cert-file': cert_file_env_string(cert_file)}
'shebang': shebang}
if os.path.exists('AppDir/AppRun'):
os.remove('AppDir/AppRun')
copy_template(PREFIX + '/data/apprun.sh', 'AppDir/AppRun',
**dictionary)

View File

@@ -1,18 +1,10 @@
{{ shebang }}
# If running from an extracted image, then export ARGV0 and APPDIR
# If running from an extracted image, then export APPDIR
if [ -z "${APPIMAGE}" ]; then
export ARGV0=$0
self="$(readlink -f -- $0)"
here="${self%/*}"
export APPDIR="${APPDIR:-${here}}"
export APPDIR="${self%/*}"
fi
# Resolve the calling command (preserving symbolic links).
export APPIMAGE_COMMAND="$(command -v -- $ARGV0)"
{{ tcltk-env }}
{{ cert-file }}
# Call the entry point
# Call the application entry point
{{ entrypoint }}

View File

@@ -0,0 +1,19 @@
{{ shebang }}
# If running from an extracted image, then export ARGV0 and APPDIR
if [ -z "${APPIMAGE}" ]; then
export ARGV0=$0
self="$(readlink -f -- $0)"
here="${self%/*}"
tmp="${here%/*}"
export APPDIR="${tmp%/*}"
fi
# Resolve the calling command (preserving symbolic links).
export APPIMAGE_COMMAND="$(command -v -- $ARGV0)"
{{ tcltk-env }}
{{ cert-file }}
# Call Python
{{ entrypoint }}

View File

@@ -16,6 +16,11 @@ def patch_pip_install():
if not 'pip' in sys.modules:
return
appdir = os.getenv('APPDIR')
python_x_y = 'python{:}.{:}'.format(*sys.version_info[:2])
if sys.prefix != '{:}/opt/{:}'.format(appdir, python_x_y):
return
args = sys.argv[1:]
if 'install' in args:
for exe in os.listdir(sys.prefix + '/bin'):
@@ -36,13 +41,15 @@ def patch_pip_install():
continue
shebang, body = content.split(os.linesep, 1)
shebang = shebang.split()
python_x_y = os.path.basename(shebang.pop(0))
if not python_x_y.startswith('python'):
shebang = shebang.strip().split()
executable = shebang.pop(0)
if executable != sys.executable:
head, altbody = body.split(os.linesep, 1)
if head.startswith("'''exec' /"): # Patch for alt shebang
body = altbody.split(os.linesep, 1)[1]
python_x_y = os.path.basename(head.split()[1])
executable = head.split()[1]
if executable != sys.executable:
continue
else:
continue