# Copyright (C) 2018 Avatao.com Innovative Learning Kft. # All Rights Reserved. See LICENSE file for details. from abc import ABC from re import match from shlex import split from tfw.config.logs import logging LOG = logging.getLogger(__name__) class TerminalCommands(ABC): # pylint: disable=anomalous-backslash-in-string """ A class you can use to define hooks for terminal commands. This means that you can have python code executed when the user enters a specific command to the terminal on our frontend. To receive events you need to subscribe TerminalCommand.callback to a HistoryMonitor instance. Inherit from this class and define methods which start with "command\_". When the user executes the command specified after the underscore, your method will be invoked. All such commands must expect the parameter \*args which will contain the arguments of the command. For example to define a method that runs when someone starts vim in the terminal you have to define a method like: "def command_vim(self, \*args)" You can also use this class to create new commands similarly. """ def __init__(self, bashrc=None): self._command_method_regex = r'^command_(.+)$' self.command_implemetations = self._build_command_to_implementation_dict() if bashrc is not None: self._setup_bashrc_aliases(bashrc) def _build_command_to_implementation_dict(self): return { self._parse_command_name(fun): getattr(self, fun) for fun in dir(self) if callable(getattr(self, fun)) and self._is_command_implementation(fun) } def _setup_bashrc_aliases(self, bashrc): with open(bashrc, 'a') as ofile: alias_template = 'type {0} &> /dev/null || alias {0}="{0} &> /dev/null"\n' for command in self.command_implemetations.keys(): ofile.write(alias_template.format(command)) def _is_command_implementation(self, method_name): return bool(self._match_command_regex(method_name)) def _parse_command_name(self, method_name): try: return self._match_command_regex(method_name).groups()[0] except AttributeError: return '' def _match_command_regex(self, string): return match(self._command_method_regex, string) def callback(self, history): parts = split(history[-1]) command = parts[0] if command in self.command_implemetations.keys(): try: self.command_implemetations[command](*parts[1:]) except Exception: # pylint: disable=broad-except LOG.exception('Command "%s" failed:', command)