Упаковываем ботов правильно: файлы .ini и .pyz

В Python 3.5 появился модуль zipapp, позволяющий "упаковать" каталог с кодом в один исполняемый интерпретатором файл с расширением .pyz. К слову, запускать (но без создания) могут версии Python, начиная с 2.6.

Казалось бы, всё отлично, давайте упаковывать каждого бота в один исполняемый файл и не заморачиваться с пересылкой архивов с кучей файликов внутри. Но внимательный читатель заметит, что прежде для конфигурирования ботов мы создавали отдельный файл config.py, который при использовании упаковщика, очевидно, попадет внутрь pyz-архива. В этом нет никакой проблемы, если бот создается исключительно для себя, но при выкладывании исходников в общий доступ, надо что-то придумать, чтобы конечный пользователь мог вписать нужный токен и вообще указать необходимые параметры. И тут на помощь придёт модуль configparser. Я не буду тратить время на описание формата ini-файла, который использует этот модуль, желающие могут пройти по ссылке и лицезреть несколько простых и доступных примеров.

Итак, давайте создадим простого бота на поллинге, упакуем его, создадим файл конфигурации и проверим. Для пущего разнообразия, все действия будут производиться в ОС Windows.

Создание файла конфигурации

Давайте создадим файл config.ini следующего содержания:

[DEFAULT]
Token = 1234567:ABCDEFGHIJKlmnoPQrsTuvWXYz
[Bot Specific]
Answer message = 123

Пусть для примера у нас будет две секции: [DEFAULT] (по умолчанию) и [Bot Specific]. Для тех, кто всё-таки не стал читать документацию по configparser, укажу, что названия секций в коде case-sensitive, а названия ключей – нет.

Пишем код

Чтобы показать все прелести упаковки кода в pyz-файл, создадим проект с такой структурой (уточню: файл another.py лежит в каталоге other):

|other
|__another.py
|bot.py
|requirements.txt

Файл another.py будет очень простым – всего лишь вывод строки:

# -*- coding: utf-8 -*-
def just_print():
return "Hello world!"

Файл bot.py будет ненамного сложнее: создадим функцию, которая будет реагировать только на сообщение, текст которого указан в конфигурационном файле. Обратите также внимание на последние строки, если вам вдруг захочется вынести содержимое функции main() в последний if – не делайте этого, позднее увидите, почему.

# -*- coding: utf-8 -*-
import telebot
import configparser
from other.another import just_print
config = configparser.ConfigParser()
config.read("config.ini")
bot = telebot.TeleBot(config["DEFAULT"]["Token"])
@bot.message_handler(func=lambda message: message.text == config["Bot Specific"]["answer message"])
def reply_to(message):
bot.send_message(message.chat.id, "Hello there!")
def main():
print(just_print())
bot.polling(none_stop=True)
if __name__ == '__main__':
main()

По-хорошему, конечно, надо проверять наличие файла config.ini, но для простоты кода мы этого делать не будем.

Давайте сделаем ещё вот что: создадим файл requirements.txt, в котором укажем, какие библиотеки нужны нашему боту, чтобы получатель мог одной командой установить все зависимости и радоваться жизни. В нашем случае в файл достаточно записать одну строку pytelegrambotapi>=1.4.2, т.к. эта библиотека потянет за собой необходимые боту requests, six и т.д.

Упаковка в pyz-файл

Настало время объединить все py-файлы в один исполняемый архив. Напоминаю, в этом уроке мы работаем в Windows. Предположим, что наш проект лежит в каталоге C:\project. Уточним сразу пару моментов: во-первых, нам необходимо указать имя главной вызываемой функции, в нашем случае это функция main() в модуле bot.py (именно поэтому я вынес код запуска бота в отдельный метод); во-вторых, при создании можно не указывать имя выходного файла, тогда в родительском каталоге будет создан файл с названием папки и расширением pyz, если вы хотите указать своё название, то расширение нужно будет ввести вручную и файл будет по умолчанию создан внутри упаковываемого каталога (но не в самом архиве, очевидно).

Итак, начинаем упаковку: C:\Python35\python.exe -m zipapp "C:\project" -m bot:main -o mybot.pyz. В результате получится файл C:\project\mybot.pyz. Как же теперь поступить человеку, который захочет запустить вашего бота у себя?

  1. Скачать себе в компьютер файлы mybot.pyz, config.ini и requirements.txt, например, в каталог D:\Bot

  2. Указать нужные параметры в файле config.ini.

  3. Установить зависимости командой C:\Python34\python.exe -m pip install -r requirements.txt. Заметьте, я намеренно назвал каталог Python34 вместо Python35 с намёком на более старую версию используемого интерпретатора. Python 3.5 нужен только для упаковки в pyz.

  4. Запустить бота командой C:\Python34\python.exe -m D:\Bot\mybot.pyz.

  5. (по желанеию) Радоваться жизни.

Собственно, это всё. Конечно, в нашем примере число файлов уменьшилось совсем незначительно, но для крупных ботов, когда число модулей превосходит десяток, такой метод упаковки может показаться крайне удобным.