MicroPython:Основы/Язык MicroPython и его реализация/Дистрибутивы, управление пакетами и развертка приложений

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Дистрибутивы, управление пакетами и развертка приложений[1]

Как и в «старшем» Python, в MicroPython можно создавать «сторонние» пакеты, распространять их и легко устанавливать на платформу каждого пользователя. Здесь рассказывается, как все это делать, но подразумевается, что у вас уже есть некоторые знания о работе с пакетами в Python.

Процесс создания и использования пакетов включает в себя следующие этапы:

  1. Модули и пакеты Python преобразовываются в дистрибутивные пакеты-архивы и публикуются в каталоге Python Package Index («каталог пакетов Python») или PyPI.
  2. Для установки дистрибутивного пакета (дистрибутива) в MicroPython-порт, у которого есть средства для выхода в сеть (например, Unix), можно воспользоваться менеджером пакетов upip.
  3. Для установки дистрибутива в MicroPython-порт, у которого нет средства для выхода в сеть, можно сначала подготовить «установочный образ» на Unix-порте, а затем с помощью подходящих коммуникационных средств передать его на устройство.
  4. Если речь об устройства с ограниченной памятью, установочный образ можно заморозить как байт-код в прошивку MicroPython, что позволит минимизировать требования к памяти.

Ниже все эти шаги описаны более подробно.

Распространение пакетов

Модули и пакеты Python можно упаковать в архивы, которые затем можно передавать из одной системы в другую, сохранять в общеизвестном хранилище (PyPl) и при необходимости оттуда их скачивать (чтобы потом установить). Эти архивы также называются «дистрибутивами» (что отличает их от Python-пакетов, предназначенных для организации исходного кода Python).

Форматом для дистрибутива MicroPython является широко известный «tar.gz», но с некоторыми адаптациями. В компрессоре GZIP для работы с TAR-архивами используется внешняя утилита, которая по умолчанию использует размер словаря в 32 Кб, и это значит, что при распаковке сжатого потокового объекта понадобятся смежные участки памяти общим размером в 32 Кб. На устройствах с ограниченной памятью такой вариант может быть неприемлем, потому что вся их память может быть меньше, чем эти 32 Кб, но даже если больше, из-за фрагментации такой большой блок смежных участков памяти, возможно, найти будет трудно. Для решения этой проблемы в дистрибутивах MicroPython используется GZIP-сжатие с размером словаря в 4 Кб, что создает приемлемый компромисс между возможностью более-менее сжать данные и возможностью распаковать их даже на самых маленьких устройствах.

Помимо маленького размера словаря при сжатии, в дистрибутивах MicroPython есть и другие оптимизационные функции – вроде удаления из архива всех файлов, не используемых в процессе установки. В частности, во время установки менеджер пакетов upip не выполняет файл «setup.py» (см. выше), и поэтому этот файл в архив добавлен не будет.

В то же время эти оптимизационные меры делают дистрибутивы MicroPython не совместимыми с пакетным менеджером CPythonpip. Но это не такая уж большая проблема, потому что пакеты можно установить с помощью upip, а потом использовать с помощью CPython (если они совместимы). С другой стороны, большинство пакетов CPython по разным причинам не совместимы с MicroPython, в первую очередь – потому что полагаются на функции, которые не реализованы в MicroPython.

Итак, если вкратце, дистрибутивы-архивы MicroPython хорошо оптимизированы под целевые платформы MicroPython, коими являются устройства с ограниченными ресурсами.

Менеджер пакетов upip

Дистрибутивы MicroPython необходимо устанавливать с помощью менеджера пакетов upip. Это Python-приложение, которое обычно распространяется (в виде замороженного байт-кода) вместе с портами MicroPython, имеющими средства для работы с сетью. Менеджер пакетов upip доступен как минимум на порте MicroPython Unix.

На любом MicroPython-порте, где есть upip, доступ к нему можно получить следующим образом:

import upip
upip.help()
upip.install(package_or_package_list, [path])

Здесь package_or_package_list – это название дистрибутива или список с названиями дистрибутивов, которые нужно установить. В опциональном параметре path указывается место в файловой системе, куда его/их надо установить. По умолчанию установка выполняется в стандартную директорию с библиотеками (см. ниже).

Ниже – пример установки пакета и его последующего использования:

>>> import upip
>>> upip.install("micropython-pystone_lowmem")
[...]
>>> import pystone_lowmem
>>> pystone_lowmem.main()

