Package starcluster :: Module optcomplete
[hide private]
[frames] | no frames]

Source Code for Module starcluster.optcomplete

  1  #******************************************************************************\ 
  2  #* Copyright (c) 2003-2004, Martin Blais 
  3  #* All rights reserved. 
  4  #* 
  5  #* Redistribution and use in source and binary forms, with or without 
  6  #* modification, are permitted provided that the following conditions are 
  7  #* met: 
  8  #* 
  9  #* * Redistributions of source code must retain the above copyright 
 10  #*   notice, this list of conditions and the following disclaimer. 
 11  #* 
 12  #* * Redistributions in binary form must reproduce the above copyright 
 13  #*   notice, this list of conditions and the following disclaimer in the 
 14  #*   documentation and/or other materials provided with the distribution. 
 15  #* 
 16  #* * Neither the name of the Martin Blais, Furius, nor the names of its 
 17  #*   contributors may be used to endorse or promote products derived from 
 18  #*   this software without specific prior written permission. 
 19  #* 
 20  #* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  #* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  #* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 23  #* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 24  #* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 25  #* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 26  #* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 27  #* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 28  #* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 29  #* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 30  #* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 31  #******************************************************************************\ 
 32   
 33  """Automatic completion for optparse module. 
 34   
 35  This module provide automatic bash completion support for programs that use the 
 36  optparse module.  The premise is that the optparse options parser specifies 
 37  enough information (and more) for us to be able to generate completion strings 
 38  esily.  Another advantage of this over traditional completion schemes where the 
 39  completion strings are hard-coded in a separate bash source file, is that the 
 40  same code that parses the options is used to generate the completions, so the 
 41  completions is always up-to-date with the program itself. 
 42   
 43  In addition, we allow you specify a list of regular expressions or code that 
 44  define what kinds of files should be proposed as completions to this file if 
 45  needed.  If you want to implement more complex behaviour, you can instead 
 46  specify a function, which will be called with the current directory as an 
 47  argument. 
 48   
 49  You need to activate bash completion using the shell script function that comes 
 50  with optcomplete (see http://furius.ca/optcomplete for more details). 
 51   
 52  """ 
 53   
 54  __version__ = "$Revision$" 
 55  __author__ = "Martin Blais <blais@furius.ca>" 
 56   
 57  ## Bash Protocol Description 
 58  ## ------------------------- 
 59  ## 
 60  ## `COMP_CWORD' 
 61  ##      An index into `${COMP_WORDS}' of the word containing the current 
 62  ##      cursor position.  This variable is available only in shell 
 63  ##      functions invoked by the programmable completion facilities (*note 
 64  ##      Programmable Completion::). 
 65  ## 
 66  ## `COMP_LINE' 
 67  ##      The current command line.  This variable is available only in 
 68  ##      shell functions and external commands invoked by the programmable 
 69  ##      completion facilities (*note Programmable Completion::). 
 70  ## 
 71  ## `COMP_POINT' 
 72  ##      The index of the current cursor position relative to the beginning 
 73  ##      of the current command.  If the current cursor position is at the 
 74  ##      end of the current command, the value of this variable is equal to 
 75  ##      `${#COMP_LINE}'.  This variable is available only in shell 
 76  ##      functions and external commands invoked by the programmable 
 77  ##      completion facilities (*note Programmable Completion::). 
 78  ## 
 79  ## `COMP_WORDS' 
 80  ##      An array variable consisting of the individual words in the 
 81  ##      current command line.  This variable is available only in shell 
 82  ##      functions invoked by the programmable completion facilities (*note 
 83  ##      Programmable Completion::). 
 84  ## 
 85  ## `COMPREPLY' 
 86  ##      An array variable from which Bash reads the possible completions 
 87  ##      generated by a shell function invoked by the programmable 
 88  ##      completion facility (*note Programmable Completion::). 
 89   
 90   
 91  import sys, os 
 92  from os.path import * 
 93  import types 
 94  import re 
 95   
 96  from pprint import pprint, pformat 
 97   
 98  from optparse import OptionParser 
 99   
