Skip to content

Commit

Permalink
feat(ZanataArgParser.py): Colored log
Browse files Browse the repository at this point in the history
refactor(test): move tests to test/
  • Loading branch information
definite committed Aug 24, 2018
1 parent 9a5e7d7 commit 9b525af
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 26 deletions.
127 changes: 113 additions & 14 deletions ZanataArgParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@

from __future__ import (absolute_import, division, print_function)

import inspect
import logging
import os
import re
import sys

from argparse import ArgumentParser, ArgumentError
# Following are for mypy
from argparse import Action # noqa: F401 # pylint: disable=W0611
from argparse import Namespace # noqa: F401 # pylint: disable=W0611
from argparse import _SubParsersAction # noqa: F401 # pylint: disable=W0611
from logging import Formatter
import logging
import os
import sys

try:
from typing import List, Any # noqa: F401 # pylint: disable=W0611
Expand All @@ -28,7 +30,7 @@
sys.stderr.write("python typing module is not installed" + os.linesep)


class ColoredFormatter(Formatter):
class ColoredFormatter(logging.Formatter):
"""Log colored formated
Inspired from KurtJacobson's colored_log.py"""
DEFAULT_COLOR = 37 # white
Expand All @@ -43,7 +45,7 @@ class ColoredFormatter(Formatter):
SUFFIX = '\033[0m'

def __init__(self, patern):
Formatter.__init__(self, patern)
logging.Formatter.__init__(self, patern)

@staticmethod
def _color(color_id, content):
Expand Down Expand Up @@ -92,11 +94,9 @@ def format(self, record):

class ZanataArgParser(ArgumentParser):
"""Zanata Argument Parser"""

def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
super(ZanataArgParser, self).__init__(*args, **kwargs)
self.sub_parsers = None # type: Optional[_SubParsersAction]
self.env_def = {} # type: Dict[str, dict]
self.parent_parser = ArgumentParser(add_help=False)
self.add_argument(
Expand All @@ -105,22 +105,38 @@ def __init__(self, *args, **kwargs):
help='Valid values: %s'
% 'DEBUG, INFO, WARNING, ERROR, CRITICAL, NONE')

self.sub_parsers = None
self.sub_command_obj_dict = {} # type: Dict[str, Any]

def add_common_argument(self, *args, **kwargs):
# type: (Any, Any) -> None
"""Add a common argument that will be used in all sub commands
In other words, common argument wil be put in common parser.
In other words, common argument will be put in common parser.
Note that add_common_argument must be put in then front of
add_sub_command that uses common arguments."""
self.parent_parser.add_argument(*args, **kwargs)

def add_sub_command(self, name, arguments, **kwargs):
# type: (str, dict, Any) -> ArgumentParser
"""Add a sub command"""
def add_sub_command(self, name, arguments, obj=None, **kwargs):
# type: (str, List, Any, Any) -> ArgumentParser
"""Add a sub command
Args:
name (str): name of the sub-command
arguments (dict): argments to be passed to argparse.add_argument()
obj (Any, optional): Defaults to None. The sub_command is
a method of the obj.
Returns:
[type]: [description]
"""
if not self.sub_parsers:
self.sub_parsers = self.add_subparsers(
title='Command', description='Valid commands',
help='Command help')

if obj:
self.sub_command_obj_dict[name] = obj

if 'parents' in kwargs:
kwargs['parents'] += [self.parent_parser]
else:
Expand All @@ -129,8 +145,13 @@ def add_sub_command(self, name, arguments, **kwargs):
anonymous_parser = self.sub_parsers.add_parser(
name, **kwargs)
if arguments:
for k, v in arguments.iteritems():
anonymous_parser.add_argument(*k.split(), **v)
for arg in arguments:
k = arg[0]
v = arg[1]
if v:
anonymous_parser.add_argument(*k.split(), **v)
else:
anonymous_parser.add_argument(*k.split())
anonymous_parser.set_defaults(sub_command=name)
return anonymous_parser

Expand Down Expand Up @@ -160,6 +181,56 @@ def add_env( # pylint: disable=too-many-arguments
'dest': dest,
'sub_commands': sub_commands}

def add_methods_as_subcommands(self, obj, name_pattern='.*'):
# type (Any, str) -> None
"""Add public methods as sub-commands
Args:
obj ([type]): Public methods of obj will be used
name_pattern (str, optional): Defaults to '.*'.
Method name should match the pattern.
"""
method_list = inspect.getmembers(obj)
for m in method_list:
if not re.match(name_pattern, m[0]):
continue

name = m[0]
m_obj = m[1]

if name[0] == '_':
# No private functions (which start with _)
continue

if not inspect.ismethod(m_obj) and not inspect.isfunction(m_obj):
continue

argspec = inspect.getargspec(m_obj)
sub_args = None
try:
start_idx = len(argspec.args) - len(argspec.defaults)
except TypeError:
start_idx = len(argspec.args) + 1
for idx, a in enumerate(argspec.args):
if a == 'self' or a == 'cls':
continue
if argspec.defaults and idx >= start_idx:
arg_def = {
'nargs': '?',
'default': argspec.defaults[idx - start_idx]}
else:
arg_def = None
if sub_args:
sub_args.append(tuple([a, arg_def]))
else:
sub_args = [tuple([a, arg_def])]

self.add_sub_command(
name,
sub_args,
obj,
help=obj.__doc__)

def has_common_argument(self, option_string=None, dest=None):
# type: (str, str) -> bool
"""Has the parser defined this argument as a common argument?
Expand Down Expand Up @@ -269,6 +340,34 @@ def parse_all(self, args=None, namespace=None):
setattr(result, k, v)
return result

def run_sub_command(self, args=None):
"""Run the sub ccommand with parsed arguments
Args:
instance ([type]): [description]
args ([type], optional): Defaults to None. Arguments
Raises:
ArgumentError: When sub_command is missing
"""
if not args.sub_command:
raise ArgumentError(args, "Missing sub-command")

if args.sub_command not in self.sub_command_obj_dict:
raise ArgumentError(
args,
"sub-command %s is not associated with any object" %
args.sub_command)
obj = self.sub_command_obj_dict[args.sub_command]
sub_cmd_obj = getattr(obj, args.sub_command)
argspec = inspect.getargspec(sub_cmd_obj)
arg_values = []
for a in argspec.args:
if a == 'self' or a == 'cls':
continue
arg_values.append(getattr(args, a))
return sub_cmd_obj(*arg_values)


if __name__ == '__main__':
print("Legend of log levels", file=sys.stderr)
Expand Down
Loading

0 comments on commit 9b525af

Please sign in to comment.