Wheel python что это
Перейти к содержимому

Wheel python что это

  • автор:

Python на колёсах

Инфраструктура системы пакетов для Python долго подвергалась критике как от разработчиков, так и от системных администраторов. Долгое время даже само комьюнити не могло прийти к соглашению, какие именно инструменты использовать в каждом конкретном случае. Уже существуют distutils, setuptools, distribute, distutils2 в качестве базовых механизмов распространения и virtualenv, buildout, easy_install и pip в качестве высокоуровневых инструментов управления всем этим беспорядком.

До setuptools основным форматом распространения были исходные файлы или некоторые бинарные MSI-дистрибутивы для Windows. Под Linux были изначально сломанный bdist_dumb и bdist_rpm , который работал только на системах, основанных на Red Hat. Но даже bdist_rpm работал недостаточно хорошо для того, чтобы люди начали его использовать.

Несколько лет назад PJE попытался исправить эту проблему, предоставив смесь из setuptools и pkg_resources для улучшения distutils и добавления метаданных в Python-пакеты. В дополнение к этому он написал утилиту easy_install для их установки. По причине отсутствия формата распространения, поддерживающего метаданные, был предоставлен формат ‘яиц’ [egg].

Python eggs – обычные zip-архивы, содержащие python-пакет и необходимые метаданные. Хотя многие люди, вероятно, никогда намеренно не собирали egg’и, их формат метаданных до сих пор жив-здоров. И все разворачивают свои проекты с использованием setuptools.

К сожалению, некоторое время спустя сообщество разделилось, и часть его провозгласила смерть бинарных форматов и ‘яиц’ в частности. После этого pip, замена easy_install, перестал принимать egg-формат.

Потом прошло еще немного времени, и отказ от бинарных пакетов стал доставлять неудобства. Люди всё больше и больше стали деплоить на облачные сервера, а необходимость перекомпиляции C-шных библиотек на каждой машине не слишком радует. Так как ‘яйца’ на тот момент были малопонятны (я так полагаю), их переделали в новых PEP-ах, и назвали ‘колёсами’ [wheels].

В дальнейшем предполагается, что все действия происходят в virtualenv-окружении.

Что за колесо?

Начнём с простого. Что представляют собой ‘колёса’ и чем они отличаются от ‘яиц’? Оба формата являются zip-файлами. Главная разница в том, что egg можно импортировать без распаковки, wheel же придётся распаковать. Хотя нет никаких технических причин, делающих ‘колёса’ неимпортируемыми, поддержка их прямого импорта никогда даже не планировалась.

Другое различие в том, что ‘яйца’ содержат скомпилированные байткод, а ‘колёса’ – нет. Главное преимущество этого в том, что нет необходимости создавать отдельные wheel’ы для каждой версии Python до тех пор, пока не придётся распространять слинкованные через libpython модули. Хотя в новых версиях Python 3 при использовании стабильного ABI даже это уже можно провернуть.

Однако wheel-формат тоже не лишен проблем, некоторые из которых он наследует от ‘яиц’. Например, бинарные дистрибутивы под Linux до сих пор неприемлемы для большинства из-за двух недостатков: Python сам по себе компилируется под Linux в разных формах, и модули линкуются с разными системными библиотеками. Первая проблема вызвана сосуществованием несовместимых версий Python 2: USC2 и USC4. В зависимости от режима компиляции меняется ABI. В настоящее время wheel (насколько я могу судить) не содержит информации о том, с каким режимом Unicode связана библиотека. Отдельная проблема в том, что дистрибутивы Linux меньше совместимы между собой, чем хотелось бы, и обстоятельства могут сложиться так, что сборка, скомпилированная под один дистрибутив, не будет работать на остальных.

Всё это выливается в то, что, вообще говоря, на данный момент бинарные ‘колёса’ нельзя загружать на PyPI как несовместимые с различными системами.

В дополнение ко всему этому wheel сейчас знает только две крайности: бинарные пакеты и пакеты, содержащие чистый python-код. Бинарные пакеты специфичны для Python ветки 2.x. Сейчас это не кажется большой проблемой, потому что цикл 2.x подходит к концу, и пакетов, собранных только для 2.7, хватит надолго. Но если бы вдруг речь пошла про Python 2.8, была бы интересна возможность заявить, что этот пакет не зависит от версии Python, но он содержит бинарники, поэтому он не может не зависеть от архитектуры.

