PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/grizzled/grizzled/history.py

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