This is a companion project to Wheel Axle/bdist_axle
- Python wheels do not support symlinks.
- PIP installation procedure is not locally extensible and does not allow adding post-install hooks.
WARNING: THIS IS EXPERIMENTAL BETA SOFTWARE. THERE ARE NO WARRANTIES OF ANY KIND. USE AT YOUR OWN RISK. ADDITIONAL INCLUDED DISCLAIMERS ALSO APPLY.
Wheel Axle Runtime library utilizes a little-known trick used in site.py
's .pth
files that allows executing
arbitrary code while the site packages are being added. Thus, specially-crafted wheels can silently execute installed
code on Python interpreter startup, facilitating the "post-install hook" functionality.
The core functionality relies on the following Python behaviors:
site.py
processes .pth filessite.py
executes .pth import lines.pth
file's line is executed with a local variablefullname
denoting the.pth
file path
These invariants have not changed for 18 years.
Once the distribution-specific .pth
is executed by the Python interpreter, the Wheel Axle Runtime behaves as follows:
- The library checks whether a file
.dist-info/axle.done
exists. If it does it is the indication that the post-install hook has executed successfully and nothing more is to be done, terminating all further processing. - A process-wide inter-thread lock is acquired.
- An OS-wide inter-process file lock is acquired on a file
.dist-info/axle.lck
. - Once the locks are acquired the
.dist-info/axle.done
existence is rechecked (double-checked locking optimization). - Now that in-process and inter-process race conditions are excluded the post-install work can begin.
- Registered
installers
are run in sequence. Installers should be idempotent. The following installers are currently implemented:- LibPython installer checks for the presence of
.dist-info/require-libpython
.- The installer determines the location of the installation: venv, user or other.
- The list of all libpython library files is located from the
sys.base_exec_prefix
. - If the installation is either venv or user and the link to the libpython library doesn't exist the symlink is created.
- Symlinks installer processes
.dist-info/symlinks.txt
, if any.- Based on the location of the
.pth
file being executed the current installationschema
and its paths are determined. Currently, installation into a virtual environment or user location is supported and tested. - For each symlink the target path is resolved and
realpath
is used to determine the final target path. - If the symlink path and symlink target path are within one of the permitted schema locations the symlink is created. Otherwise, an exception is raised and the processing is aborted.
- After all symlinks are created, the
.dist-info/RECORD
file is updated to reflect the created symlinks.
- Based on the location of the
- Axle installer finalizes the installation. This installer is always executed last.
- The
.dist-info/RECORD
is updated with.dist-info/axle.done
file record. .dist-info/axle.done
is created.<distribution name and version>.pth
is then removed. If the file cannot be removed it is left in place. This can happen on Windows, since the.pth
file in question is likely opened for exclusive reading on Windows.
- The
- LibPython installer checks for the presence of
- Any failure anywhere in the above process will result in an abort, an error message, and a retry the next time
the
.pth
will be activated.
There are several security requirements and implications of having post-install hooks implemented this way.
- The installation requires write permissions to the distribution. This will be a problem if the package is installed
as
root
in locations such as/usr
or/usr/local
, or is otherwise not write-permitted, unless the post-install hook is also ran with the sufficient privileges. This is generally acceptable as the primary use is considered to be installation into virtual envs and user locations. That said, simply runningpython -c pass
or any other python invocation that does activatesite.py
under the required privileges will finalize post-install procedures. - There is an attempt to ensure that that axle wheels symlinks and targets don't extend beyond the allowed
schema
locations. Those attempts are superficial and have not been formally verified. For example, it may be possible to escape the path validation/confinement by:- hacking symlink creation order
- hacking symlink directory targets
- exploiting OS-specific
realpath
implementation idiosyncrasies (i.e.strict
vs not, and what is considered strict)
- Support schema detection for
prefix
installations. - Validate and verify Windows support.