Python Scripts As Executables
Conversion of Python scripts into executable files is a simple, mechanical process. The pros and cons of why you might choose to do so are more interesting and may be useful background. Both topics are covered here.
Starting From Scratch
Assuming a valid installation of Python and the following script, hello.py;
if __name__ == '__main__':
print('Hello world')
Construction of an executable file looks like this;
$ python3 -m venv .env
$ source .env/bin/activate
$ pip install pyinstaller
$ pip install ansar-create
$ pyinstaller --onefile --log-level ERROR -p . hello.py
$ ls
build dist hello.py hello.spec
$ dist/hello
Hello world
$
The pyinstaller tool is downloaded and used to create the hello file, inside the local dist folder.
Running that executable produces the expected output. The arguments passed to pyinstaller are generally
about simplicity - loading everything into the one output file, limiting diagnostics to real errors and
providing a default search path for additional modules, i.e. the current folder.
A Few Reasons For Converting
Debates over scripts versus executable files might include points such as;
difficulties maintaining the same interpreter environment over time and across many hosts,
application portability
ease of use
integrity of the development, QA and production pipeline
intellectual property protection
At the root of most of the “cons” lie the difficulties of maintaining the same environment for that moment
when the Python interpreter is called on to run a script. The dependency tree that starts at the __main__
Python module and spreads out through import statements and the PYTHONPATH is extensive - it’s more Banyan
than Bonsai. Replication of exactly the same environment a few weeks later or on another developer’s machine,
or in production, is extremely difficult. All it takes is the presence of a different version of an obscure
library and the spell is broken. If a script travels from a development area through to QA and on to production,
how realistic is any claim that the application will always exhibit the same features and behaviours? Without
a guarantee the software pipeline is compromised.
There is also the issue of intellectual property - executable files might appear to bring a degree of protection against violation of copyright. This perceived benefit of using executable files is dubious as the final executable effectively contains a copy of all the scripts. Reverse engineering executables back to readable source code is a mechanical process for those motivated to do so.
Referencing Executables From Ansar
Using executables within the ansar framework is at the lowest levels, no different to other approaches. A name
is passed to some facillity (e.g. Popen()) and the executable is loaded from disk.
Ansar prefers the plain name of the file - there should be no relative or absolute path component and
no “dot extension”. The named file is expected to exist within one of the folders listed in $PATH.
Typically that does not include the current working folder. Adding the following script multi.py
to the files in the earlier paragraphs;
import ansar.create as ar
def multi(self):
self.create(ar.Process, 'hello', output=False)
self.select(ar.Completed)
if isinstance(m.value, ar.Faulted):
return m.value
return ar.Ack()
ar.bind(multi)
if __name__ == '__main__':
ar.create_object(multi)
Use pyinstaller to create the multi executable in the dist folder, alongside hello. With the
two executables in place the command line, as shown below, doesn’t quite go as planned;
$ dist/multi
{
"value": [
"ansar.create.lifecycle.Faulted",
{
"condition": "cannot resolve executable \"hello\" from \"/home/dennis/test\""
},
[]
]
}
The Process is complaining about the supplied executable name. Adding the following
environment adjustment exposes the underlying problem;
$ PATH="${PWD}/dist:${PATH}" dist/multi
Hello world
{
"value": [
"ansar.create.lifecycle.Ack",
{},
[]
]
}
With the proper PATH information in place, the Process object successfully finds
the hello executable. The rather garbled output is a symptom of combining the use of print
and Process, and is for demonstration purposes only. Ansar async processes use
built-in logging routines for “console” output.