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 __docformat__ = "restructuredtext en" |
| 46 |
| 47 from logilab.common.compat import raw_input, builtins |
| 48 if not hasattr(builtins, '_'): |
| 49 builtins._ = str |
| 50 |
| 51 |
| 52 def init_readline(complete_method, histfile=None): |
| 53 """Init the readline library if available.""" |
| 54 try: |
| 55 import readline |
| 56 readline.parse_and_bind("tab: complete") |
| 57 readline.set_completer(complete_method) |
| 58 string = readline.get_completer_delims().replace(':', '') |
| 59 readline.set_completer_delims(string) |
| 60 if histfile is not None: |
| 61 try: |
| 62 readline.read_history_file(histfile) |
| 63 except IOError: |
| 64 pass |
| 65 import atexit |
| 66 atexit.register(readline.write_history_file, histfile) |
| 67 except: |
| 68 print 'readline is not available :-(' |
| 69 |
| 70 |
| 71 class Completer : |
| 72 """Readline completer.""" |
| 73 |
| 74 def __init__(self, commands): |
| 75 self.list = commands |
| 76 |
| 77 def complete(self, text, state): |
| 78 """Hook called by readline when <tab> is pressed.""" |
| 79 n = len(text) |
| 80 matches = [] |
| 81 for cmd in self.list : |
| 82 if cmd[:n] == text : |
| 83 matches.append(cmd) |
| 84 try: |
| 85 return matches[state] |
| 86 except IndexError: |
| 87 return None |
| 88 |
| 89 |
| 90 class CLIHelper: |
| 91 """An abstract command line interface client which recognize commands |
| 92 and provide an help system. |
| 93 """ |
| 94 |
| 95 CMD_MAP = {'help': _("Others"), |
| 96 'quit': _("Others"), |
| 97 } |
| 98 CMD_PREFIX = '' |
| 99 |
| 100 def __init__(self, histfile=None) : |
| 101 self._topics = {} |
| 102 self.commands = None |
| 103 self._completer = Completer(self._register_commands()) |
| 104 init_readline(self._completer.complete, histfile) |
| 105 |
| 106 def run(self): |
| 107 """loop on user input, exit on EOF""" |
| 108 while True: |
| 109 try: |
| 110 line = raw_input('>>> ') |
| 111 except EOFError: |
| 112 print |
| 113 break |
| 114 s_line = line.strip() |
| 115 if not s_line: |
| 116 continue |
| 117 args = s_line.split() |
| 118 if args[0] in self.commands: |
| 119 try: |
| 120 cmd = 'do_%s' % self.commands[args[0]] |
| 121 getattr(self, cmd)(*args[1:]) |
| 122 except EOFError: |
| 123 break |
| 124 except: |
| 125 import traceback |
| 126 traceback.print_exc() |
| 127 else: |
| 128 try: |
| 129 self.handle_line(s_line) |
| 130 except: |
| 131 import traceback |
| 132 traceback.print_exc() |
| 133 |
| 134 def handle_line(self, stripped_line): |
| 135 """Method to overload in the concrete class (should handle |
| 136 lines which are not commands). |
| 137 """ |
| 138 raise NotImplementedError() |
| 139 |
| 140 |
| 141 # private methods ######################################################### |
| 142 |
| 143 def _register_commands(self): |
| 144 """ register available commands method and return the list of |
| 145 commands name |
| 146 """ |
| 147 self.commands = {} |
| 148 self._command_help = {} |
| 149 commands = [attr[3:] for attr in dir(self) if attr[:3] == 'do_'] |
| 150 for command in commands: |
| 151 topic = self.CMD_MAP[command] |
| 152 help_method = getattr(self, 'help_do_%s' % command) |
| 153 self._topics.setdefault(topic, []).append(help_method) |
| 154 self.commands[self.CMD_PREFIX + command] = command |
| 155 self._command_help[command] = help_method |
| 156 return self.commands.keys() |
| 157 |
| 158 def _print_help(self, cmd, syntax, explanation): |
| 159 print _('Command %s') % cmd |
| 160 print _('Syntax: %s') % syntax |
| 161 print '\t', explanation |
| 162 print |
| 163 |
| 164 |
| 165 # predefined commands ##################################################### |
| 166 |
| 167 def do_help(self, command=None) : |
| 168 """base input of the help system""" |
| 169 if command in self._command_help: |
| 170 self._print_help(*self._command_help[command]) |
| 171 elif command is None or command not in self._topics: |
| 172 print _("Use help <topic> or help <command>.") |
| 173 print _("Available topics are:") |
| 174 topics = sorted(self._topics.keys()) |
| 175 for topic in topics: |
| 176 print '\t', topic |
| 177 print |
| 178 print _("Available commands are:") |
| 179 commands = self.commands.keys() |
| 180 commands.sort() |
| 181 for command in commands: |
| 182 print '\t', command[len(self.CMD_PREFIX):] |
| 183 |
| 184 else: |
| 185 print _('Available commands about %s:') % command |
| 186 print |
| 187 for command_help_method in self._topics[command]: |
| 188 try: |
| 189 if callable(command_help_method): |
| 190 self._print_help(*command_help_method()) |
| 191 else: |
| 192 self._print_help(*command_help_method) |
| 193 except: |
| 194 import traceback |
| 195 traceback.print_exc() |
| 196 print 'ERROR in help method %s'% ( |
| 197 command_help_method.func_name) |
| 198 |
| 199 help_do_help = ("help", "help [topic|command]", |
| 200 _("print help message for the given topic/command or \ |
| 201 available topics when no argument")) |
| 202 |
| 203 def do_quit(self): |
| 204 """quit the CLI""" |
| 205 raise EOFError() |
| 206 |
| 207 def help_do_quit(self): |
| 208 return ("quit", "quit", _("quit the application")) |
OLD | NEW |