Skip to content

Instantly share code, notes, and snippets.

@Cartroo
Last active October 4, 2022 10:32
Show Gist options
  • Save Cartroo/3b33fbdae9d998424bb4598c33c9f8cf to your computer and use it in GitHub Desktop.
Save Cartroo/3b33fbdae9d998424bb4598c33c9f8cf to your computer and use it in GitHub Desktop.
A useful skeleton template for Python command-line applications, which sets up logging and provides useful command-line options to control it.
#!/usr/bin/env python3
import argparse
import logging
import logging.config
import logging.handlers
import os
import sys
from typing import Any
CONSOLE_LOG_FORMAT = "%(asctime)s.%(msecs)03d [%(levelname)s] --- %(message)s"
CONSOLE_TIME_FORMAT = "%H:%M:%S"
def get_argument_parser() -> argparse.ArgumentParser:
"""Set up command-line argument parser."""
prog_name = os.path.basename(sys.argv[0]) or "app.py"
parser = argparse.ArgumentParser(prog=prog_name)
parser.add_argument(
"-d",
"--debug",
dest="log_level",
action="store_const",
const=logging.DEBUG,
help="Show debug output",
)
parser.add_argument("-l", "--log-file", help="Log to file instead of console")
parser.add_argument(
"-q",
"--quiet",
dest="log_level",
action="store_const",
const=logging.WARNING,
help="Show only warnings and errors",
)
parser.set_defaults(log_level=logging.INFO, log_file=None)
return parser
# -------- MAIN APPLICATION
def application_main(args: argparse.Namespace) -> int:
"""Application main entrypoint."""
return 0
# -------- END MAIN APPLICATION
def get_logging_config(args: argparse.Namespace) -> None:
"""Configure logging based on command-line parameters."""
# Basic logging configuration skeleton
config_dict: dict[str, Any] = {
"version": 1,
"formatters": {
"console": {"format": CONSOLE_LOG_FORMAT, "datefmt": CONSOLE_TIME_FORMAT},
"logfile": {"format": "%(asctime)s %(name)s %(levelname)s: %(message)s"},
},
"handlers": {},
"root": {"level": 0, "handlers": []},
}
# Customise logging configuration based on command-line arguments.
if args.log_file is None:
config_dict["handlers"]["console"] = {
"class": "logging.StreamHandler",
"formatter": "console",
"level": args.log_level,
"stream": "ext://sys.stdout",
}
config_dict["root"]["handlers"].append("console")
else:
config_dict["handlers"]["logfile"] = {
"class": "logging.handlers.TimedRotatingFileHandler",
"formatter": "logfile",
"level": args.log_level,
"filename": args.log_file,
"when": "midnight",
"backupCount": 30,
}
config_dict["root"]["handlers"].append("logfile")
# Implement configuration
logging.config.dictConfig(config_dict)
def main(argv: list[str]) -> int:
"""Initialise logging and parse command-line arguments."""
# Default logging so we can start logging straight away
logging.basicConfig(
format=CONSOLE_LOG_FORMAT, datefmt=CONSOLE_TIME_FORMAT, level=logging.INFO
)
parser = get_argument_parser()
args = parser.parse_args(args=argv[1:])
get_logging_config(args)
return application_main(args)
if __name__ == "__main__":
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment