Source code for chirptext.cli
# -*- coding: utf-8 -*-
"""
Command-line interface helper
"""
# This code is a part of chirptext library: https://github.com/letuananh/chirptext
# :copyright: (c) 2012 Le Tuan Anh <tuananh.ke@gmail.com>
# :license: MIT, see LICENSE for more details.
import os
import logging
import logging.config
import argparse
import json
# -------------------------------------------------------------------------------
# Configuration
# -------------------------------------------------------------------------------
class ChirpCLI(object):
SETUP_COMPLETED = False
def getLogger():
return logging.getLogger(__name__)
# -------------------------------------------------------------------------------
# Application logic
# -------------------------------------------------------------------------------
[docs]def setup_logging(config_path, log_dir=None, force_setup=False, default_level= logging.WARNING, silent=True):
""" Try to load logging configuration from a file. Set level to INFO if failed.
:param config_path: Path to the logging config file (JSON)
:param log_dir: Path to log output directory. When log_dir is not None and the directory does not exist, it will be created automatically.
"""
if not force_setup and ChirpCLI.SETUP_COMPLETED:
if not silent:
logging.debug("Master logging has been setup. This call will be ignored.")
return
if log_dir and not os.path.exists(log_dir):
try:
os.makedirs(log_dir)
except OSError:
if not silent:
logging.exception("Could not create logging folder")
if os.path.isfile(config_path):
with open(config_path) as config_file:
try:
config = json.load(config_file)
logging.config.dictConfig(config)
if not silent:
logging.info("logging was setup using {}".format(config_path))
ChirpCLI.SETUP_COMPLETED = True
except Exception:
if not silent:
logging.exception("Could not load logging config")
# default logging config
logging.basicConfig(level=default_level)
else:
logging.basicConfig(level=default_level)
[docs]def config_logging(args):
""" Override root logger's level """
if args.quiet:
logging.getLogger().setLevel(logging.CRITICAL)
elif args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
[docs]class CLIApp(object):
""" A simple template for command-line interface applications """
def __init__(self, desc, add_vq=True, add_tasks=True, **kwargs):
"""
Init an app instance
"""
self.__parser = argparse.ArgumentParser(description=desc, add_help=False)
self.__parser.set_defaults(func=None)
self.__add_vq = add_vq
self.__config_logging = config_logging
if 'config_logging' in kwargs:
self.__config_logging = kwargs['config_logging']
if add_vq:
self.add_vq(self.__parser)
self.__show_version_func = None
if 'show_version' in kwargs:
self.add_version_func(kwargs['show_version'])
if add_tasks:
task_desc = kwargs['task_desc'] if 'task_desc' in kwargs else 'Task to be done'
self.__tasks = self.__parser.add_subparsers(help=task_desc)
else:
self.__tasks = None
self.__logger = None
self.__name = kwargs['logger'] if 'logger' in kwargs else __name__
[docs] def add_task(self, task, func=None, **kwargs):
""" Add a task parser """
if not self.__tasks:
raise Exception("Tasks subparsers is disabled")
if 'help' not in kwargs:
if func.__doc__:
kwargs['help'] = func.__doc__
task_parser = self.__tasks.add_parser(task, **kwargs)
if self.__add_vq:
self.add_vq(task_parser)
if func is not None:
task_parser.set_defaults(func=func)
return task_parser
[docs] def add_vq(self, parser):
""" Add verbose & quiet options """
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
[docs] def add_version_func(self, show_version):
""" Enable --version and -V to show version information """
if callable(show_version):
self.__show_version_func = show_version
else:
self.__show_version_func = lambda cli, args: print(show_version)
self.parser.add_argument("-V", "--version", action="store_true")
@property
def logger(self):
""" Lazy logger """
if self.__logger is None:
self.__logger = logging.getLogger(self.__name)
return self.__logger
[docs] def run(self, func=None):
""" Run the app """
args = self.parser.parse_args()
if self.__add_vq is not None and self.__config_logging:
self.__config_logging(args)
if self.__show_version_func and args.version and callable(self.__show_version_func):
self.__show_version_func(self, args)
elif args.func is not None:
args.func(self, args)
elif func is not None:
func(self, args)
else:
self.parser.print_help()
@property
def parser(self):
return self.__parser
@property
def tasks(self):
return self.__tasks