jw-python/tools/python/jwutils/Cmds.py
Jan Lindemann c5b964a5bb jwutils.Cmd: Add method add_subcommands()
Add method jwutils.Cmd.add_subcommands(). cmd.add_subcommands(C) will
parse and invoke C as a subcommand of cmd, if C is of type jwutils.cmd.

Signed-off-by: Jan Lindemann <jan@janware.com>
2020-04-04 16:32:39 +02:00

102 lines
3.7 KiB
Python

import os
import sys
import argparse
import importlib
import inspect
import re
import pickle
import asyncio
import jwutils
from jwutils import log
class Cmds: # export
def __add_cmd_to_parser(self, cmd, parsers):
parser = cmd.add_parser(parsers)
cmd.add_arguments(parser)
if len(cmd.child_classes) > len(cmd.children):
for c in cmd.child_classes:
cmd.children.append(c())
for sub_cmd in cmd.children:
subparsers = parser.add_subparsers(title='Available commands of ' + cmd.name, metavar='')
self.__add_cmd_to_parser(sub_cmd, subparsers)
def __init__(self, description = '', filter = '^Cmd.*', modules=None, eloop=None):
self.__description = description
self.__filter = filter
self.__modules = modules
self.__cmds = []
default_log_level = log.NOTICE
default_log_flags = 'stderr,position,prio,color'
# poor man's parsing in the absence of a complete command-line definition
for i in range(1, len(sys.argv)):
if i >= len(sys.argv) - 1:
break
arg = sys.argv[i]
if arg == '--log-level':
i += 1
default_log_level = sys.argv[i]
continue
if arg == '--log-flags':
default_log_flags = sys.argv[i]
continue
log.set_flags(default_log_flags)
log.set_level(default_log_level)
self.__parser = argparse.ArgumentParser(usage=os.path.basename(sys.argv[0]) + ' [command] [options]',
formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=self.__description)
self.__parser.add_argument('--log-flags', help='Log flags', default=default_log_flags)
self.__parser.add_argument('--log-level', help='Log level', default=default_log_level)
self.eloop = eloop
self.__own_eloop = False
if eloop is None:
self.eloop = asyncio.get_event_loop()
self.__own_eloop = True
if self.__modules == None:
self.__modules = [ '__main__' ]
subcmds = set()
for m in self.__modules:
if m != '__main__':
importlib.import_module(m)
for name, c in inspect.getmembers(sys.modules[m], inspect.isclass):
if not re.match(self.__filter, name):
continue
if inspect.isabstract(c):
continue
log.slog(log.DEBUG, 'instantiating command "{}"'.format(c))
cmd = c()
cmd.cmds = self
#cmd.add_parser(subparsers)
self.__cmds.append(cmd)
for child in cmd.child_classes:
subcmds.add(child)
subcmds.update(cmd.child_classes)
cmds = [cmd for cmd in self.__cmds if type(cmd) not in subcmds]
subparsers = self.__parser.add_subparsers(title='Available commands', metavar='')
for cmd in cmds:
self.__add_cmd_to_parser(cmd, subparsers)
async def __run(self):
args = self.__parser.parse_args()
log.set_flags(args.log_flags)
log.set_level(args.log_level)
return await args.func(args)
def __del__(self):
if self.__own_eloop:
if self.eloop is not None:
self.eloop.close()
self.eloop = None
self.__own_eloop = False
def parser(self):
return self.__parser
def run(self):
#return self.__run()
return self.eloop.run_until_complete(self.__run())
def run_sub_commands(description = '', filter = '^Cmd.*', modules=None): # export
cmds = Cmds(description, filter, modules)
return cmds.run()