Roman Imankulov

Roman Imankulov

Full-stack Python web developer from Porto

search results (esc to close)
22 Dec 2021

Optimize pip install with wheels

updated on 25 Dec 2021
wheels to optimize your installation
Photo by Jon Cartagena

Having that pip install -r requirements.txt that takes ages to install? Make sure you install all binary packages from wheels.

When “pip install” can’t find a wheel, it falls back to installing from source. In a large project, dependency after dependency, it slows down builds and makes developers unhappy.

What are Python wheels and why they are so fast

Long ago, most Python packages were distributed as source code with .tar.gz. The installed downloaded the archive and ran the setup.py script inside it. This format is known as “sdist”, source distribution.

Source distributions are still a thing, but about ten years ago, PEP 427 defined a so-called “Wheel format.” Wheels are nothing but the zip-files with the .whl extension. With wheels, you don’t run the code inside the package to install it or compile anything. The installer simply unzips the contents in the pre-defined location.

The performance advantage of wheels over sdist is especially apparent with binary packages. Our goal is to eradicate the need for compiling and make the installation process as fast as possible. Let’s see how we can do it.

Pre-requisites

Update pip and setuptools

Make sure you have the latest version of pip, setuptools, and wheel. Outdated versions may not recognize your platform and fail to find matching wheels.

My CI tasks or Docker files usually update these core dependencies before installing the requirements.

pip install -U pip setuptools wheel
pip install -r requirements.txt

How to spot problematic packages?

Run pip install --only-binary=:all: and see where it fails.

There is an option –only-binary to install only wheels.

pip install --force-reinstall -r requirements.txt --only-binary=:all:

Along the way, you may find some unmaintained projects that don’t have wheels.

ERROR: Could not find a version that satisfies the requirement package-name==0.0.1
ERROR: No matching distribution found for package-name==0.0.1

As long as they are pure python packages, it’s OK to keep them in sdist format. Include them in the installation list one by one.

pip install --force-reinstall -r requirements.txt --only-binary=:all: --no-binary=package-name,another-package-name

At some point, you spot a few packages that are not in the wheel format, but you really don’t want to install them from source. It’s time to find out how to fix it.

How to spot problematic packages faster?

Explore the output of pip download.

In one of my projects, there were too many source distributions, and filtering them out one by one was too cumbersome. I found an alternative approach to download everything and see how many .tar.gz files are in the output directory.

The pip download command can download all packages in the given repository.

mkdir -p downloads
pip download -r ./requirements.txt  --dest downloads
ls downloads/*.tar.gz

How does pip know which wheel to install?

It compares available wheels with your platform tags.

You can see the list of wheels on PyPI, the Python package index. For example, for Pandas 1.35, you’ll find more than a dozen wheels for different platforms.

Pandas 1.3.5. The list of files

Pandas 1.3.5. The list of files

Each wheel file has a so-called compatibility tag that defines the platform where it can be installed. The tag has the format {python tag}-{abi tag}-{platform tag}, for example, cp310-cp310-win_amd64 for the first package in our list.

The installer finds an intersection between this list and the list of tags compatible with your interpreter and platform.

TO the whole (very long) list of wheels compatible with your installation, type pip debug --verbose.

For example, for my Intel macOS machine and Python 3.10, I have the following tags:

$ pip debug --verbose
...
Compatible tags: 1945
  cp310-cp310-macosx_11_0_x86_64
  cp310-cp310-macosx_11_0_intel
  cp310-cp310-macosx_11_0_fat64
  ...
  py33-none-any
  py32-none-any
  py31-none-any
  py30-none-any

If the intersection between the two lists is found, pip will install the package from the wheel. Otherwise, it falls back to installing from source.

Why there’s no wheel for my platform?

There’s no wheel for your platform.

Now you know that somehow pip doesn’t have a wheel to install. Why is that, and what can you do about it?

  • The package doesn’t have wheels at all. You may consider politely asking the maintainer to issue a new version with a wheel.
  • You updated Python but didn’t update dependencies. The version of Python is newer than the version of the package. Older dependencies know nothing about more recent Python and don’t have wheels.
  • The opposite can also happen. If your version of Python is too old, you may not have a wheel for the package since maintainers stopped supporting it.

The list of reasons, of course, is not exhaustive. You may have more exotic reasons for the mismatch. At least, now you know how to debug it.

More about wheels

Roman Imankulov

Hey, I'm Roman, a full-stack Python web developer.

If you like what you read and want to work with me, drop me a line to roman.imankulov@gmail.com.

About me