Package grizzled :: Module history
[hide private]
[frames] | no frames]

Source Code for Module grizzled.history

  1  """ 
  2  ``grizzled.history`` provides a command line history capability that 
  3  provides the same interface across different history implementations. 
  4  Currently, it supports three history implementations: 
  5   
  6  - `GNU Readline`_, which is built into versions of Python on the Mac 
  7    and Unix systems 
  8  - `pyreadline`_, which many people use on Windows systems 
  9  - A dummy fallback history implementation that does nothing, for when readline 
 10    isn't available. 
 11     
 12  The `History` class provides the interface and some common methods for 
 13  all history operations. 
 14   
 15  .. _pyreadline: http://ipython.scipy.org/dist/ 
 16  .. _GNU Readline: http://cnswww.cns.cwru.edu/php/chet/readline/rluserman.html 
 17   
 18  To get the appropriate History implementation for the current platform, 
 19  simply call the ``get_history()`` factory method. 
 20  """ 
 21   
 22  from __future__ import with_statement 
 23   
 24  __docformat__ = "restructuredtext en" 
 25   
 26  # --------------------------------------------------------------------------- 
 27  # Imports 
 28  # --------------------------------------------------------------------------- 
 29   
 30  import re 
 31  import sys 
 32  import logging 
 33  import copy 
 34   
 35  from grizzled.decorators import abstract 
 36  from grizzled.exception import ExceptionWithMessage 
 37   
 38  # --------------------------------------------------------------------------- 
 39  # Exports 
 40  # --------------------------------------------------------------------------- 
 41   
 42  __all__ = ['get_history', 'History', 'DEFAULT_MAXLENGTH', 'HistoryError'] 
 43  __docformat__ = 'restructuredtext' 
 44   
 45  # --------------------------------------------------------------------------- 
 46  # Constants 
 47  # --------------------------------------------------------------------------- 
 48   
 49  DEFAULT_MAXLENGTH = 512 
 50   
 51  # --------------------------------------------------------------------------- 
 52  # Globals 
 53  # --------------------------------------------------------------------------- 
 54   
 55  log = logging.getLogger('history') 
 56  _have_readline = False 
 57  _have_pyreadline = False 
 58   
 59  try: 
 60      import readline 
 61      _have_readline = True 
 62   
 63      # Is it pyreadline? If so, it's not quite the same. 
 64   
 65      try: 
 66          _have_pyreadline = readline.rl.__module__.startswith('pyreadline.') 
 67      except AttributeError: 
 68          pass 
 69  except ImportError: 
 70      pass 
