| # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| # contact http://www.logilab.fr/ -- mailto:[email protected] |
| # |
| # This file is part of logilab-common. |
| # |
| # logilab-common is free software: you can redistribute it and/or modify it under |
| # the terms of the GNU Lesser General Public License as published by the Free |
| # Software Foundation, either version 2.1 of the License, or (at your option) any |
| # later version. |
| # |
| # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
| # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| # details. |
| # |
| # You should have received a copy of the GNU Lesser General Public License along |
| # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
| """Command line interface helper classes. |
| |
| It provides some default commands, a help system, a default readline |
| configuration with completion and persistent history. |
| |
| Example:: |
| |
| class BookShell(CLIHelper): |
| |
| def __init__(self): |
| # quit and help are builtins |
| # CMD_MAP keys are commands, values are topics |
| self.CMD_MAP['pionce'] = _("Sommeil") |
| self.CMD_MAP['ronfle'] = _("Sommeil") |
| CLIHelper.__init__(self) |
| |
| help_do_pionce = ("pionce", "pionce duree", _("met ton corps en veille")) |
| def do_pionce(self): |
| print('nap is good') |
| |
| help_do_ronfle = ("ronfle", "ronfle volume", _("met les autres en veille")) |
| def do_ronfle(self): |
| print('fuuuuuuuuuuuu rhhhhhrhrhrrh') |
| |
| cl = BookShell() |
| """ |
| |
| from __future__ import print_function |
| |
| __docformat__ = "restructuredtext en" |
| |
| from six.moves import builtins, input |
| |
| if not hasattr(builtins, '_'): |
| builtins._ = str |
| |
| |
| def init_readline(complete_method, histfile=None): |
| """Init the readline library if available.""" |
| try: |
| import readline |
| readline.parse_and_bind("tab: complete") |
| readline.set_completer(complete_method) |
| string = readline.get_completer_delims().replace(':', '') |
| readline.set_completer_delims(string) |
| if histfile is not None: |
| try: |
| readline.read_history_file(histfile) |
| except IOError: |
| pass |
| import atexit |
| atexit.register(readline.write_history_file, histfile) |
| except: |
| print('readline is not available :-(') |
| |
| |
| class Completer : |
| """Readline completer.""" |
| |
| def __init__(self, commands): |
| self.list = commands |
| |
| def complete(self, text, state): |
| """Hook called by readline when <tab> is pressed.""" |
| n = len(text) |
| matches = [] |
| for cmd in self.list : |
| if cmd[:n] == text : |
| matches.append(cmd) |
| try: |
| return matches[state] |
| except IndexError: |
| return None |
| |
| |
| class CLIHelper: |
| """An abstract command line interface client which recognize commands |
| and provide an help system. |
| """ |
| |
| CMD_MAP = {'help': _("Others"), |
| 'quit': _("Others"), |
| } |
| CMD_PREFIX = '' |
| |
| def __init__(self, histfile=None) : |
| self._topics = {} |
| self.commands = None |
| self._completer = Completer(self._register_commands()) |
| init_readline(self._completer.complete, histfile) |
| |
| def run(self): |
| """loop on user input, exit on EOF""" |
| while True: |
| try: |
| line = input('>>> ') |
| except EOFError: |
| print |
| break |
| s_line = line.strip() |
| if not s_line: |
| continue |
| args = s_line.split() |
| if args[0] in self.commands: |
| try: |
| cmd = 'do_%s' % self.commands[args[0]] |
| getattr(self, cmd)(*args[1:]) |
| except EOFError: |
| break |
| except: |
| import traceback |
| traceback.print_exc() |
| else: |
| try: |
| self.handle_line(s_line) |
| except: |
| import traceback |
| traceback.print_exc() |
| |
| def handle_line(self, stripped_line): |
| """Method to overload in the concrete class (should handle |
| lines which are not commands). |
| """ |
| raise NotImplementedError() |
| |
| |
| # private methods ######################################################### |
| |
| def _register_commands(self): |
| """ register available commands method and return the list of |
| commands name |
| """ |
| self.commands = {} |
| self._command_help = {} |
| commands = [attr[3:] for attr in dir(self) if attr[:3] == 'do_'] |
| for command in commands: |
| topic = self.CMD_MAP[command] |
| help_method = getattr(self, 'help_do_%s' % command) |
| self._topics.setdefault(topic, []).append(help_method) |
| self.commands[self.CMD_PREFIX + command] = command |
| self._command_help[command] = help_method |
| return self.commands.keys() |
| |
| def _print_help(self, cmd, syntax, explanation): |
| print(_('Command %s') % cmd) |
| print(_('Syntax: %s') % syntax) |
| print('\t', explanation) |
| print() |
| |
| |
| # predefined commands ##################################################### |
| |
| def do_help(self, command=None) : |
| """base input of the help system""" |
| if command in self._command_help: |
| self._print_help(*self._command_help[command]) |
| elif command is None or command not in self._topics: |
| print(_("Use help <topic> or help <command>.")) |
| print(_("Available topics are:")) |
| topics = sorted(self._topics.keys()) |
| for topic in topics: |
| print('\t', topic) |
| print() |
| print(_("Available commands are:")) |
| commands = self.commands.keys() |
| commands.sort() |
| for command in commands: |
| print('\t', command[len(self.CMD_PREFIX):]) |
| |
| else: |
| print(_('Available commands about %s:') % command) |
| print |
| for command_help_method in self._topics[command]: |
| try: |
| if callable(command_help_method): |
| self._print_help(*command_help_method()) |
| else: |
| self._print_help(*command_help_method) |
| except: |
| import traceback |
| traceback.print_exc() |
| print('ERROR in help method %s'% ( |
| command_help_method.__name__)) |
| |
| help_do_help = ("help", "help [topic|command]", |
| _("print help message for the given topic/command or \ |
| available topics when no argument")) |
| |
| def do_quit(self): |
| """quit the CLI""" |
| raise EOFError() |
| |
| def help_do_quit(self): |
| return ("quit", "quit", _("quit the application")) |