28 A CLI application starter template Here’s an example which shows how you can: • Use a logging level based on command-line arguments • Dispatch to multiple subcommands in separate files, all logging at the same level in a consistent way • Make use of simple, minimal configuration Suppose we have a command-line application whose job is to stop, start or restart some services. This could be organised for the purposes of illustration as a file$_{app.py}$ that is the main script for the application, with individual commands implemented in$_{start.py}$ ,$_{stop.py}$ and$_{restart.py}$ . Suppose further that we want to control the verbosity of the application via a command-line argument, defaulting to$_{logging.INFO}$. Here’s one way that app.py could be written: And the$_{start}$,$_{stop}$and$_{restart}$commands can be implemented in separate modules, like so for starting: (continues on next page) 41 # start.py import logging logger=logging.getLogger(__name__ ) import argparse import importlib import logging import os import sys def main(args= None ): scriptname=os.path.basename( __file__ ) parser =argparse.ArgumentParser(scriptname) levels=('DEBUG','INFO ','WARNING ','ERROR ','CRITICAL ') parser.add_argument( '--log-level ', default ='INFO ', choices=levels) subparsers =parser.add_subparsers(dest ='command', help='Available commands: ') start_cmd=subparsers.add_parser( 'start', help='Start a service') start_cmd .add_argument( 'name', metavar ='NAME ', help='Name of service to start') stop_cmd=subparsers.add_parser( 'stop', help='Stop one or more services') stop_cmd .add_argument( 'names', metavar ='NAME ', nargs ='+', help='Name of service to stop') restart_cmd=subparsers.add_parser( 'restart', help='Restart one or more services') restart_cmd .add_argument( 'names', metavar ='NAME ', nargs ='+', help='Name of service to restart') options =parser.parse_args() # the code to dispatch commands could all be in this file. For the purposes # of illustration only, we implement each command in a separate module. try : mod =importlib.import_module(options .command) cmd =getattr(mod,'command') except (ImportError,AttributeError): print('Unable to find the code for command \' %s \' '%options.command) return 1 # Could get fancy here and load configuration from file or dictionary logging.basicConfig(level =options.log_level, format=' %(levelname)s %(name)s %(message)s ') cmd(options) if __name__ =='__main__ ': sys.exit(main())