[Из песочницы] Как преподнести проект на Python рядовому пользователю без компиляторов и костылей, или подстраиваем велосипед под седока

Здравствуй, читатель.Я люблю Python таким, какой он есть: шустрый, лаконичный, имеет из-под коробки очень много полезных вещей (например, можно узнать, является ли число полиморфом всего в 1 строку).На нем в данный момент пишу клеточный автомат — упрощенное воссоздание жизни в чашке Пельтье. 5 классов живых существ, наследственность, влияние «силы водорода» на жизнь и развитие клетки. Но это уже другая история.

Собственно, это не осталось незамеченным со стороны окружающих. Появились люди, которые хотели «погонять» за свою колонию цианобактерий и вывести их в элиту своей чашки, вытеснив других. Но тут проявляется, по мнению некоторых, не очень хорошая особенность данного ЯП — он (почти) не компилируем — приходится с собой таскать 80 мегабайт дополнительного груза. А что если…Цель — сделать Python более добрым по отношению к простому смертному пользователю, то есть: — не принуждаем юзера ставить среду; — не заставляем качать большие файлы (> 25 мб); — не посылаем танцевать с бубнами, «нажми там, затем нарисуй пятиконечную звезду»; — не теряем скорость выполнения приложения. Лишь чуток подождать в начале запуска.

Для своих целей я избрал вот такую алхимию:

1) Берем девственно чистый Python (в статье используется Python 3.4, но вас ничто не сдерживает использовать более поздние\ранние версии), без Tkinter, IDLE, pydoc, тестов;

Спойлер e95134e9ae8e49a7a207c58e6a75926f.png 2) Добавляем в него дополнительные (не стандартные, + нами специально изъятые) нужные модули, использующиеся в приложении, добавляем их в »/lib/» директорию. Помещаем в отдельную директорию папку с интерпретатором + исполняемый скрипт;3) Сжимаем; Осторожно, нецензурная лексика! be9db4e1cf244995a80a917801577085.jpeg 4) Создаем на компилируемом ЯП (самое простое — pascal) маленькую программку, которая расшакалит распакует это дело уже на компьютере конечного пользователя и запустит;5) Задача, выполнена, my master! Пункты 1 и 2. Чистим Python Этот пункт можно проделать и самостоятельно. Выполняем их для уменьшения размера готового велосипеда. (В развернутом состоянии интерпретатор у меня весит 60,9 мб, в сжатом — всего 11,9 мб, то есть сжатие составляет 80,5%, что немаловажно).Осторожно, нецензурная лексика! 295c73aa98c34809bb042a6834997b46.jpg Ссылка на Dropbox. (12 мб)Затем складываем zip архив, файл (.py или .pyc) и сопутствующие файлы (картинки, ресурсы и проч.) в одну папку. Важно! Основной файл python должен называться main.py или main.pyc во избежание путаницы с другими .py или .pyc файлами вашего проекта.

Получится что-то похожее:

dir_test/… python.zip… main.py… image.bmp… README.txt

Пункт 3 Теперь сжимаем всё содержимое папки (!!!) в zip файл. Зачем мы снова архивируем интерпретатор? Интерпретатор мы сжимаем с высокой степенью сжатия и, если мы при каждой пробе будем заново архивировать его, то будет уходить довольно много времени.Пункт 4 Теперь остается написать программу на pascal/delphi: Код : program python;

{$APPTYPE CONSOLE}

{$R *.res}

uses System.SysUtils, System.Zip, ShellAPI;

var a, path: string; zip: TZipFile; i, s, p: integer; begin try // если уже проводилась распаковка… if fileexists ('.sourse\\main.py') or fileexists ('.sourse\\main.pyc') then begin writeln ('Starting…'); if fileexists ('.sourse\\main.py') then ShellExecute (0, nil, PChar ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\python\\py.exe»'), PChar (string ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\main.py»')), '', 1)

else if fileexists ('.sourse\\main.pyc') then ShellExecute (0, nil, PChar ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\python\\py.exe»'), PChar (string ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\main.pyc»')), '', 1) else begin write ('File not found! Press any key…'); read (i); end;

exit (); end;

//распаковываем zip-файл zip:= TZipFile.Create; zip.UTF8Support:= True; s:= 0; // индикатор наличия main файла p:= 0; //индикатор наличия python zip.Open ('sourse.zip', zmRead); for I:= 0 to zip.FileCount — 1 do begin writeln (zip.FileNames[i], ' extracting…'); if zip.FileNames[i] = 'main.py' then s:= 1; if zip.FileNames[i] = 'main.pyc' then s:= 2; if zip.FileNames[i] = 'python.zip' then p:= 1; zip.Extract (zip.FileNames[i], '.sourse'); end; zip.Close; DeleteFile ('sourse.zip'); //начинаем работу с распаковкой пайтона write ('Extracting Python, it may take a few seconds…'); zip.Open ('.sourse\\python.zip', zmRead); zip.ExtractAll ('.sourse\\python'); write ('Done!'+#10#13 +'Starting…'); zip.Destroy (); DeleteFile ('.sourse\\python.zip'); //проверки if s = 0 then //если в ходе распаковки основной файл не был обнаружен begin write ('No .py or .pyc file!', #10#13, 'Press any key…'); read (a); exit (); end; if p = 0 then //если в ходе распаковки python не был обнаружен begin write ('No Python!', #10#13, 'Press any key…'); read (a); exit (); end; //сам запуск if s = 1 then ShellExecute (0, nil, PChar ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\python\\py.exe»'), PChar (string ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\main.py»')), '', 1) //PChar (string (ExtractFilePath (ParamStr (0))+'\\.sourse\\main.py')) else ShellExecute (0, nil, PChar ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\python\\py.exe»'), PChar (string ('»'+ExtractFilePath (ParamStr (0))+'\\.sourse\\main.pyc»')), '', 1); //PChar (string (ExtractFilePath (ParamStr (0))+'\\.sourse\\main.py'))

exit (); except //на случай, если что случится on E: Exception do begin Writeln (E.ClassName, ': ', E.Message); write ('Broken! Press any button…'); read (a); end; end; end. Программа весит 559 кб, что очень недурно.Для проверки напишем скрипт-пробник на python:

Скрипт python print ('Привет! Я — переносной скрипт python и я занимаю в запакованном виде менее 20 мб!') print ('Сейчас я попытаюсь импортировать модуль random…')

try: import random print ('Хех, мне это удалось!') print ('Держи псевдослучайное число :', random.randrange (0,100)) except: print ('%(Не получилось')

ragnarok = input ('Нажмите любую кнопку…') Всё это весит в запакованном виде 12 мегабайт (но по увеличению количества кода вес не будет существенно меняться) и работает быстро — по тестам первая распаковка длится не более 4 секунд, а последующие запуски — менее полсекунды.В развернутом виде мы получаем 60,9 мегабайта.

Ссылка на тест: https://www.dropbox.com/s/17xdpeju7u0oieh/python%20test.zip? dl=0 (Dropbox, 12 мб)

Подведем итог Мы способны полноценно запускать python приложения на компьютерах под управлением Windows, где не стоит интерпретатор Python (также в ситуациях с установленной 2.x версией), увеличили мобильность приложения, не теряя скорости выполнения.Спасибо, что дочитали мою статью. У меня мало опыта в таком деле, но я буду стараться! Следующей будет статья про клеточный автомат.

© Habrahabr.ru