Другое

Как собрать PEX-файл с подмодулями Python для публикации

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

Как упаковать проект Python, содержащий подмодули, в один PEX‑файл для распространения?

Мой проект имеет следующую структуру:

<folder>\
    project.py
    submodule1\
        file1.py
        file2.py
    submodule2\
        file3.py
    ...

Запуск python project.py из каталога работает нормально, но я хочу раздавать проект как один исполняемый файл с помощью PEX.

Я пробовал следующие подходы безуспешно:

  1. pex -r requirements.txt -o x.pex – это создаёт виртуальное окружение с нужными пакетами, но не включает исходный код моего проекта.

  2. pex -r requirements.txt -c whatever и pex -r requirements.txt -e whatever – тоже не дали ожидаемого результата.

  3. После добавления setup.py я выполнил pex . -o x.pex, но всё равно не могу запустить программу с опциями -c или -e.

Есть ли простой способ создать PEX‑файл, который объединит как исходники проекта, так и виртуальное окружение, чтобы он работал так же, как python project.py? Например:

pex -r requirements.txt project.py -o x.pex

Какой правильный подход для упаковки проекта Python с подмодулями в один PEX‑файл, который можно запустить напрямую?

Чтобы упаковать проект Python с подмодулями в один PEX‑файл, необходимо использовать правильную структуру команды, которая включает ваш локальный код проекта и задаёт корректную точку входа. Ключевой момент – сообщить PEX включить текущий каталог (.), установить точку входа в ваш основной модуль и включить все зависимости.

Правильный подход:

bash
pex -f . -r requirements.txt -e project -o myproject.pex

Затем запустите его:

bash
./myproject.pex

Содержание


Понимание основ упаковки PEX

PEX (Python EXecutable) – это самодостаточные Python‑приложения, которые объединяют ваш код и все зависимости в один исполняемый файл. В отличие от предыдущих попыток, ключевой момент – явно указать PEX включить локальный код проекта с помощью флага -f (find‑links).

Согласно документации PEX, флаг -f «обеспечивает включение текущего каталога в путь поиска, что позволяет загружать локальные модули». Именно поэтому предыдущие попытки не сработали – они не включали ваш локальный код в PEX‑файл.

По умолчанию bdist_pex создаёт исполняемый файл, используя консольный скрипт с тем же именем, что и пакет.


Настройка структуры проекта

Для того чтобы структура вашего проекта корректно работала с PEX, необходимо убедиться, что она соответствует стандартным соглашениям Python‑пакетирования. Ваша структура в порядке, но стоит добавить файл setup.py в корень:

<folder>/
    setup.py
    project.py
    submodule1/
        __init__.py
        file1.py
        file2.py
    submodule2/
        __init__.py
        file3.py
    requirements.txt

setup.py должен выглядеть так:

python
from setuptools import setup, find_packages

setup(
    name="myproject",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        # зависимости перечислены здесь или в requirements.txt
    ],
    entry_points={
        'console_scripts': [
            'myproject=project:main',
        ],
    },
)

Как отмечено в руководстве по упаковке Python, «необходимо включить корректный setup.py для ваших модулей. Это необходимо, чтобы указать точку входа приложения».


Правильная структура команды PEX

Базовая структура команды

Основная команда для правильной упаковки вашего проекта:

bash
pex -f . -r requirements.txt -e project -o myproject.pex

Разберём каждый компонент:

  • -f . – критически важно! Это указывает PEX включить текущий каталог в путь поиска, делая локальные модули доступными.
  • -r requirements.txt – указывает файл зависимостей.
  • -e project – задаёт точку входа в модуль project (или project:main, если у вас конкретная функция).
  • -o myproject.pex – указывает имя выходного файла.

Альтернативные форматы точки входа

Вы можете задать точку входа разными способами, как указано в документации PEX:

  1. Точка входа в модуль: -e project (запускает модуль).
  2. Точка входа в функцию: -e project:main (запускает функцию main в project.py).
  3. Консольный скрипт: -c myproject (использует консольный скрипт, определённый в setup.py).

Согласно Stack Overflow, «Чтобы задать точку входа в .py файл при создании PEX, используйте -c command!».


Опции конфигурации точки входа

Опция 1: Точка входа в модуль

Если ваш project.py содержит код, который можно запустить как модуль:

