Package: cmdline
cmdlineThe cmdline package provides a standard way to specify and override command settings and logging configuration.  Using cmdline setttings is as simple as creating asettings.yml:REMOTE_ADDR:
  default: 'https://example.com/'
  help: the remote addressand compiling the settings in your main program:from cmdline import SettingsParser, settings


def main():
    SettingsParser.compile_settings()

    remote = settings.REMOTE_ADDR

    print('remote addr:', remote)


if __name__ == '__main__':
    sys.exit(main())Your program can then be called in any of the following ways:$ remote
remote addr: https://example.com/
$ export REMOTE_ADDR=https://env.example.com/
$ remote
remote addr: https://env.example.com/
$ remote --remote-addr=https://arg-takes-precedence.example.com/
remote addr: https://arg-takes-precedence.example.com/Installationpip install cmdlineSettingsSettings are configured in an overlaid fashion starting with a root configuration packaged with the application.  Standardargparseoptions are used to configure settings.Override defaultsThe packaged settings can be overridden by additional settings files on the filesystem, environment variables, and finally command line arguments.  For a command namedremoteorder in which they are applied are:packaged settings<sys.prefix>/etc/remote/settings.yml~/.remote/settings.ymlenvironment variablescommand line argumentsEnvironment variables map exactly to the name of the setting, i.e. the environment variableREMOTE_ADDRconfigures theREMOTE_ADDRsetting.Command line arguments are lowercase-dashed versions of the setting, for instance, the command line argument--remote-addrconfigures theREMOTE_ADDRsetting.SubcommandsThe settings parser supportsargparse subcommands.  A setting can be configured to be for a subcommand by adding a_subcommandkey to the setting's options.  For example:COPY_FORCE
  default: no
  _subcommand: copyThe_SUBCOMMANDsetting contains the name of the subcommand the command was run with.DocumentationCommand descriptions and help text can be added in a_COMMANDSsection.  The_mainwill set the description for the main program.  Any other keys will correspond to a subcommand by the same name.  For example, below sets the description for the main program and the description and help text for thecopysubcommand:_COMMANDS:
  _main:
    description: >
      this is main description and can be a very long string
      that covers multiple lines
  copy:
    description: >
      this is copy subcommand description and can be a very long string
      that covers multiple lines
    help: copy filesType conversionSettings can be converted to a particular type using argparse'stypesetting.  For example settingtype: intwill convert the setting into an integer.  This is done by using Python's built-inint()function.Whentypeis a dotted string, the given function will be imported and used.  For example, settingtype: convesion.convert_boolwill call theconvert_bool()function in theconversionpackage.LoggingSimilarly, logging is configured through Python'slogging.config.dictConfig()function.  More info on dictconfig is atdocs.python.org; an example is below.  This configuration sets aconsolehandler that sends logs to stdout and anullhandler that throws away logs.  The default logging configuration is to log WARN level and higher messages to the console, which is setup by therootlogger, and two additional loggers are configured for themypkg.fooandmypkg.barloggers, where the loglevel formypkg.foois set to DEBUG andmypkg.baris thrown out altogether.version: 1
disable_existing_loggers: False

formatters:
  simple:
    format: "%(asctime)s %(name)s:%(lineno)d %(levelname)s %(message)s"


handlers:
  console:
    class: logging.StreamHandler
    level: WARN
    formatter: simple
    stream: ext://sys.stdout

  "null":
    class: logging.NullHandler


loggers:
  mypkg.foo:
    level: DEBUG
    handlers: [console]
    propagate: no

  mypkg.bar:
    handlers: ["null"]
    propagate: no


root:
  level: WARN
  handlers: [console]Once the configuration is written, logging is configured by calling thesetup_logging()function:import logging
from cmdline import setup_logging


def main():
    setup_logging()

    logging.getLogger(__name__).warning('ello')


if __name__ == '__main__':
    sys.exit(main())Log level environmentThe only logging configuration that can be updated outside the configuration file is the default log level, which can be changed with the environment variableLOG_LEVEL; for example,LOG_LEVEL=debug remote.File locationCreate a directory namedconfig.  In that directory, create the fileslogconfig.ymlandsettings.yml.  A bare-bonessetup.pythat installs these files correctly is below:#!/usr/bin/env python 
import os

from setuptools import setup


def get_data_files(base):
    for dirpath, dirnames, filenames in os.walk(base):
        for filename in filenames:
            yield os.path.join(dirpath, filename)


data_files = [
    ('config', list(get_data_files('config'))),
]

setup(name='remote',
      version='0.0.1',
      packages=['remote'],
      data_files=data_files,
      entry_points = {
          'console_scripts': [
              'remote = remote.command:main',
          ],
      },
)The file structure for thesetup.pyabove looks like:root_dir/
+- remote/
|  +- __init__.py
|  +- command.py
+- config/
|  +- logconfig.yml
|  +- settings.yml
+- setup.pyConfig root environment variableSetting the environment variableCMDLINE_CONFIG_ROOTwill make the given path the primary config location for settings and logging.