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 os 
 92  import sys 
 93  import types 
 94  import re 
 95   
 96  from pprint import pformat 
 97   
 98  from optparse import OptionParser 
 99   
100  debugfn = '/tmp/completion-debug.log'  # for debugging only 
101   
102   
103 -class AllCompleter(object):
104 105 """Completes by listing all possible files in current directory.""" 106
107 - def __call__(self, pwd, line, point, prefix, suffix):
108 return os.listdir(pwd)
109 110
111 -class NoneCompleter(object):
112 113 """Generates empty completion list.""" 114
115 - def __call__(self, pwd, line, point, prefix, suffix):
116 return []
117 118
119 -class DirCompleter(object):
120 121 """Completes by listing subdirectories only.""" 122
123 - def __call__(self, pwd, line, point, prefix, suffix):
124 return filter(os.path.isdir, os.listdir(pwd))
125 126
127 -class RegexCompleter(object):
128 129 """Completes by filtering all possible files with the given list of 130 regexps.""" 131
132 - def __init__(self, regexlist, always_dirs=True):
133 self.always_dirs = always_dirs 134 135 if isinstance(regexlist, types.StringType): 136 regexlist = [regexlist] 137 self.regexlist = [] 138 for r in regexlist: 139 if isinstance(r, types.StringType): 140 r = re.compile(r) 141 self.regexlist.append(r)
142
143 - def __call__(self, pwd, line, point, prefix, suffix):
144 dn = os.path.dirname(prefix) 145 if dn: 146 pwd = dn 147 files = os.listdir(pwd) 148 ofiles = [] 149 for fn in files: 150 for r in self.regexlist: 151 if r.match(fn): 152 if dn: 153 fn = os.path.join(dn, fn) 154 ofiles.append(fn) 155 break 156 if self.always_dirs and os.path.isdir(fn): 157 ofiles.append(fn + '/') 158 return ofiles
159 160
161 -class ListCompleter(object):
162 163 """Completes by filtering using a fixed list of strings.""" 164
165 - def __init__(self, stringlist):
166 self.olist = stringlist
167
168 - def __call__(self, pwd, line, point, prefix, suffix):
169 return self.olist
170 171
172 -def extract_word(line, point):
173 174 """Return a prefix and suffix of the enclosing word. The character under 175 the cursor is the first character of the suffix.""" 176 177 wsre = re.compile('[ \t]') 178 179 if point < 0 or point > len(line): 180 return '', '' 181 182 preii = point - 1 183 while preii >= 0: 184 if wsre.match(line[preii]): 185 break 186 preii -= 1 187 preii += 1 188 189 sufii = point 190 while sufii < len(line): 191 if wsre.match(line[sufii]): 192 break 193 sufii += 1 194 195 return line[preii:point], line[point:sufii]
196 197
198 -def autocomplete(parser, 199 arg_completer=None, # means use default. 200 opt_completer=None, 201 subcmd_completer=None, 202 subcommands=None):
203 204 """Automatically detect if we are requested completing and if so generate 205 completion automatically from given parser. 206 207 'parser' is the options parser to use. 208 209 'arg_completer' is a callable object that gets invoked to produce a list of 210 completions for arguments completion (oftentimes files). 211 212 'opt_completer' is the default completer to the options that require a 213 value. 'subcmd_completer' is the default completer for the subcommand 214 arguments. 215 216 If 'subcommands' is specified, the script expects it to be a map of 217 command-name to an object of any kind. We are assuming that this object is 218 a map from command name to a pair of (options parser, completer) for the 219 command. If the value is not such a tuple, the method 220 'autocomplete(completer)' is invoked on the resulting object. 221 222 This will attempt to match the first non-option argument into a subcommand 223 name and if so will use the local parser in the corresponding map entry's 224 value. This is used to implement completion for subcommand syntax and will 225 not be needed in most cases.""" 226 227 # If we are not requested for complete, simply return silently, let the 228 # code caller complete. This is the normal path of execution. 229 if 'OPTPARSE_AUTO_COMPLETE' not in os.environ: 230 return 231 232 # Set default completers. 233 if arg_completer is None: 234 arg_completer = NoneCompleter() 235 if opt_completer is None: 236 opt_completer = NoneCompleter() 237 if subcmd_completer is None: 238 ## subcmd_completer = arg_completer 239 subcmd_completer = NoneCompleter() 240 241 # By default, completion will be arguments completion, unless we find out 242 # later we're trying to complete for an option. 243 completer = arg_completer 244 245 # 246 # Completing... 247 # 248 249 # Fetching inputs... not sure if we're going to use these. 250 251 # zsh's bashcompinit does not pass COMP_WORDS, replace with 252 # COMP_LINE for now... 253 if not 'COMP_WORDS' in os.environ: 254 os.environ['COMP_WORDS'] = os.environ['COMP_LINE'] 255 256 cwords = os.environ['COMP_WORDS'].split() 257 cline = os.environ['COMP_LINE'] 258 cpoint = int(os.environ['COMP_POINT']) 259 cword = int(os.environ['COMP_CWORD']) 260 261 # If requested, try subcommand syntax to find an options parser for that 262 # subcommand. 263 if subcommands: 264 assert isinstance(subcommands, types.DictType) 265 value = guess_first_nonoption(parser, subcommands) 266 if value: 267 if isinstance(value, types.ListType) or \ 268 isinstance(value, types.TupleType): 269 parser = value[0] 270 if len(value) > 1 and value[1]: 271 # override completer for command if it is present. 272 completer = value[1] 273 else: 274 completer = subcmd_completer 275 return autocomplete(parser, completer) 276 else: 277 # Call completion method on object. This should call 278 # autocomplete() recursively with appropriate arguments. 279 if hasattr(value, 'autocomplete'): 280 return value.autocomplete(subcmd_completer) 281 else: 282 sys.exit(1) # no completions for that command object 283 284 # Extract word enclosed word. 285 prefix, suffix = extract_word(cline, cpoint) 286 # The following would be less exact, but will work nonetheless . 287 # prefix, suffix = cwords[cword], None 288 289 # Look at previous word, if it is an option and it requires an argument, 290 # check for a local completer. If there is no completer, what follows 291 # directly cannot be another option, so mark to not add those to 292 # completions. 293 optarg = False 294 try: 295 # Look for previous word, which will be containing word if the option 296 # has an equals sign in it. 297 prev = None 298 if cword < len(cwords): 299 mo = re.search('(--.*)=(.*)', cwords[cword]) 300 if mo: 301 prev, prefix = mo.groups() 302 if not prev: 303 prev = cwords[cword - 1] 304 305 if prev and prev.startswith('-'): 306 option = parser.get_option(prev) 307 if option: 308 if option.nargs > 0: 309 optarg = True 310 if hasattr(option, 'completer'): 311 completer = option.completer 312 elif option.type != 'string': 313 completer = NoneCompleter() 314 else: 315 completer = opt_completer 316 # Warn user at least, it could help him figure out the problem. 317 elif hasattr(option, 'completer'): 318 raise SystemExit( 319 "Error: optparse option with a completer " 320 "does not take arguments: %s" % str(option)) 321 except KeyError: 322 pass 323 324 completions = [] 325 326 # Options completion. 327 if not optarg and (not prefix or prefix.startswith('-')): 328 completions += parser._short_opt.keys() 329 completions += parser._long_opt.keys() 330 # Note: this will get filtered properly below. 331 332 # File completion. 333 if completer and (not prefix or not prefix.startswith('-')): 334 335 # Call appropriate completer depending on type. 336 if isinstance(completer, types.StringType) or \ 337 isinstance(completer, types.ListType) or \ 338 isinstance(completer, types.TupleType): 339 completer = RegexCompleter(completer) 340 completions += completer(os.getcwd(), cline, 341 cpoint, prefix, suffix) 342 elif isinstance(completer, types.FunctionType) or \ 343 isinstance(completer, types.LambdaType) or \ 344 isinstance(completer, types.ClassType) or \ 345 isinstance(completer, types.ObjectType): 346 completions += completer(os.getcwd(), cline, 347 cpoint, prefix, suffix) 348 349 # Filter using prefix. 350 if prefix: 351 completions = filter(lambda x: x.startswith(prefix), completions) 352 353 # Print result. 354 print ' '.join(completions) 355 356 # Print debug output (if needed). You can keep a shell with 'tail -f' to 357 # the log file to monitor what is happening. 358 if debugfn: 359 f = open(debugfn, 'a') 360 print >> f, '---------------------------------------------------------' 361 print >> f, 'CWORDS', cwords 362 print >> f, 'CLINE', cline 363 print >> f, 'CPOINT', cpoint 364 print >> f, 'CWORD', cword 365 print >> f, '\nShort options' 366 print >> f, pformat(parser._short_opt) 367 print >> f, '\nLong options' 368 print >> f, pformat(parser._long_opt) 369 print >> f, 'Prefix/Suffix:', prefix, suffix 370 print >> f, 'completions', completions 371 f.close() 372 373 # Exit with error code (we do not let the caller continue on purpose, this 374 # is a run for completions only.) 375 sys.exit(1)
376 377
378 -def error_override(self, msg):
379 """Hack to keep OptionParser from writing to sys.stderr when 380 calling self.exit from self.error""" 381 self.exit(2, msg=None)
382 383
384 -def guess_first_nonoption(gparser, subcmds_map):
385 386 """Given a global options parser, try to guess the first non-option without 387 generating an exception. This is used for scripts that implement a 388 subcommand syntax, so that we can generate the appropriate completions for 389 the subcommand.""" 390 391 import copy 392 gparser = copy.deepcopy(gparser) 393 394 def print_usage_nousage(self, file=None): 395 pass
396 gparser.print_usage = print_usage_nousage 397 398 # save state to restore 399 prev_interspersed = gparser.allow_interspersed_args 400 gparser.disable_interspersed_args() 401 402 cwords = os.environ['COMP_WORDS'].split() 403 404 # save original error_func so we can put it back after the hack 405 error_func = gparser.error 406 try: 407 try: 408 instancemethod = type(OptionParser.error) 409 # hack to keep OptionParser from wrinting to sys.stderr 410 gparser.error = instancemethod(error_override, 411 gparser, OptionParser) 412 gopts, args = gparser.parse_args(cwords[1:]) 413 except SystemExit: 414 return None 415 finally: 416 # undo the hack and restore original OptionParser error function 417 gparser.error = instancemethod(error_func, gparser, OptionParser) 418 419 value = None 420 if args: 421 subcmdname = args[0] 422 try: 423 value = subcmds_map[subcmdname] 424 except KeyError: 425 pass 426 427 gparser.allow_interspersed_args = prev_interspersed # restore state 428 429 return value # can be None, indicates no command chosen. 430 431
432 -class CmdComplete(object):
433 434 """Simple default base class implementation for a subcommand that supports 435 command completion. This class is assuming that there might be a method 436 addopts(self, parser) to declare options for this subcommand, and an 437 optional completer data member to contain command-specific completion. Of 438 course, you don't really have to use this, but if you do it is convenient 439 to have it here.""" 440
441 - def autocomplete(self, completer):
442 import logging 443 logging.disable(logging.CRITICAL) 444 import optparse 445 parser = optparse.OptionParser(self.__doc__.strip()) 446 if hasattr(self, 'addopts'): 447 self.addopts(parser) 448 if hasattr(self, 'completer'): 449 completer = self.completer 450 logging.disable(logging.NOTSET) 451 return autocomplete(parser, completer)
452 453
454 -def test():
455 print extract_word("extraire un mot d'une phrase", 11) 456 print extract_word("extraire un mot d'une phrase", 12) 457 print extract_word("extraire un mot d'une phrase", 13) 458 print extract_word("extraire un mot d'une phrase", 14) 459 print extract_word("extraire un mot d'une phrase", 0) 460 print extract_word("extraire un mot d'une phrase", 28) 461 print extract_word("extraire un mot d'une phrase", 29) 462 print extract_word("extraire un mot d'une phrase", -2) 463 print extract_word("optcomplete-test do", 19)
464 465 if __name__ == '__main__': 466 test() 467