Обратите внимание, что названия Python-пакета и дистрибутива, как правило, совпадать не должны, и зачастую действительно не совпадают. Дело в том, что PyPI – это центральный репозиторий пакетов для всего разнообразия реализаций и версий Python, и поэтому ваш дистрибутив, возможно, надо будет поместить в пространство имен конкретной реализации. К примеру, названия всех пакетов в «micropython-lib» следуют следующему правилу: если модуль или пакет Python назван foo, то его дистрибутив будет называться micropython-foo.

В портах, где прошивка MicroPython запускается из командной оболочки ОС (вроде Unix-порта), upip можно запустить (и обычно именно так он и запускается) именно оттуда, а не из MicroPython’овского терминала REPL. Вот команды, соответствующие фрагменту выше:

micropython -m upip -h
micropython -m upip install [-p <path>] <packages>...
micropython -m upip install micropython-pystone_lowmem

Кросс-установка пакетов

Для MicroPython-портов, не имеющих средства для работы с сетью, рекомендуется «кросс-установить» их в «установочный образ» при помощи порта MicroPython Unix, а затем передать этот образ на устройство с помощью подходящих коммуникационных средств.

Для установки в установочный образ в upip нужно воспользоваться переключателем -p:

micropython -m upip install -p install_dir micropython-pystone_lowmem

После того, как эта команда будет выполнена, содержимое пакета (и всех зависимых пакетов) можно будет найти в поддиректории install_dir/. Вам нужно будет отправить содержимое этой директории (без префикса install_dir/) на необходимое место в устройстве – туда, где его сможет найти Python-оператор import (см. раздел «Менеджер пакетов upip» выше).

Кросс-установка пакетов с заморозкой

Для устройств с ограниченной памятью вариант установки в предыдущем разделе не слишком эффективен с точки зрения использования памяти, потому что в нем пакеты устанавливаются в виде исходного кода, поэтому при каждом импорте их надо будет компилировать в байт-код. Эта компиляция требует RAM-памяти, и получившийся байт-код тоже будет храниться в RAM-памяти, что уменьшит количество памяти для хранения данных приложения. Более того, процесс установки выше требует наличия файловой системы на устройстве, а у большинства устройств с ограниченной памятью ее может даже не быть.

Заморозка байт-кода – это процесс, позволяющий решить все описанные выше проблемы, потому что:

  • Исходный код предкомпилируется в байт-код и будет сохранен в таком виде
  • Байт-код хранится в ROM-, а не в RAM-памяти
  • Замороженным пакетам файловая система не нужна

Использование замороженного байт-кода требует создания прошивки MicroPython-порта из исходного кода на Си. Соответственно, процесс будет таким:

  1. Следуйте инструкциям для своей платформы по настройке тулчейна и сборке порта. К примеру, инструкции для ESP8266-порта находятся в «ports/esp8266/README.md». Перед тем, как приступать к следующим шагам, убедитесь, что сборка порта и установка получившейся прошивки прошли успешно.
  2. Соберите порт MicroPython Unix, и убедитесь, что он находится в PATH, а затем выполните micropython.
  3. Переключитесь на директорию порта (например, у ESP8266 это «ports/esp8266/»).
  4. Запустите make clean-frozen. Это удалит все предыдущие модули, которые были установлены для заморозки (если вы хотите добавить новые модули, этот шаг надо пропустить, чтобы не начинать с нуля).
  5. Запустите micropython -m upip install -p modules <пакеты>..., чтобы установить пакеты, которые вы хотите заморозить.
  6. Запустите make clean.
  7. Запустите make.

После этого вы должны получить прошивку, внутри которой будут модули в виде замороженного байт-кода, которую можно устанавливать как обычно. Пара примечаний:

  1. Шаг 5 в инструкции выше подразумевает, что используемый дистрибутив есть в каталоге PyPI. Если его там нет, вам надо будет вручную скопировать файлы исходного кода Python в поддиректорию «modules/» в директории порта. (Учтите, что upip не поддерживает установку из репозиториев системы управления версиями).
  2. У прошивки для «голого железа» обычно имеются ограничения по размеру, поэтому слишком большое количество замороженных модулей может ее переполнить. Обычно в таком случае выдается ошибка связывания. Но иногда сгенерированный образ запустить не получается – это типовой баг, о нем нужно сообщить разработчикам, чтобы они разобрались в чем дело. Если вы столкнулись с такой проблемой, первым делом можно попробовать уменьшить количество замороженных модулей в прошивке.