71 72 # --------------------------------------------------------------------------- 73 # Functions 74 # --------------------------------------------------------------------------- 75 76 -def get_history(verbose=True):
77 """ 78 Factory method to create an appropriate History object. 79 80 :Parameters: 81 verbose : bool 82 ``True`` to display a message on standard output about what 83 history management mechanism is being used. 84 85 :rtype: ``History`` 86 :return: the ``History`` object 87 """ 88 global _have_readline 89 global _have_pyreadline 90 result = None 91 if _have_pyreadline: 92 if verbose: 93 print 'Using pyreadline for history management.' 94 result = PyReadlineHistory() 95 96 elif _have_readline: 97 if verbose: 98 print 'Using readline for history management.' 99 result = ReadlineHistory() 100 101 else: 102 print 'WARNING: Readline unavailable. There will be no history.' 103 result = DummyHistory() 104 105 result.max_length = DEFAULT_MAXLENGTH 106 return result
107
108 # --------------------------------------------------------------------------- 109 # Classes 110 # --------------------------------------------------------------------------- 111 112 -class HistoryError(ExceptionWithMessage):
113 """ 114 Thrown to indicate history errors, when another exception won't do. 115 """ 116 pass
117
118 -class History(object):
119 """ 120 Base class for history implementations. All concrete history 121 implementations must extend this class. 122 """
123 - def __init__(self):
125
126 - def show(self, out=sys.stdout):
127 """ 128 Dump the history to a file-like object (defaulting to standard output). 129 130 :Parameters: 131 out : file 132 Where to dump the history. 133 """ 134 for i in range(1, self.total + 1): 135 print >> out, '%4d: %s' % (i, self.get_item(i))
136
137 - def get_last_matching_item(self, command_name):
138 """ 139 Get the most recently entered item that matches ``command_name`` 140 at the beginning. 141 142 :Parameters: 143 command_name : str 144 The string to match against the commands in the history 145 146 :rtype: str 147 :return: the matching string, or ``None`` 148 """ 149 result = None 150 for i in range(self.get_total(), 0, -1): 151 s = self.get_item(i) 152 tokens = s.split(None, 1) 153 if len(command_name) <= len(s): 154 if s[0:len(command_name)] == command_name: 155 result = s 156 break 157 return result
158
159 - def get_last_item(self):
160 """ 161 Get the most recent item in the history. 162 163 :rtype: str 164 :return: The most recent command, or ``None`` 165 """ 166 return self.get_item(self.get_total() - 1)
167
168 - def get_item(self, index):
169 """ 170 Get an item from the history. 171 172 :Parameters: 173 index : int 174 0-based index of the item to get. The larger the index 175 value, the more recent the entry 176 177 :rtype: str 178 :return: the item at that index 179 180 :raise IndexError: Index out of range 181 """ 182 return None
183
184 - def set_completer_delims(self, s):
185 """ 186 Set the completer delimiters--the characters that delimit tokens 187 that are eligible for completion. 188 189 :Parameters: 190 s : str 191 The delimiters 192 """ 193 pass
194
195 - def get_completer_delims(self):
196 """ 197 Get the completer delimiters--the characters that delimit tokens 198 that are eligible for completion. 199 200 :rtype: str 201 :return: the delimiters 202 """ 203 return ''
204 205 @property
206 - def total(self):
207 """ 208 The total number number of commands in the history. Identical to 209 calling ``get_total()``. 210 """ 211 return self.get_total()
212
213 - def get_total(self):
214 """ 215 Get the total number number of commands in the history. Identical to 216 the ``total`` property. 217 218 :rtype: int 219 :return: the number of commands in the history 220 """ 221 return 0
222
223 - def __set_max_length(self, n):
224 return self.set_max_length(n)
225
226 - def __get_max_length(self):
227 return self.get_max_length()
228 229 maxLength = property(__get_max_length, __set_max_length, 230 doc="The maximum length of the history") 231 232 @abstract
233 - def get_max_length(self):
234 """ 235 Get the maximum length of the history. This isn't the maximum number 236 of entries in the in-memory history buffer; instead, it's the maximum 237 number of entries that will be saved to the history file. Subclasses 238 *must* provide an implementation of this method. 239 240 :rtype: int 241 :return: the maximum saved size of the history 242 """ 243 pass
244 245 @abstract
246 - def set_max_length(self, n):
247 """ 248 Set the maximum length of the history. This isn't the maximum number 249 of entries in the in-memory history buffer; instead, it's the maximum 250 number of entries that will be saved to the history file. Subclasses 251 *must* provide an implementation of this method. 252 253 :Parameters: 254 n : int 255 the maximum saved size of the history 256 """ 257 pass
258 259 @abstract
260 - def add_item(self, line):
261 """ 262 Add (append) a line to the history buffer. Subclasses *must* provide 263 an implementation of this method. 264 265 :Parameters: 266 line : str 267 the command to append to the history 268 """ 269 pass
270 271 @abstract
272 - def remove_item(self, i):
273 """ 274 Remove a line from the history buffer. Subclasses *must* provide an 275 implementation of this method. 276 277 :Parameters: 278 i : int 279 the 0-based index of the item to be removed 280 """ 281 pass
282 283 @abstract
284 - def clear_history(self):
285 """ 286 Clear the history buffer. Subclasses *must* provide an 287 implementation of this method. 288 """ 289 pass
290
291 - def get_history_list(self):
292 """ 293 Get a copy of the history buffer. 294 295 :rtype: list 296 :return: a list of commands from the history 297 """ 298 result = [] 299 for i in range(1, self.total + 1): 300 result += [self.get_item(i)] 301 302 return result
303
304 - def remove_matches(self, regexp_string):
305 """ 306 Remove all history items that match a regular expression. 307 308 :Parameters: 309 regexp_string : str 310 the uncompiled regular expression to match 311 312 :raise HistoryError: bad regular expression 313 """ 314 try: 315 pat = re.compile(regexp_string) 316 except: 317 raise HistoryError(str(sys.exc_info[1])) 318 319 buf = [] 320 321 for i in range(1, self.total + 1): 322 s = self.get_item(i) 323 if not pat.match(s): 324 buf += [s] 325 326 self.replace_history(buf)
327
328 - def cut_back_to(self, index):
329 """ 330 Cut the history back to the specified index, removing all entries 331 more recent than that index. 332 333 :Parameters: 334 index : int 335 the index of the command that should become the last command 336 in the history 337 338 :raise IndexError: index out of range 339 """ 340 if (index > 0) and (index <= self.total): 341 buf = [] 342 for i in range(1, index): 343 buf += [self.get_item(i)] 344 345 self.replace_history(buf)
346
347 - def replace_history(self, commands):
348 """ 349 Replace the entire contents of the history with another set of values 350 351 :Parameters: 352 commands : list 353 List of strings to put in the history after clearing it of any 354 existing entries 355 """ 356 self.clear_history() 357 for command in commands: 358 self.add_item(command, force=True)
359
360 - def save_history_file(self, path):
361 """ 362 Save the history to a file. The file is overwritten with the contents 363 of the history buffer. 364 365 :Parameters: 366 path : str 367 Path to the history file to receive the output. 368 369 :raise IOError: Unable to open file 370 """ 371 log.debug('Writing history file "%s"' % path) 372 with open(path, 'w') as f: 373 for i in range(1, self.total + 1): 374 f.write(self.get_item(i) + '\n')
375
376 - def load_history_file(self, path):
377 """ 378 Load the history buffer with the contents of a file, completely 379 replacing the in-memory history with the file's contents. 380 381 :Parameters: 382 path : str 383 Path to the history file to read 384 385 :raise IOError: Unable to open file 386 """ 387 log.debug('Loading history file "%s"' % path) 388 with open(path, 'r') as f: 389 buf = [] 390 for line in f: 391 buf += [line.strip()] 392 393 max = self.get_max_length() 394 if len(buf) > max: 395 buf = buf[max] 396 self.replace_history(buf)
397
398 -class ReadlineHistory(History):
399
400 - def __init__(self):
401 global _have_readline 402 assert(_have_readline) 403 History.__init__(self)
404
405 - def get_item(self, index):
406 return readline.get_history_item(index)
407
408 - def get_total(self):
409 return readline.get_current_history_length()
410
411 - def set_completer_delims(self, s):
413
414 - def get_completer_delims(self,):
416
417 - def remove_item(self, index):
418 # readline.remove_history_item() doesn't seem to work. Do it the 419 # hard way. 420 421 #try: 422 # readline.remove_history_item(i) 423 #except ValueError: 424 # pass 425 426 buf = [] 427 for i in range(1, self.total + 1): 428 if i != index: 429 buf += self.get_item(i) 430 431 self.clear_history() 432 for s in buf: 433 readline.add_history(s)
434
435 - def clear_history(self):
436 try: 437 readline.clear_history() 438 except AttributeError: 439 len = self.get_max_length() 440 readline.set_history_length(0) 441 readline.set_history_length(len)
442
443 - def get_max_length(self):
444 return readline.get_history_length()
445
446 - def set_max_length(self, n):
447 readline.set_history_length(n)
448
449 - def add_item(self, line, force=False):
450 readline.add_history(line)
451
452 -class PyReadlineHistory(ReadlineHistory):
453 - def __init__(self):
454 global _have_pyreadline 455 assert(_have_pyreadline) 456 ReadlineHistory.__init__(self)
457
458 - def get_item(self, index):
459 return self.__get_buf()[index - 1].get_line_text()
460
461 - def get_total(self):
462 return len(self.__get_buf())
463
464 - def set_completer_delims(self, s):
466
467 - def get_completer_delims(self):
469
470 - def remove_item(self, index):
471 buf = copy.deepcopy(self.__get_buf()) 472 self.clear_history() 473 for s in buf: 474 readline.add_history(s)
475
476 - def clear_history(self):
478
479 - def get_max_length(self):
480 return readline.get_history_length()
481
482 - def set_max_length(self, n):
483 readline.set_history_length(n)
484
485 - def add_item(self, line, force=False):
486 # Kludge. pyreadline is a pain in the ass. 487 from pyreadline import lineobj 488 from pyreadline.unicode_helper import ensure_unicode 489 490 line = ensure_unicode(line.rstrip()) 491 readline.add_history(lineobj.ReadLineTextBuffer(line))
492
493 - def __get_buf(self):
494 return readline.rl._history.history
495
496 -class DummyHistory(History):
497
498 - def __init__(self):
499 History.__init__(self)
500
501 - def remove_item(self, i):
502 pass
503
504 - def get_item(self, index):
505 return None
506
507 - def get_history_list(self):
508 return []
509
510 - def get_total(self):
511 return 0
512
513 - def get_max_length(self):
514 return 0
515
516 - def set_max_length(self, n):
517 pass
518
519 - def clear_history(self):
520 pass
521
522 - def add_item(self, line, force=False):
523 pass
524 525 # --------------------------------------------------------------------------- 526 # Main 527 # --------------------------------------------------------------------------- 528 529 if __name__ == "__main__": 530 h = getHistory() 531