The other day I decided to try to assemble my freshly baked bot for Telegrams into an executable file. There are various solutions:
py2exe ,
pyInstaller ,
cx_Freeze . I use
Ubuntu 14.04 both on my computer and on the server, so the choice fell on
cx_Freeze , due to its cross-platform and
Python 3 support. (At the time of this writing, I have not yet had time to
try out
pyInstaller , which has the same properties. If there is something interesting, I will tell about it in another article).
Cx_Freeze installation
Initially, it seemed that everything was trivial:
sudo apt install cx-freeze
Not suitable because it installs cx_Freeze for
Python2 .
Let's try through pip3:
sudo pip3 install cx_Freeze
However, as if not so. On Ubunt (I have not tried other OSes yet) this causes the following error (I apologize for the pictures. At the time of writing, it was difficult to make a text copy-paste for reasons from the “this is a long history” series):

')
Having rummaged through StackExchange, I realized that without installing from the
sources, it would not do.
Downloaded. Unpacked. Launched:
sudo python3 setup.py install
The story is similar:

Rummaged through
StackOverflow again. I had to get into
setup.py and replace the line there:
if not vars.get("Py_ENABLE_SHARED", 0):
on
if True:
I tried to start again - and it was installed! Hooray!
On a note
While writing the article, I repeated the installation process on a “fresh” virtual machine with Ubuntu 14.04 32bit. I ran into an error:
/ usr / bin / ld: cannot find -lz
According to
this , it is solved (on 32-bit systems) using:
sudo apt-get install lib32z1-dev
And also, just in case (since it was mentioned in
this question ), I installed
python-dev :
sudo apt-get install python-dev
Putting the script!
Consider the simplest way to build a script using cx_Freeze. Go to the folder with the scripts and run:
cxfreeze main.py --target-dir build
where
main.py is the name of the main (launched) script, and
build is the folder where the executable file and libraries will go.
It should be noted that, unlike, say, C ++ compilers, cx_Freeze does not catch errors. Therefore, it can perfectly collect everything, but when you run the executable file, exceptions will sprinkle, and in a rather illegible format. I advise you to test the code while it is still pythonic.
I think that optimizing the code for building into an executable file is a topic for a separate discussion, because when building, problems that couldn’t have happened when running the script through the Python interpreter could come up. Here I will talk about one (thank God, one! No more just arose) such a problem.
My script accesses external settings files. But when launched from an executable file, for some reason he began to perceive the name of the script as one of the folders on the way to the settings file. That is, if the assembled executable file called
main is in
/ tmp / 001 , and the settings file is in
/ tmp / 001 / sett , the script thought that the settings file is actually located in
/ tmp / 001 / main / sett . That, naturally, is nonsense. And if you run the python script - this problem does not exist.
The fact is that at the beginning of my programs I often cram the following line:
SCRIPT_FOLDER = path.dirname(path.realpath(__file__))
I then use this variable when setting paths to files that are always in the same place
relative to the script : settings files, save, tokens, application codes, etc. Thus, I guarantee that the program reads the file from the right place regardless of where it was launched from (for example, from another folder or through a daemon).
Scripts collected through cx_Freeze usually either incorrectly interpret the global variable __file__ or do not know it at all. The latter just happened when I tried to build and run the executable file from a simple test script:
from os import path SCRIPT_FOLDER = path.dirname(path.realpath(__file__)) print(SCRIPT_FOLDER)
The problem turned out to be
known . It was enough to slightly patch my program:
if getattr(sys, 'frozen', False):
As a result, everything started with half a pint! Fine. The simple part is complete! Go to repeating all this in a virtual environment!
Oh, those virtual environments!
If you do not use
virtualenv , I think you are not very interested in further reading this article. The peculiarity lies not only in installing cx_Freeze on Wednesday, but also in running it in such a way that it uses dependencies from the environment, and not system dependencies.
On a note
We will not have to use the environment in the shell (run through “source env / bin / activate”). I tried, it did not give effect.
Installation
My virtual environment is in the
env folder in the main directory of my project. To install cx_Freeze on Wednesday, go to the source folder cx_Freeze (with the
setup.py file processed by the above method) and run:
/ path / to / my / project / env / bin / python3 setup.py install
Not very comfortable. I had to prescribe the path to the interpreter of Python in our environment. But everything was established.
Assembly
The single line method described above will not work here. It is necessary to use another - through the installation file.
This file, in short form, which was quite sufficient for me, looks like this:
from cx_Freeze import setup, Executable PROGRAM_NAME = "Train delays bot" VERSION = "0.0.5" MAIN_SCRIPT_NAME = "train_delays_bot_main.py" setup( name = PROGRAM_NAME, version = VERSION, executables = [Executable(MAIN_SCRIPT_NAME)])
The most important thing here is to set MAIN_SCRIPT_NAME, which corresponds to the name of the main file of our program.
We place this script in the project folder and go there. Run:
env / bin / python3 setup.py install
The
build folder was automatically created, and in it another one is
exe.linux-x86_64-3.4 (in your case, it may be called differently, depending on the OS and version of the interpreter). It contains the executable file and the library. I transferred there folders with files of tokens and settings. Everything started. And versions of dependences appeared just from the virtual environment. That can not but rejoice, especially after two hours of
travidroma of hard work.
Finally
And instead of a disclaimer. In no case do I claim to be absolute correctness and maximum convenience of the above methods. If you have more effective methods, tell us about them in the comments. I think everyone who has mastered my writings will be interested.