RPKI Engine  1.0
cli.py (4014)
Go to the documentation of this file.
00001 """
00002 Utilities for writing command line tools.
00003 
00004 $Id: cli.py 4014 2011-10-05 16:30:24Z sra $
00005 
00006 Copyright (C) 2010--2011  Internet Systems Consortium ("ISC")
00007 
00008 Permission to use, copy, modify, and distribute this software for any
00009 purpose with or without fee is hereby granted, provided that the above
00010 copyright notice and this permission notice appear in all copies.
00011 
00012 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00013 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00014 AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00015 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00016 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00017 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00018 PERFORMANCE OF THIS SOFTWARE.
00019 """
00020 
00021 import cmd, glob, os.path, traceback
00022 
00023 try:
00024   import readline
00025   have_readline = True
00026 except ImportError:
00027   have_readline = False
00028 
00029 class Cmd(cmd.Cmd):
00030   """
00031   Customized subclass of Python cmd module.
00032   """
00033 
00034   emptyline_repeats_last_command = False
00035 
00036   EOF_exits_command_loop = True
00037 
00038   identchars = cmd.IDENTCHARS + "/-."
00039 
00040   histfile = None
00041 
00042   def __init__(self, argv = None):
00043     cmd.Cmd.__init__(self)
00044     if argv:
00045       self.onecmd(" ".join(argv))
00046     else:      
00047       self.cmdloop_with_history()
00048 
00049   def onecmd(self, line):
00050     """
00051     Wrap error handling around cmd.Cmd.onecmd().  Might want to do
00052     something kinder than showing a traceback, eventually.
00053     """
00054     try:
00055       return cmd.Cmd.onecmd(self, line)
00056     except SystemExit:
00057       raise
00058     except Exception:
00059       traceback.print_exc()
00060 
00061   def do_EOF(self, arg):
00062     """
00063     Exit program.
00064     """
00065     if self.EOF_exits_command_loop and self.prompt:
00066       print
00067     return self.EOF_exits_command_loop
00068 
00069   def do_exit(self, arg):
00070     """
00071     Exit program.
00072     """
00073     return True
00074 
00075   do_quit = do_exit
00076 
00077   def emptyline(self):
00078     """
00079     Handle an empty line.  cmd module default is to repeat the last
00080     command, which I find to be violation of the principal of least
00081     astonishment, so my preference is that an empty line does nothing.
00082     """
00083     if self.emptyline_repeats_last_command:
00084       cmd.Cmd.emptyline(self)
00085 
00086   def filename_complete(self, text, line, begidx, endidx):
00087     """
00088     Filename completion handler, with hack to restore what I consider
00089     the normal (bash-like) behavior when one hits the completion key
00090     and there's only one match.
00091     """
00092     result = glob.glob(text + "*")
00093     if len(result) == 1:
00094       path = result.pop()
00095       if os.path.isdir(path) or (os.path.islink(path) and os.path.isdir(os.path.join(path, "."))):
00096         result.append(path + os.path.sep)
00097       else:
00098         result.append(path + " ")
00099     return result
00100 
00101   def completenames(self, text, *ignored):
00102     """
00103     Command name completion handler, with hack to restore what I
00104     consider the normal (bash-like) behavior when one hits the
00105     completion key and there's only one match.
00106     """
00107     result = set(cmd.Cmd.completenames(self, text, *ignored))
00108     if len(result) == 1:
00109       result.add(result.pop() + " ")
00110     return list(result)
00111 
00112   def help_help(self):
00113     """
00114     Type "help [topic]" for help on a command,
00115     or just "help" for a list of commands.
00116     """
00117     self.stdout.write(self.help_help.__doc__ + "\n")
00118 
00119   def complete_help(self, *args):
00120     """
00121     Better completion function for help command arguments.
00122     """
00123     text = args[0]
00124     names = self.get_names()
00125     result = []
00126     for prefix in ("do_", "help_"):
00127       result.extend(s[len(prefix):] for s in names if s.startswith(prefix + text))
00128     return result
00129 
00130   if have_readline:
00131 
00132     def cmdloop_with_history(self):
00133       """
00134       Better command loop, with history file and tweaked readline
00135       completion delimiters.
00136       """
00137       old_completer_delims = readline.get_completer_delims()
00138       if self.histfile is not None:
00139         try:
00140           readline.read_history_file(self.histfile)
00141         except IOError:
00142           pass
00143       try:
00144         readline.set_completer_delims("".join(set(old_completer_delims) - set(self.identchars)))
00145         self.cmdloop()
00146       finally:
00147         if self.histfile is not None and readline.get_current_history_length():
00148           readline.write_history_file(self.histfile)
00149         readline.set_completer_delims(old_completer_delims)
00150 
00151   else:
00152 
00153     cmdloop_with_history = cmd.Cmd.cmdloop
00154 
00155 
00156 
00157 def yes_or_no(prompt, default = None, require_full_word = False):
00158   """
00159   Ask a yes-or-no question.
00160   """
00161   prompt = prompt.rstrip() + _yes_or_no_prompts[default]
00162   while True:
00163     answer = raw_input(prompt).strip().lower()
00164     if not answer and default is not None:
00165       return default
00166     if answer == "yes" or (not require_full_word and answer.startswith("y")):
00167       return True
00168     if answer == "no"  or (not require_full_word and answer.startswith("n")):
00169       return False
00170     print 'Please answer "yes" or "no"'
00171 
00172 _yes_or_no_prompts = {
00173   True  : ' ("yes" or "no" ["yes"]) ',
00174   False : ' ("yes" or "no" ["no"]) ',
00175   None  : ' ("yes" or "no") ' }
00176 
 All Classes Namespaces Files Functions Variables Properties