Wheel

A built-package format for Python.

A wheel is a ZIP-format archive with a specially formatted filename and the .whl extension. It is designed to contain all the files for a PEP 376 compatible install in a way that is very close to the on-disk format. Many packages will be properly installed with only the “Unpack” step (simply extracting the file onto sys.path), and the unpacked archive preserves enough information to “Spread” (copy data and scripts to their final locations) at any later time.

The wheel project provides a bdist_wheel command for setuptools (requires setuptools >= 0.8.0). Wheel files can be installed with a newer pip from https://github.com/pypa/pip or with wheel’s own command line utility.

The wheel documentation is at http://wheel.rtfd.org/. The file format is documented in PEP 427 (http://www.python.org/dev/peps/pep-0427/).

The reference implementation is at https://bitbucket.org/pypa/wheel

Why not egg?

Python’s egg format predates the packaging related standards we have today, the most important being PEP 376 “Database of Installed Python Distributions” which specifies the .dist-info directory (instead of .egg-info) and PEP 426 “Metadata for Python Software Packages 2.0” which specifies how to express dependencies (instead of requires.txt in .egg-info).

Wheel implements these things. It also provides a richer file naming convention that communicates the Python implementation and ABI as well as simply the language version used in a particular package.

Unlike .egg, wheel will be a fully-documented standard at the binary level that is truly easy to install even if you do not want to use the reference implementation.

Usage

The current version of wheel can be used to speed up repeated installations by reducing the number of times you have to compile your software. When you are creating a virtualenv for each revision of your software the savings can be dramatic. This example packages pyramid and all its dependencies as wheels, and then installs pyramid from the built packages:

# Make sure you have the latest pip that supports wheel
pip install --upgrade pip

# Install wheel
pip install wheel

# Build a directory of wheels for pyramid and all its dependencies
pip wheel --wheel-dir=/tmp/wheelhouse pyramid

# Install from cached wheels
pip install --use-wheel --no-index --find-links=/tmp/wheelhouse pyramid

# Install from cached wheels remotely
pip install --use-wheel --no-index --find-links=https://wheelhouse.example.com/ pyramid

For lxml, an up to 3-minute “search for the newest version and compile” can become a less-than-1 second “unpack from wheel”.

As a side effect the wheel directory, “/tmp/wheelhouse” in the example, contains installable copies of the exact versions of your application’s dependencies. By installing from those cached wheels you can recreate that environment quickly and with no surprises.

To build an individual wheel, run python setup.py bdist_wheel. Note that bdist_wheel only works with distribute (import setuptools); plain distutils does not support pluggable commands like bdist_wheel. On the other hand pip always runs setup.py with setuptools enabled.

Wheel also includes its own installer that can only install wheels (not sdists) from a local file or folder, but has the advantage of working even when distribute or pip has not been installed.

Wheel’s builtin utility can be invoked directly from wheel’s own wheel:

$ python wheel-0.21.0-py2.py3-none-any.whl/wheel -h
usage: wheel [-h]

             {keygen,sign,unsign,verify,unpack,install,install-scripts,convert,help}
             ...

positional arguments:
  {keygen,sign,unsign,verify,unpack,install,install-scripts,convert,help}
                        commands
    keygen              Generate signing key
    sign                Sign wheel
    unsign              Remove RECORD.jws from a wheel by truncating the zip
                        file. RECORD.jws must be at the end of the archive.
                        The zip file must be an ordinary archive, with the
                        compressed files and the directory in the same order,
                        and without any non-zip content after the truncation
                        point.
    verify              Verify a wheel. The signature will be verified for
                        internal consistency ONLY and printed. Wheel's own
                        unpack/install commands verify the manifest against
                        the signature and file contents.
    unpack              Unpack wheel
    install             Install wheels
    install-scripts     Install console_scripts
    convert             Convert egg or wininst to wheel
    help                Show this help

optional arguments:
  -h, --help            show this help message and exit

Setuptools scripts handling

Setuptools’ popular console_scripts and gui_scripts entry points can be used to generate platform-specific scripts wrappers. Most usefully these wrappers include .exe launchers if they are generated on a Windows machine.

As of 0.23.0, bdist_wheel no longer places pre-generated versions of these wrappers into the *.data/scripts/ directory of the archive (non-setuptools scripts are still present, of course).

If the scripts are needed, use a real installer like pip. The wheel tool python -m wheel install-scripts package [package ...] can also be used at any time to call setuptools to write the appropriate scripts wrappers.

Defining the Python version

The bdist_wheel command automatically determines the correct tags to use for the generated wheel. These are based on the Python interpreter used to generate the wheel and whether the project contains C extension code or not. While this is usually correct for C code, it can be too conservative for pure Python code. The bdist_wheel command therefore supports two flags that can be used to specify the Python version tag to use more precisely:

--universal        Specifies that a pure-python wheel is "universal"
                   (i.e., it works on any version of Python).  This
                   equates to the tag "py2.py3".
--python-tag XXX   Specifies the precise python version tag to use for
                   a pure-python wheel.

Neither of these two flags have any effect when used on a project that includes C extension code.

A reasonable use of the –python-tag argument would be for a project that uses Python syntax only introduced in a particular Python version. There are no current examples of this, but if wheels had been available when Python 2.5 was released (the first version containing the with statement), wheels for a project that used the with statement would typically use –python-tag py25.

Typically, projects would not specify Python tags on the command line, but would use setup.cfg to set them as a project default:

[bdist_wheel]
universal=1

or:

[bdist_wheel]
python-tag = py32

Defining conditional dependencies

In wheel, the only way to have conditional dependencies (that might only be needed on certain platforms) is to use environment markers as defined by PEP 426.

As of wheel 0.24.0, the recommended way to do this is in the setuptools extras_require parameter. A : separates the extra name from the marker. Wheel’s own setup.py has an example:

extras_require={
    ':python_version=="2.6"': ['argparse'],
    'signatures': ['keyring'],
    'signatures:sys_platform!="win32"': ['pyxdg'],
    'faster-signatures': ['ed25519ll'],
    'tool': []
    },

The extra named ‘’ signifies a default requirement, as if it was passed to install_requires.

Older versions of bdist_wheel supported passing requirements in a now-deprecated [metadata] section in setup.cfg.

Automatically sign wheel files

Wheel contains an experimental digital signatures scheme based on Ed25519 signatures; these signatures are unrelated to pgp/gpg signatures and do not include a trust model.

python setup.py bdist_wheel will automatically sign wheel files if the environment variable WHEEL_TOOL is set to the path of the wheel command line tool.:

# Install the wheel tool and its dependencies
$ pip install wheel[tool]
# Generate a signing key (only once)
$ wheel keygen

$ export WHEEL_TOOL=/path/to/wheel
$ python setup.py bdist_wheel

Signing is done in a subprocess because it is not convenient for the build environment to contain bindings to the keyring and cryptography libraries. The keyring library may not be able to find your keys (choosing a different key storage back end based on available dependencies) unless you run it from the same environment used for keygen.

Format

The wheel format is documented as PEP 427 “The Wheel Binary Package Format...” (http://www.python.org/dev/peps/pep-0427/).

Slogans

Wheel

  • Because ‘newegg’ was taken.
  • Python packaging - reinvented.
  • A container for cheese.
  • It makes it easier to roll out software.