From 6304565a9ab13412557e162f9277533400f003b3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 9 Jun 2016 13:03:53 +0200 Subject: [PATCH] Refactor browser.history - Rename HistoryEntry to Entry - Move history line parsing from WebHistory.async_read to Entry.from_str - Improve errors for invalid history lines - Pass history directory/filename from the outside to WebHistory - Clear temp history after reading it when async_read is done --- qutebrowser/browser/history.py | 87 +++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index 465dffb0e..1c3d7d5a6 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -31,7 +31,7 @@ from qutebrowser.config import config from qutebrowser.misc import lineparser -class HistoryEntry: +class Entry: """A single entry in the web history. @@ -57,6 +57,30 @@ class HistoryEntry: def __str__(self): return '{} {} {}'.format(int(self.atime), self.url_string, self.title) + def __eq__(self, other): + return (self.atime == other.atime and + self.title == other.title and + self.url_string == other.url_string and + self.hidden == other.hidden) + + @classmethod + def from_str(cls, line): + data = line.split(maxsplit=2) + if len(data) == 2: + atime, url = data + title = "" + elif len(data) == 3: + atime, url, title = data + else: + raise ValueError("2 or 3 fields expected") + if atime.startswith('\0'): + log.init.debug( + "Removing NUL bytes from entry {!r} - see " + "https://github.com/The-Compiler/qutebrowser/issues/" + "670".format(data)) + atime = atime.lstrip('\0') + return cls(atime, url, title) + class WebHistoryInterface(QWebHistoryInterface): @@ -92,8 +116,9 @@ class WebHistory(QObject): Attributes: history_dict: An OrderedDict of URLs read from the on-disk history. + _hist_dir: The directory to store the history in _lineparser: The AppendLineParser used to save the history. - _new_history: A list of HistoryEntry items of the current session. + _new_history: A list of Entry items of the current session. _saved_count: How many HistoryEntries have been written to disk. _initial_read_started: Whether async_read was called. _initial_read_done: Whether async_read has completed. @@ -101,24 +126,25 @@ class WebHistory(QObject): async_read was called. Signals: - add_completion_item: Emitted before a new HistoryEntry is added. - arg: The new HistoryEntry. - item_added: Emitted after a new HistoryEntry is added. - arg: The new HistoryEntry. + add_completion_item: Emitted before a new Entry is added. + arg: The new Entry. + item_added: Emitted after a new Entry is added. + arg: The new Entry. cleared: Emitted after the history is cleared. """ - add_completion_item = pyqtSignal(HistoryEntry) - item_added = pyqtSignal(HistoryEntry) + add_completion_item = pyqtSignal(Entry) + item_added = pyqtSignal(Entry) cleared = pyqtSignal() async_read_done = pyqtSignal() - def __init__(self, parent=None): + def __init__(self, hist_dir, hist_name, parent=None): super().__init__(parent) self._initial_read_started = False self._initial_read_done = False - self._lineparser = lineparser.AppendLineParser( - standarddir.data(), 'history', parent=self) + self._hist_dir = hist_dir + self._lineparser = lineparser.AppendLineParser(hist_dir, hist_name, + parent=self) self.history_dict = collections.OrderedDict() self._temp_history = collections.OrderedDict() self._new_history = [] @@ -145,7 +171,7 @@ class WebHistory(QObject): return self._initial_read_started = True - if standarddir.data() is None: + if self._hist_dir is None: self._initial_read_done = True self.async_read_done.emit() assert not self._temp_history @@ -154,32 +180,23 @@ class WebHistory(QObject): with self._lineparser.open(): for line in self._lineparser: yield - data = line.rstrip().split(maxsplit=2) - if not data: - # empty line + + line = line.rstrip() + if not line: continue - elif len(data) == 2: - atime, url = data - title = "" - elif len(data) == 3: - atime, url, title = data - else: - # other malformed line - log.init.warning("Invalid history entry {!r}!".format( - line)) + + try: + entry = Entry.from_str(line) + except ValueError as e: + log.init.warning("Invalid history entry {!r}: {}!".format( + line, e)) continue - if atime.startswith('\0'): - log.init.debug( - "Removing NUL bytes from entry {!r} - see " - "https://github.com/The-Compiler/qutebrowser/issues/" - "670".format(data)) - atime = atime.lstrip('\0') + # This de-duplicates history entries; only the latest # entry for each URL is kept. If you want to keep # information about previous hits change the items in - # old_urls to be lists or change HistoryEntry to have a + # old_urls to be lists or change Entry to have a # list of atimes. - entry = HistoryEntry(atime, url, title) self._add_entry(entry) self._initial_read_done = True @@ -190,6 +207,7 @@ class WebHistory(QObject): if not entry.hidden: self._new_history.append(entry) self.add_completion_item.emit(entry) + self._temp_history.clear() def _add_entry(self, entry, target=None): """Add an entry to self.history_dict or another given OrderedDict.""" @@ -236,7 +254,7 @@ class WebHistory(QObject): return if config.get('general', 'private-browsing'): return - entry = HistoryEntry(time.time(), url_string, title, hidden=hidden) + entry = Entry(time.time(), url_string, title, hidden=hidden) if self._initial_read_done: self._add_entry(entry) if not entry.hidden: @@ -254,7 +272,8 @@ def init(parent=None): Args: parent: The parent to use for WebHistory. """ - history = WebHistory(parent) + history = WebHistory(hist_dir=standarddir.data(), hist_name='history', + parent=parent) objreg.register('web-history', history) interface = WebHistoryInterface(history, parent=history)