Создание дистрибутивов

Дистрибутивы для MicroPython создаются также, как и для CPython и любой другой реализации Python (более подробно см. в ссылках в конце статьи). Вместо distutils необходимо использовать setuptools, потому что в distutils не поддерживаются зависимости и другие функции. Для упаковки используется формат sdist (от англ. «source distribution»). Постобработка, о которой рассказывалось выше (и предобработка, о которой будет рассказано в следующем разделе) выполняется при помощи модифицированной setuptools-команды sdist. То есть упаковка будет работать, как и у стандартного setuptools, но пользователю лишь нужно переопределить команду sdist, вставив в setup() вот такой аргумент:

from setuptools import setup
import sdist_upip

setup(
    ...,
    cmdclass={'sdist': sdist_upip.sdist}
)

Модуль sdist_upip.py из фрагмента выше можно найти в micropython-lib.

Ресурсы приложения

Помимо исходного кода, в готовом приложении также часто содержатся файлы данных: шаблоны веб-страниц, изображения и т.д. Понятно, как работать с ними, если приложение установлено вручную – вы просто помещаете эти данные в какое-то место файловой системы, а затем используете обычные функции для доступа к данным.

Но все меняется, когда приложение устанавливается из пакета. Это более продвинутый, простой и гибкий способ установки, но в этом случае вам также нужно воспользоваться более продвинутым методом для доступа к файлам данных. Он заключается в том, чтобы относиться к этим файлам данных как к «ресурсам» и сделать парочку дополнительных манипуляций.

В Python доступ к ресурсам поддерживается с помощью модуля pkg_resources из библиотеки «setuptools». MicroPython тоже частично поддерживает функционал этого модуля – в частности, за это отвечает функция pkg_resources.resource_stream(пакет, ресурс). Идея в том, что приложение может вызвать эту функцию и задать в ней аргументом идентификатор нужного ресурса – это будет относительный путь к файлу данных внутри заданного пакета (как правило, пакета приложения высокого уровня). Она вернет потоковый объект, который можно использовать для доступа к содержимому ресурсов. Таким образом, функция resource_stream() эмулирует интерфейс стандартной функции open().

Если пакет установлен в файловую систему, то resourse_stream() будет использовать файловые операции. Но эта функция может работать и без файловой системы – например, если пакет заморожен в виде байт-кода. Это, однако, требует дополнительного этапа при упаковке приложения – создания «ресурсного Python-модуля».

Идея этого модуля в том, чтобы преобразовать двоичные данные в Python-объект bytes, а затем поместить его в словарь, данные в котором будут упорядочены по названиям ресурсов. Это преобразование выполняется автоматически при помощи переопределенной команды sdist, описанной в предыдущем разделе.

Теперь давайте рассмотрим всё это с помощью наглядного примера. Допустим, ваше приложение имеет следующую структуру:

my_app/
    __main__.py
    utils.py
    data/
        page.html
        image.png

Файлы «__main__.py» и «utils.py» получают доступ к ресурсам с помощью вот этих функций:

import pkg_resources

pkg_resources.resource_stream(__name__, "data/page.html")
pkg_resources.resource_stream(__name__, "data/image.png")

Вы можете как обычно разрабатывать и делать отладку приложения на порте MicroPython Unix. Когда наступит время делать из него дистрибутив, просто воспользуйтесь переопределенной командой sdist из модуля sdist_upip, как описывалось в разделе выше.

Это создаст ресурсный Python-модуль под названием R.py на основе файлов, объявленных в файлах MANIFEST и MANIFEST.in (любой файл без «*.py» будет считаться ресурсом и добавлен в R.py) – перед тем, как приступить к обычным этапам упаковки.

Если подготовить приложение таким образом, оно будет работать и в виде замороженного байт-кода, и будучи установленным в файловую систему.

Чтобы начать отладку создания R.py, запустите следующее:

python3 setup.py sdist --manifest-only

В качестве альтернативы можно воспользоваться скриптом «tools/mpy_bin2res.py» из дистрибутива MicroPython – в этом случае вам нужно будет также указать пути ко всем файлам ресурсов:

mpy_bin2res.py data/page.html data/image.png

Ссылки

См.также

Внешние ссылки