Script, Library, or Executable: You can have it all!
Sometimes a stand-alone script contains useful code to share across projects so you refactor into an importable library. After that library gains traction some less tech-savvy users want the functionality in a GUI. It can be difficult for beginner or intermediate Python developers to structure a Python package that can provide a good interface to CLI users, developers, and GUI lovers. This talk will describe one potential project layout, API guidelines, and tools to easily grow a project to support all of these.
Tags: Programming, Python
Scheduled on wednesday 16:00 in room lounge
Over the last 13 years, Luke Lee has professionally written software for applications ranging from Python desktop and web applications to embedded C drivers for Solid State Disks. Currently, he writes Python desktop and web applications for clients in the Oil and Education industries.
His enthusiasm for Python is emphasized throughout his presentations at several Python related conferences including Pycon, PyTexas, and PyArkansas. He also maintains a technical blog at www.lukelee.me/blog
I will describe one possible way to achieve this using the following features:
* Argparse based CLI script * Using Gooey to wrap CLI API for a GUI * Pyinstaller to package with no external dependencies
We want to create a package layout that can support a CLI interface, an importable library, and a GUI all while sharing as much code as possible.
Although text and graphical interfaces are very different we can provide a consistent API with careful consideration. This way users can easily use our library or either interface without starting all over again.
- Use the same terminology in each entry point (CLI/GUI) - Allow all the same inputs to be passed to a single functional-entry point when imported as a library
Step 1: CLI
First we will layout a single-file CLI script using argparse similar to the Unix
wc tool that takes a text file and outputs the following information:
1. Number of words 2. Number of lines 3. Rough estimate of reading time
We'll discuss the
__name__ == __main__ Python idiom, separating the argument parsing from the main function, and why keeping as little as possible in
__main__ is better for reusability.
There are several pros and cons to providing others with a single-file script. It's easy to develop and simple to read, but it requires any user to have the correct version of Python installed. It's also difficult for other developers to reuse the code in their own projects or deploy to PyPI.
Step 2: Add library interface
Next we'll take our single-file script and expand it into a basic Python package using a main folder, init.py, and a cli.py script to expose the same CLI as before.
We'll discuss how to restructure the main and parsing functions from step 1 into an public API defined by the init.py that exposes the same CLI functionality as a library.
We won't dive into setup.py at all, but there will be links and a brief description on the various tools to layout a package such as cookiecutter and setup.py.
Step 3: Add Gooey interface
The Gooey project can easily expose a CLI as GUI with a few decorators. We'll discuss briefly how to use Gooey and some of the extra functionality it provides to create more advanced GUIs.
We'll also give a simple mental model for how it maps argparse argument types to GUI widgets.
Step 4: PyInstaller
Until step 3 all we required of users was a working Python 3 installation. However, adding Gooey requires users to have a working Python installer, the Gooey library, and wxPython. Typically GUIs are meant for higher-level users so asking them to install all of this to benefit from our little app is too much.
Instead we'll see how we run PyInstaller on our entry script to package up all our dependencies including Python itself into a simple executable. We'll briefly discuss the build and dist output folders from PyInstaller along with the ability to use it to package all sorts of complicated Python applications using Qt, Numpy, etc.
End-users in management don't even have to know we used Python!