history.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. from __future__ import unicode_literals
  2. from abc import ABCMeta, abstractmethod
  3. from six import with_metaclass
  4. import datetime
  5. import os
  6. __all__ = (
  7. 'FileHistory',
  8. 'History',
  9. 'InMemoryHistory',
  10. )
  11. class History(with_metaclass(ABCMeta, object)):
  12. """
  13. Base ``History`` interface.
  14. """
  15. @abstractmethod
  16. def append(self, string):
  17. " Append string to history. "
  18. @abstractmethod
  19. def __getitem__(self, key):
  20. " Return one item of the history. It should be accessible like a `list`. "
  21. @abstractmethod
  22. def __iter__(self):
  23. " Iterate through all the items of the history. Cronologically. "
  24. @abstractmethod
  25. def __len__(self):
  26. " Return the length of the history. "
  27. def __bool__(self):
  28. """
  29. Never evaluate to False, even when the history is empty.
  30. (Python calls __len__ if __bool__ is not implemented.)
  31. This is mainly to allow lazy evaluation::
  32. x = history or InMemoryHistory()
  33. """
  34. return True
  35. __nonzero__ = __bool__ # For Python 2.
  36. class InMemoryHistory(History):
  37. """
  38. :class:`.History` class that keeps a list of all strings in memory.
  39. """
  40. def __init__(self):
  41. self.strings = []
  42. def append(self, string):
  43. self.strings.append(string)
  44. def __getitem__(self, key):
  45. return self.strings[key]
  46. def __iter__(self):
  47. return iter(self.strings)
  48. def __len__(self):
  49. return len(self.strings)
  50. class FileHistory(History):
  51. """
  52. :class:`.History` class that stores all strings in a file.
  53. """
  54. def __init__(self, filename):
  55. self.strings = []
  56. self.filename = filename
  57. self._load()
  58. def _load(self):
  59. lines = []
  60. def add():
  61. if lines:
  62. # Join and drop trailing newline.
  63. string = ''.join(lines)[:-1]
  64. self.strings.append(string)
  65. if os.path.exists(self.filename):
  66. with open(self.filename, 'rb') as f:
  67. for line in f:
  68. line = line.decode('utf-8')
  69. if line.startswith('+'):
  70. lines.append(line[1:])
  71. else:
  72. add()
  73. lines = []
  74. add()
  75. def append(self, string):
  76. self.strings.append(string)
  77. # Save to file.
  78. with open(self.filename, 'ab') as f:
  79. def write(t):
  80. f.write(t.encode('utf-8'))
  81. write('\n# %s\n' % datetime.datetime.now())
  82. for line in string.split('\n'):
  83. write('+%s\n' % line)
  84. def __getitem__(self, key):
  85. return self.strings[key]
  86. def __iter__(self):
  87. return iter(self.strings)
  88. def __len__(self):
  89. return len(self.strings)