Единственный случай, оправдывающий существование такого пакета – это когда он содержит распределенные библиотеки, загружаемые с ctypes из CFFI. Такие библиотеки не связаны через libpython и не зависимы от реализации языка (их можно использовать даже с pypy).

Но есть и светлая сторона: ничто не запрещает использовать бинарные wheel’ы в своих собственных однородных инфраструктурах.

Сборка колеса

Итак, теперь мы знаем, что такое wheel. Как сделать своё собственное ‘колесо’? Сборка из собственных библиотек – простейший процесс. Всё, что нужно – свежая версия setuptools и библиотека wheel . Как только они оба установлены, ‘колесо’ собирается следующей командой:

$ python setup.py bdist_wheel 

Wheel будет создан в директории пакета. Однако есть одна вещь, которой следует опасаться: распространение бинарников. По умолчанию собираемое ‘колесо’ (при условии, что в setup.py не используется никаких бинарных шагов) состоит из pure-python кода. Это значит, что даже если распространять .so , .dylib или .dll как часть своего пакета, полученное ‘колесо’ будет выглядеть платформо-независимым.

Решение этой проблемы – вручную реализовать Distribution из setuptools, скинув флаг чистоты в false :

import os from setuptools import setup from setuptools.dist import Distribution class BinaryDistribution(Distribution): def is_pure(self): return False setup( . include_package_data=True, distclass=BinaryDistribution, ) 
Установка колеса

С использованием свежей версии pip ‘колесо’ ставится следующим образом:

$ pip install package-1.0-cp27-none-macosx_10_7_intel.whl 

Но что с зависимостями? Тут появляются некоторые сложности. Обычно одним из требований к пакету является возможность его установки даже без подключения к интернету. К счастью, pip позволяет отключать загрузку из индекса и устанавливать директорию, содержащую всё необходимое для установки. Если у нас есть wheel’ы для всех зависимостей необходимых версий, можно сделать следующее:

$ pip install --no-index --find-links=path/to/wheels package==1.0 

Таким образом будет установлена версия 1.0 пакета package в наше виртуальное окружение.

Колёса для зависимостей

Окей, но что, если у нас нет .whl для всех наших зависимостей? Pip в теории позволяет решить эту проблему использованием команды wheel . Это должно работать как-то так:

pip wheel --wheel-dir=path/to/wheels package==1.0 

Эта команда выгрузит все пакеты, от которых зависит наш пакет, в указанную папку. Но есть пара проблем.
Первая состоит в том, что в команде в настоящий момент есть баг, который не выгружает зависимости, которые уже являются ‘колёсами’. Так что если зависимость уже доступна на PyPI в wheel-формате, она не будет загружена.

Это временно решается shell-скриптом, который вручную перемещает из кэша скачанные wheel’ы.

#!/bin/sh WHEEL_DIR=path/to/wheels DOWNLOAD_CACHE_DIR=path/to/cache rm -rf $DOWNLOAD_CACHE_DIR mkdir -p $DOWNLOAD_CACHE_DIR pip wheel --use-wheel -w "$WHEEL_DIR" -f "$WHEEL_DIR" \ --download-cache "$DOWNLOAD_CACHE_DIR" package==1.0 for x in "$DOWNLOAD_CACHE_DIR/"*.whl; do mv "$x" "$WHEEL_DIR/$" done 

Вторая проблема чуть серьёзней: как pip найдет наш собственный пакет, если его нет на PyPI? Правильно, никак. Документация в таком случае рекомендует использовать не pip wheel package , а pip wheel -r requirements.txt , где requirements.txt содержит все необходимые зависимости.

Сборка пакетов c использованием DevPI

Такое временное решение проблемы зависимостей вполне применимо в простых ситуациях, но что делать, если есть множество внутренних python-пакетов, зависящих друг от друга? Такая конструкция быстро разваливается.

