Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Hrabal committed Dec 15, 2018
1 parent 2720907 commit 3cde796
Showing 1 changed file with 39 additions and 36 deletions.
75 changes: 39 additions & 36 deletions classcli/clibuilder.py
Expand Up @@ -15,6 +15,41 @@ def __getattribute__(self, attr):
fg = rs = EmptyStringer()


def _load_object(module_or_obj_collection):
# Input cleanup, get an iterable of classes out of the argument
if isinstance(module_or_obj_collection, dict):
iterator = ((cname, obj) for cname, obj in module_or_obj_collection.items() if inspect.isclass(obj))
elif isinstance(module_or_obj_collection, list):
iterator = ((obj.__name__, obj) for obj in module_or_obj_collection if inspect.isclass(obj))
elif inspect.ismodule(module_or_obj_collection):
iterator = inspect.getmembers(module_or_obj_collection, inspect.isclass)
else:
raise TypeError('Unsupported type for CLI init: list/dist with classes in it or module supported.')
return iterator


def _make_bool_arg(arg):
return ('-%s' % arg.name, ), {'action': 'store_false' if arg.default is False else 'store_true'}


def _make_arg(arg, used_aliases: set):
arg_kwargs = {
'type': arg.annotation if arg.annotation is not arg.empty else str
}
if arg.default is not arg.empty:
arg_kwargs['default'] = arg.default
names = ['--%s' % arg.name, ]
try:
letter = next(l for l in arg.name if l not in used_aliases)
names.insert(0, '-%s' % letter)
used_aliases.add(letter)
except StopIteration:
raise argparse.ArgumentError(None, 'Impossible to find an alias for argument %s.' % arg)
else:
names = (arg.name, )
return names, arg_kwargs


class ClassParser(argparse.ArgumentParser):
def error(self, message):
sys.stderr.write('%serror: %s%s\n' % (fg.red, message, rs.fg))
Expand All @@ -34,23 +69,11 @@ def __init__(self, module_or_obj_collection):
self.subparsers = self.parser.add_subparsers()

# For every controller class defined in the input module we add a subparser
for _, cls in self._load_object(module_or_obj_collection):
for _, cls in _load_object(module_or_obj_collection):
# Only classes with callable_cls will be added as subparser (so to exclude utility classes)
if getattr(cls, 'callable_cls', False):
self._make_subparser(cls)

def _load_object(self, module_or_obj_collection):
# Input cleanup, get an iterable of classes out of the argument
if isinstance(module_or_obj_collection, dict):
iterator = ((cname, obj) for cname, obj in module_or_obj_collection.items() if inspect.isclass(obj))
elif isinstance(module_or_obj_collection, list):
iterator = ((obj.__name__, obj) for obj in module_or_obj_collection if inspect.isclass(obj))
elif inspect.ismodule(module_or_obj_collection):
iterator = inspect.getmembers(module_or_obj_collection, inspect.isclass)
else:
raise TypeError('Unsupported type for CLI init: list/dist with classes in it or module supported.')
return iterator

def _make_subparser(self, cls):
# Help for this command from the class docstring and the _base method docstring
help_str = '\n'.join(doc for doc in (inspect.getdoc(cls), inspect.getdoc(cls._base)) if doc)
Expand Down Expand Up @@ -78,31 +101,11 @@ def _read_arguments(self, method_args_parser, fnc):
for arg in inspect.signature(fnc).parameters.values():
arg_kwargs = {}
if arg.annotation is bool:
names, arg_kwargs = self._make_bool_arg(arg)
names, arg_kwargs = _make_bool_arg(arg)
else:
names, arg_kwargs = self._make_arg(arg, used_aliases)
names, arg_kwargs = _make_arg(arg, used_aliases)
method_args_parser.add_argument(*names, **arg_kwargs)

def _make_bool_arg(self, arg):
return ('-%s' % arg.name, ), {'action': 'store_false' if arg.default is False else 'store_true'}

def _make_arg(self, arg, used_aliases: set):
arg_kwargs = {
'type': arg.annotation if arg.annotation is not arg.empty else str
}
if arg.default is not arg.empty:
arg_kwargs['default'] = arg.default
names = ['--%s' % arg.name, ]
try:
letter = next(l for l in arg.name if l not in used_aliases)
names.insert(0, '-%s' % letter)
used_aliases.add(letter)
except StopIteration:
raise argparse.ArgumentError(None, 'Impossible to find an alias for argument %s.' % arg)
else:
names = (arg.name, )
return names, arg_kwargs

def run_cli(self, args=None):
if args:
args = map(str, args)
Expand All @@ -112,6 +115,6 @@ def run_cli(self, args=None):
raise argparse.ArgumentError(None, 'At least one argument needed.')
return args.func(**{k: v for k, v in vars(args).items() if k != 'func'})
except argparse.ArgumentError as ex:
print('ERROR! Wrong command invocation: %s%s' % (fg.red, ex.message, rs.fg))
print('%sERROR! Wrong command invocation: %s%s' % (fg.red, ex.message, rs.fg))
print('Please read the help:')
self.parser.print_help()

0 comments on commit 3cde796

Please sign in to comment.