Skip to content
This repository has been archived by the owner on Sep 26, 2022. It is now read-only.

Commit

Permalink
Added docs, minor other code refactorings.
Browse files Browse the repository at this point in the history
  • Loading branch information
bigspider committed Oct 9, 2020
1 parent 3aa8318 commit 9adca26
Showing 1 changed file with 60 additions and 16 deletions.
76 changes: 60 additions & 16 deletions teos/cli/teos_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,35 +147,50 @@ def stop(self):


class CliCommand:
"""
Base class of each Cli command.
All the implementations should have an appropriately formatted docstring. See existing commands for an example.
Any implementation _must_ override the ``name`` attribute, and it might override the ``shortopts`` and ``longopts``
attributes.
"""

name = None
shortopts = ""
longopts = []

@classmethod
def parse_args(cls, args):
"""Parses the ``args`` array using ``getopt``, using ``shortopts`` and ``longopts`` as options."""

return getopt(args, cls.shortopts, cls.longopts)

@classmethod
def run(rpc_client, opts_args):
"""
Executes the command. Receives as parameters the rpc_client and the output of ``parse_args`` on the command
arguments.
"""

raise NotImplementedError()


class Cli:
COMMANDS = {}
"""
This class contains the logic for running all the commands of the command line interface. All the commands must be
subclasses of :class:`CliCommand` and need to be added to this class using the ``_register_command`` decorator.
@classmethod
def _register_command(cls, command_cls):
if not issubclass(command_cls, CliCommand):
raise TypeError(f"{command_cls.__name__} is not a subclass of CliCommand")
Args:
data_dir (:obj:`str`): the path to the data directory where the configuration file may be found.
command_line_conf (:obj"`dict`): the command line settings, parsed in a dictionary.
try:
if not isinstance(command_cls.name, str):
raise TypeError(f'{command_cls.__name__} has a "name" attribute, but it is not a string.')
except AttributeError:
raise TypeError(f'{command_cls.__name__} does not have a "name" attribute.')
Attributes:
rpc_client (:class:`RpcClient`): the rpc client that is passed to the ``run`` method of the commands.
"""

cls.COMMANDS[command_cls.name] = command_cls
return command_cls
# A dictionary mapping each command's name to the corresponding CliCommand subclass.
# It is populated by the ``_register_command`` decorator.
COMMANDS = {}

def __init__(self, data_dir, command_line_conf):
# Loads config and sets up the data folder and log file
Expand All @@ -189,15 +204,44 @@ def __init__(self, data_dir, command_line_conf):

self.rpc_client = RPCClient(teos_rpc_host, teos_rpc_port)

def run(self, command, raw_args):
if command not in self.COMMANDS:
@classmethod
def _register_command(cls, command_cls):
"""
Register a new command, which must be a subclass of :class:`CliCommand` and must override the ``name`` field
with an appropriate string.
Raises:
:obj:`TypeError`: ``command_cls`` is not a subclass of :class:`CliCommand`, or its ``name`` field is not a
string.
"""

if not issubclass(command_cls, CliCommand):
raise TypeError(f"{command_cls.__name__} is not a subclass of CliCommand")

if not isinstance(command_cls.name, str):
raise TypeError(f'The "name" attribute of {command_cls.__name__} must be a string.')

cls.COMMANDS[command_cls.name] = command_cls
return command_cls

def run(self, command_name, raw_args):
"""
Parses ``raw_args`` using the ``parse_args`` method of the command.
Then, executes the command's ``run`` method, passing the ``rpc_client`` and the output of ``parse_args``.
It any error that might happen, showing an appropriate message to console.
Returns:
The return value of the ``run`` command, or :obj:`None` if an error was raised.
"""

if command_name not in self.COMMANDS:
sys.exit("Unknown command. Use help to check the list of available commands")

cmd = self.COMMANDS[command]
cmd = self.COMMANDS[command_name]

try:
args = cmd.parse_args(raw_args)
return self.COMMANDS[command].run(self.rpc_client, args)
return self.COMMANDS[command_name].run(self.rpc_client, args)
except grpc.RpcError as e:
if e.code() == grpc.StatusCode.UNAVAILABLE:
sys.exit("It was not possible to reach the Eye of Satoshi. Are you sure the tower is running?")
Expand Down

0 comments on commit 9adca26

Please sign in to comment.