.. _python-scripts-as-executables: 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``; .. code:: if __name__ == '__main__': print('Hello world') Construction of an executable file looks like this; .. code:: $ 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; .. code:: python 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; .. code:: $ dist/multi { "value": [ "ansar.create.lifecycle.Faulted", { "condition": "cannot resolve executable \"hello\" from \"/home/dennis/test\"" }, [] ] } The :class:`~.processing.Process` is complaining about the supplied executable name. Adding the following environment adjustment exposes the underlying problem; .. code:: $ PATH="${PWD}/dist:${PATH}" dist/multi Hello world { "value": [ "ansar.create.lifecycle.Ack", {}, [] ] } With the proper PATH information in place, the :class:`~.processing.Process` object successfully finds the ``hello`` executable. The rather garbled output is a symptom of combining the use of ``print`` and :class:`~.processing.Process`, and is for demonstration purposes only. Ansar async processes use built-in logging routines for "console" output.