Package common :: Module cli
[frames] | no frames]

Source Code for Module common.cli

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