mainland

“Namespaces are good, let’s do more of those!” – Zen of Python

Introduction

Commands are not naturally put in namespaces, because they are meant to be easily available to the user: nobody would want to type system.files.management.copy file1 file2 at the command-line. However, Python command-lines are often not intended to be directly used from an interactive shell at a high frequency. (An example might be a web-app server like gunicorn.)

However, the “console scripts” section of setup scripts will inject scripts directly into the dirtiest namespace of them all: the shell-commandline. Even if physically they will be in a separate directory, the shell’s PATH means that they can be hiding or be hidden by any command in thousand of packages on a modern UNIX or Windows box.

Python already owns one component of this namespace: python is Python. mainland is a way to build on this namespace by making it easy for packages to forge their own command-line namespace under python -m <packagename>.

A typical command, an example from mainland itself, is

python -m mainland tests.nitpicker

Note that this distinct from

python -m mainland.tests.nitpicker

which, as with most modules, do nothing.

Usage

python -m <packagename> will execute <packagename>.__main__. The goal of mainland is to make writing <packagename>.__main__ easy.

In fact, here is mainland’s __main__ file, annotated:

First, make sure that nobody can import the module:

if __name__ != '__main__':
    raise ImportError('module cannot be imported')

this reversal of the classic idiom makes the main module good for only one thing – direct command-line execution. The temptation to import it in the main code, or the possibility of doing it accidentally, is removed.

Then, import the sys and mainland modules

import sys
import mainland

The real fun comes later, when calling the only function defined in the mainland module:

mainland.main(
    root='mainland',
    marker='MAINLAND_MAIN_OK',
    argv=sys.argv,
)

The root is the name that should be prepended to module names on the command-line: in our case, it is ‘mainland.’ (the dot is automatically supplied if missing).

The marker attribute will be checked for existence and truthiness. If it does not exist or is false, the module will not be executed. This “off by default” means mainland can be added to projects, without allowing accidental calling of random modules’ main() functions.

Lastly, argv is passed in.

The only argument not documented here is suffix, an iterable of possible suffixes. The import will be tried with each suffix before succeeding. This means that mainland can be added to projects with pre-existing names of libraries ending in, say, lib, and still have nice command-lines.