jw-python/tools/python/jwutils/Cmds.py

107 lines
3.9 KiB
Python
Raw Normal View History

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 __instantiate(self, cls):
r = cls()
r.cmds = self # Rename Cmds class to App, "Cmds" isn't very self-explanatory
r.app = self
return r
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(self.__instantiate(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 = self.__instantiate(c)
#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()