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