OLD | NEW |
(Empty) | |
| 1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # |
| 4 # This file is part of logilab-common. |
| 5 # |
| 6 # logilab-common is free software: you can redistribute it and/or modify it unde
r |
| 7 # the terms of the GNU Lesser General Public License as published by the Free |
| 8 # Software Foundation, either version 2.1 of the License, or (at your option) an
y |
| 9 # later version. |
| 10 # |
| 11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
| 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| 14 # details. |
| 15 # |
| 16 # You should have received a copy of the GNU Lesser General Public License along |
| 17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
| 18 """Command line interface helper classes. |
| 19 |
| 20 It provides some default commands, a help system, a default readline |
| 21 configuration with completion and persistent history. |
| 22 |
| 23 Example:: |
| 24 |
| 25 class BookShell(CLIHelper): |
| 26 |
| 27 def __init__(self): |
| 28 # quit and help are builtins |
| 29 # CMD_MAP keys are commands, values are topics |
| 30 self.CMD_MAP['pionce'] = _("Sommeil") |
| 31 self.CMD_MAP['ronfle'] = _("Sommeil") |
| 32 CLIHelper.__init__(self) |
| 33 |
| 34 help_do_pionce = ("pionce", "pionce duree", _("met ton corps en veille")
) |
| 35 def do_pionce(self): |
| 36 print('nap is good') |
| 37 |
| 38 help_do_ronfle = ("ronfle", "ronfle volume", _("met les autres en veille
")) |
| 39 def do_ronfle(self): |
| 40 print('fuuuuuuuuuuuu rhhhhhrhrhrrh') |
| 41 |
| 42 cl = BookShell() |
| 43 """ |
| 44 |
| 45 from __future__ import print_function |
| 46 |
| 47 __docformat__ = "restructuredtext en" |
| 48 |
| 49 from six.moves import builtins, input |
| 50 |
| 51 if not hasattr(builtins, '_'): |
| 52 builtins._ = str |
| 53 |
| 54 |
| 55 def init_readline(complete_method, histfile=None): |
| 56 """Init the readline library if available.""" |
| 57 try: |
| 58 import readline |
| 59 readline.parse_and_bind("tab: complete") |
| 60 readline.set_completer(complete_method) |
| 61 string = readline.get_completer_delims().replace(':', '') |
| 62 readline.set_completer_delims(string) |
| 63 if histfile is not None: |
| 64 try: |
| 65 readline.read_history_file(histfile) |
| 66 except IOError: |
| 67 pass |
| 68 import atexit |
| 69 atexit.register(readline.write_history_file, histfile) |
| 70 except: |
| 71 print('readline is not available :-(') |
| 72 |
| 73 |
| 74 class Completer : |
| 75 """Readline completer.""" |
| 76 |
| 77 def __init__(self, commands): |
| 78 self.list = commands |
| 79 |
| 80 def complete(self, text, state): |
| 81 """Hook called by readline when <tab> is pressed.""" |
| 82 n = len(text) |
| 83 matches = [] |
| 84 for cmd in self.list : |
| 85 if cmd[:n] == text : |
| 86 matches.append(cmd) |
| 87 try: |
| 88 return matches[state] |
| 89 except IndexError: |
| 90 return None |
| 91 |
| 92 |
| 93 class CLIHelper: |
| 94 """An abstract command line interface client which recognize commands |
| 95 and provide an help system. |
| 96 """ |
| 97 |
| 98 CMD_MAP = {'help': _("Others"), |
| 99 'quit': _("Others"), |
| 100 } |
| 101 CMD_PREFIX = '' |
| 102 |
| 103 def __init__(self, histfile=None) : |
| 104 self._topics = {} |
| 105 self.commands = None |
| 106 self._completer = Completer(self._register_commands()) |
| 107 init_readline(self._completer.complete, histfile) |
| 108 |
| 109 def run(self): |
| 110 """loop on user input, exit on EOF""" |
| 111 while True: |
| 112 try: |
| 113 line = input('>>> ') |
| 114 except EOFError: |
| 115 print |
| 116 break |
| 117 s_line = line.strip() |
| 118 if not s_line: |
| 119 continue |
| 120 args = s_line.split() |
| 121 if args[0] in self.commands: |
| 122 try: |
| 123 cmd = 'do_%s' % self.commands[args[0]] |
| 124 getattr(self, cmd)(*args[1:]) |
| 125 except EOFError: |
| 126 break |
| 127 except: |
| 128 import traceback |
| 129 traceback.print_exc() |
| 130 else: |
| 131 try: |
| 132 self.handle_line(s_line) |
| 133 except: |
| 134 import traceback |
| 135 traceback.print_exc() |
| 136 |
| 137 def handle_line(self, stripped_line): |
| 138 """Method to overload in the concrete class (should handle |
| 139 lines which are not commands). |
| 140 """ |
| 141 raise NotImplementedError() |
| 142 |
| 143 |
| 144 # private methods ######################################################### |
| 145 |
| 146 def _register_commands(self): |
| 147 """ register available commands method and return the list of |
| 148 commands name |
| 149 """ |
| 150 self.commands = {} |
| 151 self._command_help = {} |
| 152 commands = [attr[3:] for attr in dir(self) if attr[:3] == 'do_'] |
| 153 for command in commands: |
| 154 topic = self.CMD_MAP[command] |
| 155 help_method = getattr(self, 'help_do_%s' % command) |
| 156 self._topics.setdefault(topic, []).append(help_method) |
| 157 self.commands[self.CMD_PREFIX + command] = command |
| 158 self._command_help[command] = help_method |
| 159 return self.commands.keys() |
| 160 |
| 161 def _print_help(self, cmd, syntax, explanation): |
| 162 print(_('Command %s') % cmd) |
| 163 print(_('Syntax: %s') % syntax) |
| 164 print('\t', explanation) |
| 165 print() |
| 166 |
| 167 |
| 168 # predefined commands ##################################################### |
| 169 |
| 170 def do_help(self, command=None) : |
| 171 """base input of the help system""" |
| 172 if command in self._command_help: |
| 173 self._print_help(*self._command_help[command]) |
| 174 elif command is None or command not in self._topics: |
| 175 print(_("Use help <topic> or help <command>.")) |
| 176 print(_("Available topics are:")) |
| 177 topics = sorted(self._topics.keys()) |
| 178 for topic in topics: |
| 179 print('\t', topic) |
| 180 print() |
| 181 print(_("Available commands are:")) |
| 182 commands = self.commands.keys() |
| 183 commands.sort() |
| 184 for command in commands: |
| 185 print('\t', command[len(self.CMD_PREFIX):]) |
| 186 |
| 187 else: |
| 188 print(_('Available commands about %s:') % command) |
| 189 print |
| 190 for command_help_method in self._topics[command]: |
| 191 try: |
| 192 if callable(command_help_method): |
| 193 self._print_help(*command_help_method()) |
| 194 else: |
| 195 self._print_help(*command_help_method) |
| 196 except: |
| 197 import traceback |
| 198 traceback.print_exc() |
| 199 print('ERROR in help method %s'% ( |
| 200 command_help_method.__name__)) |
| 201 |
| 202 help_do_help = ("help", "help [topic|command]", |
| 203 _("print help message for the given topic/command or \ |
| 204 available topics when no argument")) |
| 205 |
| 206 def do_quit(self): |
| 207 """quit the CLI""" |
| 208 raise EOFError() |
| 209 |
| 210 def help_do_quit(self): |
| 211 return ("quit", "quit", _("quit the application")) |
OLD | NEW |