К счастью, в прошлом году Holker Krekel создал решение этой беды под названием DevPI, который по существу является хаком, эмулирующим работу pip с PyPI. После установки на компьютер DevPI работает как прозрачный прокси перед PyPI и позволяет pip-у устанавливать пакеты из локального репозитория. К тому же все пакеты, скачанные с PyPI, автоматически кэшируются, так что даже если отключить сеть, эти пакеты будут доступны для установки. И, в конце концов, появляется возможность загрузки своих собственных пакетов на локальный сервер, чтобы ссылаться на них так же, как и на хранящиеся в публичном индексе.

Я рекомендую установить DevPI в локальный virtualenv, после чего добавить ссылки на devpi-server и devpi в PATH .

$ virtualenv devpi-venv $ devpi-venv/bin/pip install --upgrade pip wheel setuptools devpi $ ln -s `pwd`/devpi-venv/bin/devpi ~/.local/bin $ ln -s `pwd`/devpi-venv/bin/devpi-server ~/.local/bin 

После этого остаётся просто запустить devpi-server , и он будет работать до ручной остановки.

$ devpi-server --start 

После запуска его необходимо единожды проинициализировать:

$ devpi use http://localhost:3141 $ devpi user -c $USER password= $ devpi login $USER --password= $ devpi index -c yourproject 

Так как я использую DevPI ‘для себя’, имена пользователя DevPI и системного пользователя совпадают. На последнем шаге создаётся индекс по имени проекта (при необходимости можно создать несколько).

Для перенаправления pip на локальный репозиторий можно сделать экспорт переменной окружения:

$ export PIP_INDEX_URL=http://localhost:3141/$USER/yourproject/+simple/ 

Я размешаю эту команду в скрипт postactivate моего virtualenv для предотвращения случайной загрузки из неверного индекса.

Для размещения собственных wheel’ов в локальном DevPI используется утилита devpi :

$ devpi use yourproject $ devpi upload --no-vcs --formats=bdist_wheel 

Флаг —no-vcs отключает магию, которая пытается определить систему контроля версий и перемещает некоторые файлы в первую очередь. Мне это не нужно, так как в моих проектах распространяются файлы, которые я не включаю в VCS (бинарники, например).

Напоследок я настоятельно рекомендую разбить файлы setup.py таким образом, что PyPI их отвергнет, а DevPI примет, чтобы случайно не зарелизить свой код с помощью setup.py resease . Самый простой способ это сделать – добавить неверный классификатор PyPI:

setup( . classifier=['Private :: Do Not Upload'], ) 
Заворачиваем

Теперь всё готово для начала использования внутренних зависимостей и сборки собственных ‘колёс’. Как только они появятся, их можно заархивировать, загрузить на другой сервер и установить в отдельный virtualenv.
Весь процесс станет чуть проще, когда pip wheel перестанет игнорировать существующие wheel-пакеты. А пока приведенный выше shell-скрипт – не худшее решение.

В сравнении с ‘яйцами’

Сейчас wheel-формат более притягателен, чем egg. Его разработка активнее, PyPI начал добавлять его поддержку и, так как с ним начинают работать утилиты, он похож на лучшее решение. ‘Яйца’ пока что поддерживаются только easy_install, хотя большинство давно перешло на pip.

Я считаю, что сообщество Zope до сих пор крупнейшее из базирующихся на egg-формате и buildout. И я считаю, что если решение на основе ‘яиц’ в вашем случае применимо, его и стоит применять. Я знаю, что многие не используют eggs вовсе, предпочитая создавать virtualenv-ы, архивировать их и рассылать по разным серверам. Как раз для такого развёртывания wheels – лучшее решение, так как у разных серверов могут быть разные пути к библиотекам. Встречалась проблема, связанная с тем, что .pyc -файлы создавались на билд-сервере для virtualenv, а эти файлы содержат конкретные пути к файлам. С использованием wheel .pyc создаются после установки в виртуальное окружение и автоматически будут иметь корректные пути.

Итак, теперь он у вас есть. Python на колёсах. И это вроде даже работает, и, возможно, стоит потраченного времени.

  • Python
  • Программирование

Tech blog by @dizballanze

Python wheels для быстрой установки зависимостей

