nubia/internal/registry.py (78 lines of code) (raw):
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
#
from prompt_toolkit.completion import WordCompleter
from termcolor import cprint
from nubia.internal.cmdbase import Command
from nubia.internal.helpers import try_await
from nubia.internal.io.eventbus import Listener
class CommandsRegistry:
"""
A registry that holds all commands implementations and creates a quick
access point for resolving a command string into the corresponding handling
object
"""
def __init__(self, parser, listeners):
self._completer = WordCompleter([], ignore_case=True, sentence=True)
# maps a command to Command Instance
self._cmd_instance_map = {}
# objects interested in receiving messages
self._listeners = []
# argparser so each command can add its options
self._parser = parser
for lst in listeners:
self.register_listener(lst(self))
async def register_command(self, cmd_instance, override=False):
if not isinstance(cmd_instance, Command):
raise TypeError(
"Invalid command instance, must be an instance of "
"subclass of Command"
)
cmd_instance.set_command_registry(self)
cmd_keys = cmd_instance.get_command_names()
for cmd in cmd_keys:
if not cmd_instance.get_help(cmd):
cprint(
(
"[WARNING] The command {} will not be loaded. "
"Please provide a help message by either defining a "
"docstring or filling the help argument in the "
"@command annotation"
).format(cmd_keys[0]),
"red",
)
return None
await try_await(cmd_instance.add_arguments(self._parser))
if not override:
conflicts = [cmd for cmd in cmd_keys if cmd in self._cmd_instance_map]
if conflicts:
raise ValueError(
"Some other command instance has registered "
"the name(s) {}".format(conflicts)
)
if isinstance(cmd_instance, Listener):
self._listeners.append(cmd_instance)
for cmd in cmd_keys:
self._cmd_instance_map[cmd.lower()] = cmd_instance
if cmd not in self._completer.words:
self._completer.words.append(cmd)
self._completer.meta_dict[cmd] = cmd_instance.get_help_short(cmd)
aliases = cmd_instance.get_cli_aliases()
for alias in aliases:
self._cmd_instance_map[alias.lower()] = cmd_instance
def register_priority_listener(self, instance):
"""
Registers a listener that get the top priority in callbacks
"""
if not isinstance(instance, Listener):
raise TypeError("Only Listeners can be registered")
self._listeners.insert(0, instance)
def register_listener(self, instance):
if not isinstance(instance, Listener):
raise TypeError("Only Listeners can be registered")
self._listeners.append(instance)
def __contains__(self, cmd):
return cmd.lower() in self._cmd_instance_map
def get_completer(self):
return self._completer
def get_all_commands(self):
return set(self._cmd_instance_map.values())
def get_all_commands_map(self):
return self._cmd_instance_map
def find_command(self, cmd):
return self._cmd_instance_map.get(cmd.lower())
def get_completions(self, document, complete_event):
return self._completer.get_completions(document, complete_event)
async def dispatch_message(self, msg, *args, **kwargs):
for mod in self._listeners:
await try_await(mod.react(msg, *args, **kwargs))
def set_cli_args(self, args):
self._args = args
def get_cli_arg(self, arg):
return getattr(self._args, arg, None)