bash
pex -f . -r requirements.txt -e project -o myproject.pex

Запуск:

bash
./myproject.pex

Опция 2: Точка входа в конкретную функцию

Если у вас есть конкретная функция в project.py:

bash
pex -f . -r requirements.txt -e project:main -o myproject.pex

Запуск:

bash
./myproject.pex

Опция 3: Точка входа в консольный скрипт

Используя подход с консольным скриптом и setup.py:

bash
pex -f . -r requirements.txt -c myproject -o myproject.pex

Запуск:

bash
./myproject.pex

Как объясняется в документации PEX, «проекты, указывающие console_scripts в своей конфигурации, могут создавать автономные исполняемые файлы для этих точек входа».


Устранение распространённых проблем

Проблема: Отсутствие локальных модулей

Проблема: В вашем PEX‑файле отсутствует код проекта.

Решение: Всегда используйте -f ., чтобы включить текущий каталог. Согласно исследованиям, «это было сложно понять по нескольким причинам. Изучив документацию, я смог вывести, что правильная команда в моём случае должна выглядеть примерно так: pex .».

Проблема: Точка входа не найдена

Проблема: Вы получаете ошибки «module not found» при запуске PEX.

Решение: Убедитесь, что синтаксис точки входа правильный:

  • Используйте -e project для всего модуля.
  • Используйте -e project:main для конкретной функции.
  • Проверьте, что имя модуля соответствует структуре файлов.

Проблема: Подмодули недоступны

Проблема: Подмодули не импортируются при запуске из PEX.

Решение: Убедитесь:

  1. Каждый подмодуль имеет файл __init__.py.
  2. Точка входа корректно импортирует и использует подмодули.
  3. Вы используете -f ., чтобы включить весь локальный код.

Как отмечено в GitHub issues, «я не могу динамически импортировать подмодули внутри PEX, потому что моя функция обнаружения ищет локальную файловую систему, а не папки внутри PEX».


Продвинутые техники для сложных проектов

Множественные точки входа

Если вам нужны несколько точек входа, укажите их в setup.py:

python
entry_points={
    'console_scripts': [
        'tool1=project:function1',
        'tool2=submodule1:function2',
    ],
}

Использование PEX_PATH для внедрения модулей во время выполнения

Для сложных сценариев вы можете использовать переменную окружения PEX_PATH, как упомянуто в документации PEXI: «Добавлена поддержка переменной окружения PEX_PATH, которая позволяет объединять среды PEX во время выполнения. Это можно использовать для внедрения плагинов, точек входа или модулей из одного PEX в другой без явной сборки их вместе».

Создание из виртуального окружения

Если у вас уже есть виртуальное окружение, вы можете заморозить его в PEX:

bash
pex -r <(pip freeze) -f . -e project -o myproject.pex

Как отмечено в документации PEX, «вывод pip freeze (список закреплённых зависимостей) можно передать напрямую в pex. Это удобно для заморозки виртуального окружения в PEX‑файл».


Заключение

Чтобы успешно упаковать ваш Python‑проект с подмодулями в один PEX‑файл:

  1. Используйте -f . – это самый критический шаг, который вы пропустили.
  2. Настройте правильные точки входа с помощью -e project:main или -c myproject.
  3. Включите setup.py с корректной конфигурацией пакета.
  4. Используйте правильную структуру команды: pex -f . -r requirements.txt -e project:main -o myproject.pex.
  5. Проверьте ваш PEX, чтобы убедиться, что все подмодули доступны.

Ключевой вывод: PEX требует явных инструкций для включения вашего локального кода. Без -f . он будет упаковывать только зависимости, но не ваш фактический проект. После освоения этого шаблона вы сможете создавать надёжные, портативные Python‑исполняемые файлы, которые работают точно так же, как python project.py, но со всеми зависимостями в одном файле.


Источники

  1. Документация PEX – Создание .pex файлов
  2. Stack Overflow – Упаковка локального модуля с pex
  3. CopyProgramming – Руководство по упаковке PEX и локальных модулей
  4. PEPI – Документация PEX 2.0.3
  5. GitHub – pex-tool/pex: Динамический импорт внутри PEX не работает
  6. Medium – Упаковка кода с PEX — пример PySpark
Авторы
Проверено модерацией
Модерация