grooveondemand/groove/shell/base.py

135 lines
3.7 KiB
Python
Raw Normal View History

2022-12-17 18:02:12 -08:00
import functools
from collections import namedtuple, defaultdict
2022-11-30 00:09:23 -08:00
from prompt_toolkit.completion import Completer, Completion
from groove.console import Console
2022-12-17 18:02:12 -08:00
from textwrap import dedent
COMMANDS = defaultdict(dict)
Command = namedtuple('Commmand', 'prompt,handler,usage')
2022-12-17 18:02:12 -08:00
def register_command(handler, usage):
prompt = handler.__qualname__.split('.', -1)[0]
cmd = handler.__name__
if cmd not in COMMANDS[prompt]:
COMMANDS[prompt][cmd] = Command(prompt=prompt, handler=handler, usage=usage)
def command(usage):
def decorator(func):
register_command(func, usage)
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
2022-11-30 00:09:23 -08:00
class BasePrompt(Completer):
def __init__(self, manager=None, console=None, parent=None):
2022-11-30 00:09:23 -08:00
super(BasePrompt, self).__init__()
2022-12-07 23:41:49 -08:00
if (not manager and not parent): # pragma: no cover
2022-11-30 00:09:23 -08:00
raise RuntimeError("Must define either a database manager or a parent object.")
self._prompt = ''
2022-12-17 18:02:12 -08:00
self._autocomplete_values = []
2022-11-30 00:09:23 -08:00
self._parent = parent
self._manager = manager
self._console = None
self._theme = None
2022-12-17 18:02:12 -08:00
def _get_help(self, cmd=None):
try:
return dedent(COMMANDS[self.__class__.__name__][cmd].usage)
except KeyError:
return self.usage
def default_completer(self, document, complete_event):
raise NotImplementedError(f"Implement the 'default_completer' method of {self.__class__.__name__}")
2022-11-30 00:09:23 -08:00
@property
def usage(self):
2022-12-17 18:02:12 -08:00
text = dedent("""
[title]GROOVE ON DEMAND INTERACTIVE SHELL[/title]
Available commands are listed below. Try 'help COMMAND' for detailed help.
[title]COMMANDS[/title]
""")
for (name, cmd) in sorted(self.commands.items()):
text += f" [b]{name:10s}[/b] {cmd.handler.__doc__.strip()}\n"
return text
@property
def commands(self):
return COMMANDS[self.__class__.__name__]
2022-11-30 00:09:23 -08:00
@property
2022-12-17 18:02:12 -08:00
def console(self):
if not self._console:
self._console = Console(color_system='truecolor')
return self._console
2022-11-30 00:09:23 -08:00
@property
def manager(self):
if self._manager:
return self._manager
elif self._parent:
return self._parent.manager
@property
def parent(self):
return self._parent
@property
def prompt(self):
return self._prompt
@property
2022-12-17 18:02:12 -08:00
def autocomplete_values(self):
return self._autocomplete_values
2022-11-30 00:09:23 -08:00
def get_completions(self, document, complete_event):
word = document.get_word_before_cursor()
found = False
2022-12-17 18:02:12 -08:00
for value in self.autocomplete_values:
2022-11-30 00:09:23 -08:00
if word in value:
found = True
yield Completion(value, start_position=-len(word))
if not found:
try:
for result in self.default_completer(document, complete_event):
yield result
except NotImplementedError:
pass
def help(self, parts):
2022-12-17 18:02:12 -08:00
attr = None
if parts:
attr = parts[0]
self.console.print(self._get_help(attr))
return True
2022-12-17 18:02:12 -08:00
def process(self, cmd, *parts):
if cmd in self.commands:
return self.commands[cmd].handler(self, parts)
self.console.error(f"Command {cmd} not understood.")
2022-11-30 00:09:23 -08:00
def start(self, cmd=''):
while True:
if not cmd:
cmd = self.console.prompt(self.prompt, completer=self)
2022-11-30 00:09:23 -08:00
if not cmd:
return
cmd, *parts = cmd.split()
2022-12-17 18:02:12 -08:00
res = self.process(cmd, *parts)
if res is False:
return res
2022-11-30 00:09:23 -08:00
cmd = ''