Часто все зависимые python пакеты устанавливаются при помощи pip из PyPI и/или VCS. Такой подход имеет ряд недостатков:

  • производительность — каждый раз необходима скачивать и собирать пакеты что занимает большое количество времени
  • работа в оффлайн режиме — без подключения к интернету не получится установить зависимости
  • стабильность — установка зависимостей невозможна в случае:
    • неполадок на стороне PyPI
    • неполадок на стороне VCS (GitHub, Bitbucket, etc)
    • нарушения зависимостей (удаление репозитория с Github, удаление пакета из PyPI и тд)
    • неполадок у хостинг провайдера, которые могут привести к недоступности необходимых сетевых ресурсов (PyPI, VSC, etc)

    Для решения этой проблемы предлагается использование заранее подготовленных пакетов wheel для всех зависимостей и хранение их в репозитории системы.

    Создаем архив wheel пакетов

    Wheel — это современный формат распространения пакетов в Python среде, который пришел на замену eggs. Рассмотрим процесс создания архива wheel для всех зависимостей системы.

    Представим типичный Python проект с файлом requirements.txt содержащим зависимости. Пример файла requirements.txt :

    svgwrite==1.1.6 ipython==2.3.0 flask==0.10.1 flask-mongoengine==0.7.1 flask-uploads==0.1.3 -e git://github.com/Samael500/flask-principal.git@dab7f391f0eeb76a25fa1b3dae7308a0924c8a12#egg=flask-principal -e git://github.com/Samael500/flask-security.git@f1042b5db67147b8ddaa8b767b2dfe063bb56ffa#egg=flask-security Flask-Admin==1.0.8 Flask-Session==0.1.1 Flask-Script==2.0.5 gunicorn==19.1.1 Flask-Testing==0.4.2 tornado==4.0.2 nose==1.3.4 pep8==1.5.7 Pillow==2.6.1 pyflakes==0.8.1 pylama==6.1.1 spec==0.11.1 py-bcrypt==0.4 WTForms==1.0.4 blessings==1.6 beautifulsoup4==4.3.2 lxml==3.4.1 -e git://github.com/Samael500/jinja-assets-compressor.git@8e1639cec6f8b347794fe1334519daacc6b763b0#egg=jac PyYAML==3.10 

    В нашем файле requirements.txt есть зависимости из внешних ресурсов (не PyPI), которые предполагают загрузку пакетов из VCS (в данном случае из git репозиториев на Github). Скопируем старый requirements.txt в requirements-remote.txt , а в requirements.txt заменим внешние ресурсы на обычные пакеты из PyPI и получим:

    svgwrite==1.1.6 ipython==2.3.0 flask==0.10.1 flask-mongoengine==0.7.1 flask-uploads==0.1.3 flask-principal flask-security Flask-Admin==1.0.8 Flask-Session==0.1.1 Flask-Script==2.0.5 gunicorn==19.1.1 Flask-Testing==0.4.2 tornado==4.0.2 nose==1.3.4 pep8==1.5.7 Pillow==2.6.1 pyflakes==0.8.1 pylama==6.1.1 spec==0.11.1 py-bcrypt==0.4 WTForms==1.0.4 blessings==1.6 beautifulsoup4==4.3.2 lxml==3.4.1 jac PyYAML==3.10

    Это делается для того, чтобы при установке из архива wheel пакетов не происходили запросы к внешним VCS, а брались локальные wheel, которые мы сейчас будем генерировать.

    Cоздаем и активируем venv :

    pyvenv venv . venv/bin/activate

    Устанавливаем все пакеты как обычно, но из requirements-remote.txt :

    pip install -r requirements-remote.txt

    Сгенерируем архив всех пакетов PyPI, всех их зависимостей и всех зависимостей внешних пакетов (VCS). Для этого нам потребуется свежая версия pip и пакет wheel :

    pip install -U pip pip install wheel mkdir wheels pip wheel -w wheels/ -r requirements-remote.txt --pre --allow-all-external

    После этого получаем архив wheel пакетов для всех зависимостей кроме внешних (VCS). Для внешних пакетов устанавливаемых из исходников необходимо сгенерировать пакеты вручную при помощи setup.py bdist_wheel :

    cd venv/src/flask-principal python setup.py bdist_wheel --dist-dir ../../../wheels/ cd ../flask-security python setup.py bdist_wheel --dist-dir ../../../wheels/ cd ../jac python setup.py bdist_wheel --dist-dir ../../../wheels/

    Теперь в директории wheels есть все необходимые пакеты для установки всех зависимостей системы. Процесс уставновки зависимостей из локального архива пакетов выполняется так:

    pip install --no-index -f wheels/ -r requirements.txt

    обратите внимание, что используется файл requirements.txt , а не requirements-remote.txt .

    Тестирование скорости установки

    Обычная установка со скачиванием пакетов из PyPI и VCS:

    time pip install -r requirements-remote.txt real 4m20.655s user 1m31.242s sys 0m55.539s

    Установка из локального архива wheels:

    time pip install --no-index -f wheels/ -r requirements.txt real 1m3.412s user 0m4.808s sys 0m31.210s

    Из результатов можно сделать вывод, что время установки пакетов из локального архива в нашем случае меньше в 4 раза. Что логично, т.к. пакеты заново не скачиваются из интернета и не компилируются.

    Бонус

    Для удобства написал небольшой скрипт, автоматизирующий сборку пакетов установленных из исходников.

    Пример использования скрипта:

    python build_wheels --sources-dir venv/src/ --wheels-dir wheels/

    Скрипт пройдется по всем поддиректориям venv/src/ и в каждой из них попробует собрать пакет в директорию wheels/ .

    Что значит cp35m в названии wheel на Python?

    При попытке установить модуль .whl через pip мне выдаётся ошибка о несовместимости пакета. И это со всеми пакетами с cp35m в названии. Но модули без cp35m устанавливаются просто на ура. В чём может быть проблема?

    Отслеживать
    задан 24 июн 2017 в 10:11
    user240810 user240810
    11 2 2 бронзовых знака

    1 ответ 1

    Сортировка: Сброс на вариант по умолчанию

    cp — значит, что wheel предназначен для интерпретатора CPython.

    35 — значит, что для версии 3.5 интерпретатора.

    m — означает использование PyMalloc для управления памятью.

    Если в названии файла после символов cp35m идёт сразу расширение .whl , значит пакет кроссплатформенный, и тогда остаётся только убедиться, что вы используете именно Python 3.5. Сделать это можно запуском интерпретатора с ключом -V .

    Если же в имени файла есть и другие символы, типа win_amd64 , то он предназначен для конкретной операционной системы определённой разрядности.

    Wheel python что это

    What’s on this Page

    .WHL вариант №

    Файл WHL (Wheel) — это файл пакета дистрибутива, сохраненный в формате колеса Python. Это стандартный формат установки дистрибутивов Python, который содержит все файлы и метаданные, необходимые для установки. Файл WHL также содержит информацию о версиях и платформах Python, поддерживаемых этим файлом колеса. Подобно установочному файлу MSI, формат файла WHL представляет собой готовый к установке формат, который позволяет запускать установочный пакет без создания исходного дистрибутива.

    Формат файла WHL

    Формат файла WHL представляет собой архив ZIP (.zip), который содержит все установочные файлы и метаданные, необходимые установщикам для установки пакета. Эти файлы WHL можно извлечь с помощью функции распаковки или стандартных приложений для распаковки, таких как WinZIP и WinRAR.

    Соглашение об именах файлов WHL

    Файл WHL назван в соответствии со следующим соглашением.

    Ниже приведен пример имени файла WHL.

    cryptography-2.9.2-cp35-abi3-macosx_10_9_x86_64.whl 
    • cryptography — это имя пакета.
    • «2.9.2» — это пакетная версия криптографии. Версия — это строка, совместимая с PEP 440, например 2.9.2, 3.4 или 3.9.0.a3.
    • cp35 — это тег Python, обозначающий реализацию и версию Python, которые требуются для колеса.
    • abi3 — это тег ABI. ABI означает двоичный интерфейс приложения.
    • macosx_10_9_x86_64 — это тег платформы, который довольно многословен.

    использованная литература

    • Что такое колеса Python и зачем вам это?
    • Колесо Python

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *