Python CLI Tamed — Invoked
I spent all that money on a graphical user interface, I bought a Macintosh in 1985, and now the latest version wants me to develop with just the 256 icons of ascii.
The terminal is required. Some employers expect a typing speed test and take exception to those with terminal phobia. I am ok with clis. I read the documentation and become proficient with them as tools. But there are so many and as I move from one to the other I forget the arguments and short cuts.
invoke
I have a solution. I have evolved a work practice that includes the package Invoke. Once you have mastered a tool, encapsulate your hard won knowledge in tasks and rule the day. The documentation is excellent, but let me give you a taste.
setup
mkdir tamed
cd tamed
python3.9 -m venv .
. bin/activate
pip install invoke
echo '""" our development tasks """' > tasks.py
echo 'from invoke import task' >> tasks.py
Here we create a directory called tamed
, move to that directory and install a virtual environment, which we activate. Then we ask pip
to install invoke
. Finally, we create a file called tasks.py
and place within a comment and import.
Calling invoke
now will result in:
% invoke --list
No tasks found in collection 'tasks'!
So let’s add some tasks.
tasks
First we need a hello world
:
You’ll notice the underscore first parameter, that’s the context and we’ll address it later. But for now, let’s see the help
for this task:
% inv -h hello
Usage: inv[oke] [--core-opts] hello [--options] [other tasks here ...]
Docstring:
print message
Options:
-m STRING, --message=STRING
Now, let’s call it:
%inv hello world
hello world
Since the terminal is now the window on your world we’ll need an essential tool to know what it’s like in there:
Some things to note here: we have a flag for the short format, we’ve added help to our task and we’re using the context. The context allows us to run subprocesses. Here we’re using curl. I’ve imported urllib.parse
at the top of the file and quote
what is passed in as the location. Short will be a boolean and we can test its truthiness
to append the format argument. Now who remembers that curl
takes the argument -s
for silent mode and not -q
for quiet mode - for that matter who knew you could get rid of the progress information? I called up the man
page to remember how to get rid of it for this article! Now I no longer have to remember as it’s encapsulated in my task! Oh, and the weather here:
% inv weather 'milford haven' -s
milford haven: ⛅ +13°C
the hard part
I recently was asked to build a python package with sub-packages that could be delivered independently, with their own versions. So I immediately went to the documentation and read up on sub-packages: Packaging namespace packages
From this I chose pkg_resources-style
namespace packages as it supports zip-safe
and we’d be using resources within the package such as templates and icons. The resultant directory structure looks like this:
And yet, properly installed, you import starwars.goongas.main
. When asked to add another sub-package… I have a task for that!
First we have to create a subdirectory within a package directory within a subdirectory of the package directory. The __init__.py
of the embedded package directory needs to declare a namespace and our version resides in the subpackage’s __init__.py
. Then we create a config file for bumpversion and a simple setup.py
, add our creation to dev.txt
, make a directory for the new tests and finally instruct the user to run pip
and start testing!
By now you must see that I could not possibly remember all that. But I put it in a task and have used it often on more than one project. It looks like I also use bumpversion.
Another useful parameter to task
is pre
; a chain of tasks to invoke before invoking this task. For example, a release
task might have pre=[clean, lint, test, build]
. If any task or ctx.run
fails the task exits. So if test fails release will never be invoked. Invoke, also, has a concept of collections
that allow you to create groups of tasks, for example: db tasks and documentation tasks. It’s worth a look: Loading collections.
Some do not like the underscore policy of invoke
, as in changing them to hyphens, and so there is an adaptation for that: raft
Going forward
I just used this subtitle to annoy my brother. In the Invoke
documentation it explains how to write your own command line tool. When you publish it, I will likely have to learn it, and encapsulate my usage of it in my own tasks, but have a look. Using Invoke as a library. I’m afraid Invoke
has not cured my terminal phobia, but the cli
is now my friend.