100  debugfn = '/tmp/completion-debug.log' # for debugging only 
101   
102 -class AllCompleter:
103 104 """Completes by listing all possible files in current directory.""" 105
106 - def __call__(self, pwd, line, point, prefix, suffix):
107 return os.listdir(pwd)
108
109 -class NoneCompleter:
110 111 """Generates empty completion list.""" 112
113 - def __call__(self, pwd, line, point, prefix, suffix):
114 return []
115
116 -class DirCompleter:
117 118 """Completes by listing subdirectories only.""" 119
120 - def __call__(self, pwd, line, point, prefix, suffix):
121 return filter(isdir, os.listdir(pwd))
122
123 -class RegexCompleter:
124 125 """Completes by filtering all possible files with the given list of 126 regexps.""" 127
128 - def __init__(self, regexlist, always_dirs=True):
129 self.always_dirs = always_dirs 130 131 if isinstance(regexlist, types.StringType): 132 regexlist = [regexlist] 133 self.regexlist = [] 134 for r in regexlist: 135 if isinstance(r, types.StringType): 136 r = re.compile(r) 137 self.regexlist.append(r)
138
139 - def __call__(self, pwd, line, point, prefix, suffix):
140 dn = dirname(prefix) 141 if dn: 142 pwd = dn 143 files = os.listdir(pwd) 144 ofiles = [] 145 for fn in files: 146 for r in self.regexlist: 147 if r.match(fn): 148 if dn: 149 fn = join(dn, fn) 150 ofiles.append(fn) 151 break 152 if self.always_dirs and isdir(fn): 153 ofiles.append(fn + '/') 154 return ofiles
155
156 -class ListCompleter:
157 158 """Completes by filtering using a fixed list of strings.""" 159
160 - def __init__(self, stringlist):
161 self.olist = stringlist
162
163 - def __call__(self, pwd, line, point, prefix, suffix):
164 return self.olist
165
166 -def extract_word(line, point):
167 168 """Return a prefix and suffix of the enclosing word. The character under 169 the cursor is the first character of the suffix.""" 170 171 wsre = re.compile('[ \t]') 172 173 if point < 0 or point > len(line): 174 return '', '' 175 176 preii = point - 1 177 while preii >= 0: 178 if wsre.match(line[preii]): 179 break 180 preii -= 1 181 preii += 1 182 183 sufii = point 184 while sufii < len(line): 185 if wsre.match(line[sufii]): 186 break 187 sufii += 1 188 189 return line[preii : point], line[point : sufii]
190
191 -def autocomplete(parser, 192 arg_completer=None, # means use default. 193 opt_completer=None, 194 subcmd_completer=None, 195 subcommands=None):
196 197 """Automatically detect if we are requested completing and if so generate 198 completion automatically from given parser. 199 200 'parser' is the options parser to use. 201 202 'arg_completer' is a callable object that gets invoked to produce a list of 203 completions for arguments completion (oftentimes files). 204 205 'opt_completer' is the default completer to the options that require a 206 value. 'subcmd_completer' is the default completer for the subcommand 207 arguments. 208 209 If 'subcommands' is specified, the script expects it to be a map of 210 command-name to an object of any kind. We are assuming that this object is 211 a map from command name to a pair of (options parser, completer) for the 212 command. If the value is not such a tuple, the method 213 'autocomplete(completer)' is invoked on the resulting object. 214 215 This will attempt to match the first non-option argument into a subcommand 216 name and if so will use the local parser in the corresponding map entry's 217 value. This is used to implement completion for subcommand syntax and will 218 not be needed in most cases.""" 219 220 # If we are not requested for complete, simply return silently, let the code 221 # caller complete. This is the normal path of execution. 222 if not os.environ.has_key('OPTPARSE_AUTO_COMPLETE'): 223 return 224 225 # Set default completers. 226 if arg_completer is None: 227 arg_completer = NoneCompleter() 228 if opt_completer is None: 229 opt_completer = NoneCompleter() 230 if subcmd_completer is None: 231 ## subcmd_completer = arg_completer 232 subcmd_completer = NoneCompleter() 233 234 # By default, completion will be arguments completion, unless we find out 235 # later we're trying to complete for an option. 236 completer = arg_completer 237 238 # 239 # Completing... 240 # 241 242 # Fetching inputs... not sure if we're going to use these. 243 244 # zsh's bashcompinit does not pass COMP_WORDS, replace with 245 # COMP_LINE for now... 246 if not os.environ.has_key('COMP_WORDS'): 247 os.environ['COMP_WORDS'] = os.environ['COMP_LINE'] 248 249 cwords = os.environ['COMP_WORDS'].split() 250 cline = os.environ['COMP_LINE'] 251 cpoint = int(os.environ['COMP_POINT']) 252 cword = int(os.environ['COMP_CWORD']) 253 254 255 # If requested, try subcommand syntax to find an options parser for that 256 # subcommand. 257 if subcommands: 258 assert isinstance(subcommands, types.DictType) 259 value = guess_first_nonoption(parser, subcommands) 260 if value: 261 if isinstance(value, types.ListType) or \ 262 isinstance(value, types.TupleType): 263 parser = value[0] 264 if len(value) > 1 and value[1]: 265 # override completer for command if it is present. 266 completer = value[1] 267 else: 268 completer = subcmd_completer 269 return autocomplete(parser, completer) 270 else: 271 # Call completion method on object. This should call 272 # autocomplete() recursively with appropriate arguments. 273 if hasattr(value, 'autocomplete'): 274 return value.autocomplete(subcmd_completer) 275 else: 276 sys.exit(1) # no completions for that command object 277 278 # Extract word enclosed word. 279 prefix, suffix = extract_word(cline, cpoint) 280 # The following would be less exact, but will work nonetheless . 281 # prefix, suffix = cwords[cword], None 282 283 # Look at previous word, if it is an option and it requires an argument, 284 # check for a local completer. If there is no completer, what follows 285 # directly cannot be another option, so mark to not add those to 286 # completions. 287 optarg = False 288 try: 289 # Look for previous word, which will be containing word if the option 290 # has an equals sign in it. 291 prev = None 292 if cword < len(cwords): 293 mo = re.search('(--.*)=(.*)', cwords[cword]) 294 if mo: 295 prev, prefix = mo.groups() 296 if not prev: 297 prev = cwords[cword - 1] 298 299 if prev and prev.startswith('-'): 300 option = parser.get_option(prev) 301 if option: 302 if option.nargs > 0: 303 optarg = True 304 if hasattr(option, 'completer'): 305 completer = option.completer 306 elif option.type != 'string': 307 completer = NoneCompleter() 308 else: 309 completer = opt_completer 310 # Warn user at least, it could help him figure out the problem. 311 elif hasattr(option, 'completer'): 312 raise SystemExit( 313 "Error: optparse option with a completer " 314 "does not take arguments: %s" % str(option)) 315 except KeyError: 316 pass 317 318 completions = [] 319 320 # Options completion. 321 if not optarg and (not prefix or prefix.startswith('-')): 322 completions += parser._short_opt.keys() 323 completions += parser._long_opt.keys() 324 # Note: this will get filtered properly below. 325 326 # File completion. 327 if completer and (not prefix or not prefix.startswith('-')): 328 329 # Call appropriate completer depending on type. 330 if isinstance(completer, types.StringType) or \ 331 isinstance(completer, types.ListType) or \ 332 isinstance(completer, types.TupleType): 333 334 completer = RegexCompleter(completer) 335 completions += completer(os.getcwd(), cline, cpoint, prefix, suffix) 336 337 338 elif isinstance(completer, types.FunctionType) or \ 339 isinstance(completer, types.LambdaType) or \ 340 isinstance(completer, types.ClassType) or \ 341 isinstance(completer, types.ObjectType): 342 completions += completer(os.getcwd(), cline, cpoint, prefix, suffix) 343 344 # Filter using prefix. 345 if prefix: 346 completions = filter(lambda x: x.startswith(prefix), completions) 347 348 # Print result. 349 print ' '.join(completions) 350 351 # Print debug output (if needed). You can keep a shell with 'tail -f' to 352 # the log file to monitor what is happening. 353 if debugfn: 354 f = open(debugfn, 'a') 355 print >> f, '---------------------------------------------------------' 356 print >> f, 'CWORDS', cwords 357 print >> f, 'CLINE', cline 358 print >> f, 'CPOINT', cpoint 359 print >> f, 'CWORD', cword 360 print >> f, '\nShort options' 361 print >> f, pformat(parser._short_opt) 362 print >> f, '\nLong options' 363 print >> f, pformat(parser._long_opt) 364 print >> f, 'Prefix/Suffix:', prefix, suffix 365 print >> f, 'completions', completions 366 f.close() 367 368 # Exit with error code (we do not let the caller continue on purpose, this 369 # is a run for completions only.) 370 sys.exit(1)
371
372 -def error_override(self, msg):
373 """Hack to keep OptionParser from writing to sys.stderr when 374 calling self.exit from self.error""" 375 self.exit(2, msg=None)
376
377 -def guess_first_nonoption(gparser, subcmds_map):
378 379 """Given a global options parser, try to guess the first non-option without 380 generating an exception. This is used for scripts that implement a 381 subcommand syntax, so that we can generate the appropriate completions for 382 the subcommand.""" 383 384 import copy 385 gparser = copy.deepcopy(gparser) 386 def print_usage_nousage (self, file=None): 387 pass
388 gparser.print_usage = print_usage_nousage 389 390 prev_interspersed = gparser.allow_interspersed_args # save state to restore 391 gparser.disable_interspersed_args() 392 393 cwords = os.environ['COMP_WORDS'].split() 394 395 # save original error_func so we can put it back after the hack 396 error_func = gparser.error 397 try: 398 try: 399 instancemethod = type(OptionParser.error) 400 # hack to keep OptionParser from wrinting to sys.stderr 401 gparser.error = instancemethod(error_override, gparser, OptionParser) 402 gopts, args = gparser.parse_args(cwords[1:]) 403 except SystemExit: 404 return None 405 finally: 406 # undo the hack and restore original OptionParser error function 407 gparser.error = instancemethod(error_func, gparser, OptionParser) 408 409 value = None 410 if args: 411 subcmdname = args[0] 412 try: 413 value = subcmds_map[subcmdname] 414 except KeyError: 415 pass 416 417 gparser.allow_interspersed_args = prev_interspersed # restore state 418 419 return value # can be None, indicates no command chosen. 420
421 -class CmdComplete:
422 423 """Simple default base class implementation for a subcommand that supports 424 command completion. This class is assuming that there might be a method 425 addopts(self, parser) to declare options for this subcommand, and an 426 optional completer data member to contain command-specific completion. Of 427 course, you don't really have to use this, but if you do it is convenient to 428 have it here.""" 429
430 - def autocomplete(self, completer):
431 import logging 432 logging.disable(logging.CRITICAL) 433 import optparse 434 parser = optparse.OptionParser(self.__doc__.strip()) 435 if hasattr(self, 'addopts'): 436 self.addopts(parser) 437 if hasattr(self, 'completer'): 438 completer = self.completer 439 logging.disable(logging.NOTSET) 440 return autocomplete(parser, completer)
441 442
443 -def test():
444 print extract_word("extraire un mot d'une phrase", 11) 445 print extract_word("extraire un mot d'une phrase", 12) 446 print extract_word("extraire un mot d'une phrase", 13) 447 print extract_word("extraire un mot d'une phrase", 14) 448 print extract_word("extraire un mot d'une phrase", 0) 449 print extract_word("extraire un mot d'une phrase", 28) 450 print extract_word("extraire un mot d'une phrase", 29) 451 print extract_word("extraire un mot d'une phrase", -2) 452 print extract_word("optcomplete-test do", 19)
453 454 if __name__ == '__main__': 455 test() 456