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.