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
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
40
41
42 __all__ = ['get_history', 'History', 'DEFAULT_MAXLENGTH', 'HistoryError']
43 __docformat__ = 'restructuredtext'
44
45
46
47
48
49 DEFAULT_MAXLENGTH = 512
50
51
52
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
64
65 try:
66 _have_pyreadline = readline.rl.__module__.startswith('pyreadline.')
67 except AttributeError:
68 pass
69 except ImportError:
70 pass
71
72
73
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
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
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
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
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
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
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):
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
413
416
417 - def remove_item(self, index):
418
419
420
421
422
423
424
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):
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
466
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
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):
500
501 - def remove_item(self, i):
503
504 - def get_item(self, index):
506
509
510 - def get_total(self):
512
513 - def get_max_length(self):
515
516 - def set_max_length(self, n):
518
519 - def clear_history(self):
521
522 - def add_item(self, line, force=False):
524
525
526
527
528
529 if __name__ == "__main__":
530 h = getHistory()
531