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>
This commit is contained in:
Jan Lindemann 2020-04-04 16:32:39 +02:00
commit c5b964a5bb
2 changed files with 41 additions and 3 deletions

View file

@ -1,9 +1,14 @@
import abc import abc
import argparse import argparse
from jwutils import log
# compatible with Python 2 *and* 3 # compatible with Python 2 *and* 3
ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()})
# full blown example of one level of nested subcommands
# git -C project remote -v show -n myremote
class Cmd(ABC): # export class Cmd(ABC): # export
@abc.abstractmethod @abc.abstractmethod
@ -13,6 +18,9 @@ class Cmd(ABC): # export
def __init__(self, name, help): def __init__(self, name, help):
self.name = name self.name = name
self.help = help self.help = help
self.parent = None
self.children = []
self.child_classes = []
async def _run(self, args): async def _run(self, args):
pass pass
@ -23,3 +31,12 @@ class Cmd(ABC): # export
r.set_defaults(func=self.run) r.set_defaults(func=self.run)
return r return r
def add_subcommands(self, cmd):
if isinstance(cmd, list):
for c in cmd:
self.add_subcommands(c)
return
self.child_classes.append(cmd)
def add_arguments(self, parser):
pass

View file

@ -6,15 +6,27 @@ import inspect
import re import re
import pickle import pickle
import asyncio import asyncio
import jwutils import jwutils
from jwutils import log from jwutils import log
class Cmds: # export 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): def __init__(self, description = '', filter = '^Cmd.*', modules=None, eloop=None):
self.__description = description self.__description = description
self.__filter = filter self.__filter = filter
self.__modules = modules self.__modules = modules
self.__cmds = []
default_log_level = log.NOTICE default_log_level = log.NOTICE
default_log_flags = 'stderr,position,prio,color' default_log_flags = 'stderr,position,prio,color'
# poor man's parsing in the absence of a complete command-line definition # poor man's parsing in the absence of a complete command-line definition
@ -40,9 +52,9 @@ class Cmds: # export
if eloop is None: if eloop is None:
self.eloop = asyncio.get_event_loop() self.eloop = asyncio.get_event_loop()
self.__own_eloop = True self.__own_eloop = True
subparsers = self.__parser.add_subparsers(title='Available commands', metavar='')
if self.__modules == None: if self.__modules == None:
self.__modules = [ '__main__' ] self.__modules = [ '__main__' ]
subcmds = set()
for m in self.__modules: for m in self.__modules:
if m != '__main__': if m != '__main__':
importlib.import_module(m) importlib.import_module(m)
@ -54,7 +66,16 @@ class Cmds: # export
log.slog(log.DEBUG, 'instantiating command "{}"'.format(c)) log.slog(log.DEBUG, 'instantiating command "{}"'.format(c))
cmd = c() cmd = c()
cmd.cmds = self cmd.cmds = self
cmd.add_parser(subparsers) #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): async def __run(self):
args = self.__parser.parse_args() args = self.__parser.parse_args()
@ -69,7 +90,7 @@ class Cmds: # export
self.eloop = None self.eloop = None
self.__own_eloop = False self.__own_eloop = False
def parser(): def parser(self):
return self.__parser return self.__parser
def run(self): def run(self):