Utilities for command-line programs (pwkit.cli)

pwkit.cli - miscellaneous utilities for command-line programs.

Functions:

backtrace_on_usr1 - Make it so that a Python backtrace is printed on SIGUSR1. check_usage - Print usage and exit if –help is in argv. die - Print an error and exit. fork_detached_process - Fork a detached process. pop_option - Check for a single command-line option. propagate_sigint - Ensure that calling shells know when we die from SIGINT. show_usage - Print a usage message. unicode_stdio - Ensure that sys.std{in,out,err} accept unicode strings. warn - Print a warning. wrong_usage - Print an error about wrong usage and the usage help.

Context managers:

print_tracebacks - Catch exceptions and print tracebacks without reraising them.

Submodules:

multitool - Framework for command-line programs with sub-commands.

pwkit.cli.check_usage(docstring, argv=None, usageifnoargs=False)[source]

Check if the program has been run with a –help argument; if so, print usage information and exit.

Parameters:
  • docstring (str) – the program help text

  • argv – the program arguments; taken as sys.argv if given as None (the default). (Note that this implies argv[0] should be the program name and not the first option.)

  • usageifnoargs (bool) – if True, usage information will be printed and the program will exit if no command-line arguments are passed. If “long”, print long usasge. Default is False.

This function is intended for small programs launched from the command line. The intention is for the program help information to be written in its docstring, and then for the preamble to contain something like:

"""myprogram - this is all the usage help you get"""
import sys
... # other setup
check_usage (__doc__)
... # go on with business

If it is determined that usage information should be shown, show_usage() is called and the program exits.

See also wrong_usage().

pwkit.cli.die(fmt, *args)[source]

Raise a SystemExit exception with a formatted error message.

Parameters:
  • fmt (str) – a format string

  • args – arguments to the format string

If args is empty, a SystemExit exception is raised with the argument 'error: ' + str (fmt). Otherwise, the string component is fmt % args. If uncaught, the interpreter exits with an error code and prints the exception argument.

Example:

if ndim != 3:
   die ('require exactly 3 dimensions, not %d', ndim)
pwkit.cli.fork_detached_process()[source]

Fork this process, creating a subprocess detached from the current context.

Returns a pwkit.Holder instance with information about what happened. Its fields are:

whoami

A string, either “original” or “forked” depending on which process we are.

pipe

An open binary file descriptor. It is readable by the original process and writable by the forked one. This can be used to pass information from the forked process to the one that launched it.

forkedpid

The PID of the forked process. Note that this process is not a child of the original one, so waitpid() and friends may not be used on it.

Example:

from pwkit import cli

info = cli.fork_detached_process ()
if info.whoami == 'original':
    message = info.pipe.readline ().decode ('utf-8')
    if not len (message):
        cli.die ('forked process (PID %d) appears to have died', info.forkedpid)
    info.pipe.close ()
    print ('forked process said:', message)
else:
    info.pipe.write ('hello world'.encode ('utf-8'))
    info.pipe.close ()

As always, the vital thing to understand is that immediately after a call to this function, you have two nearly-identical but entirely independent programs that are now both running simultaneously. Until you execute some kind of if statement, the only difference between the two processes is the value of the info.whoami field and whether info.pipe is readable or writeable.

This function uses os.fork() twice and also calls os.setsid() in between the two invocations, which creates new session and process groups for the forked subprocess. It does not perform other operations that you might want, such as changing the current directory, dropping privileges, closing file descriptors, and so on. For more discussion of best practices when it comes to “daemonizing” processes, see (stalled) PEP 3143.

pwkit.cli.pop_option(ident, argv=None)[source]

A lame routine for grabbing command-line arguments. Returns a boolean indicating whether the option was present. If it was, it’s removed from the argument string. Because of the lame behavior, options can’t be combined, and non-boolean options aren’t supported. Operates on sys.argv by default.

Note that this will proceed merrily if argv[0] matches your option.

class pwkit.cli.print_tracebacks(types=(<class 'Exception'>, ), header=None, file=None)[source]

Context manager that catches exceptions and prints their tracebacks without reraising them. Intended for robust programs that want to continue execution even if something bad happens; this provides the infrastructure to swallow exceptions while still preserving exception information for later debugging.

You can specify which exception classes to catch with the types keyword argument to the constructor. The header keyword will be printed if specified; this could be used to add contextual information. The file keyword specifies the destination for the printed output; default is sys.stderr.

Instances preserve the exception information in the fields ‘etype’, ‘evalue’, and ‘etb’ if your program in fact wants to do something with the information. One basic use would be checking whether an exception did, in fact, occur.

pwkit.cli.show_usage(docstring, short, stream, exitcode)[source]

Print program usage information and exit.

Parameters:

docstring (str) – the program help text

This function just prints docstring and exits. In most cases, the function check_usage() should be used: it automatically checks sys.argv for a sole “-h” or “–help” argument and invokes this function.

This function is provided in case there are instances where the user should get a friendly usage message that check_usage() doesn’t catch. It can be contrasted with wrong_usage(), which prints a terser usage message and exits with an error code.

pwkit.cli.unicode_stdio()[source]

Make sure that the standard I/O streams accept Unicode.

This function does nothing, because we have dropped support for Python 2. In Python 3 it is not necessary.

In Python 2, the standard I/O streams accept bytes, not Unicode characters. This means that in principle every Unicode string that we want to output should be encoded to utf-8 before print()ing. But Python 2.X has a hack where, if the output is a terminal, it will automatically encode your strings, using UTF-8 in most cases.

BUT this hack doesn’t kick in if you pipe your program’s output to another program. So it’s easy to write a tool that works fine in most cases but then blows up when you log its output to a file.

The proper solution is just to do the encoding right. This function sets things up to do this in the most sensible way I can devise, if we’re running on Python 2. This approach sets up compatibility with Python 3, which has the stdio streams be in text mode rather than bytes mode to begin with.

Basically, every command-line Python program should call this right at startup. I’m tempted to just invoke this code whenever this module is imported since I foresee many accidentally omissions of the call.

pwkit.cli.wrong_usage(docstring, *rest)[source]

Print a message indicating invalid command-line arguments and exit with an error code.

Parameters:
  • docstring (str) – the program help text

  • rest – an optional specific error message

This function is intended for small programs launched from the command line. The intention is for the program help information to be written in its docstring, and then for argument checking to look something like this:

"""mytask <input> <output>

Do something to the input to create the output.
"""
...
import sys
... # other setup
check_usage (__doc__)
... # more setup
if len (sys.argv) != 3:
   wrong_usage (__doc__, "expect exactly 2 arguments, not %d",
                len (sys.argv))

When called, an error message is printed along with the first stanza of docstring. The program then exits with an error code and a suggestion to run the program with a –help argument to see more detailed usage information. The “first stanza” of docstring is defined as everything up until the first blank line, ignoring any leading blank lines.

The optional message in rest is treated as follows. If rest is empty, the error message “invalid command-line arguments” is printed. If it is a single item, the stringification of that item is printed. If it is more than one item, the first item is treated as a format string, and it is percent-formatted with the remaining values. See the above example.

See also check_usage() and show_usage().