diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 8d3a11656..594764c37 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -185,8 +185,6 @@ def init(args, crash_handler): QDesktopServices.setUrlHandler('https', open_desktopservices_url) QDesktopServices.setUrlHandler('qute', open_desktopservices_url) - objreg.get('web-history').import_txt() - log.init.debug("Init done!") crash_handler.raise_crashdlg() diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index 6b172a6e8..e13923309 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -338,108 +338,6 @@ class WebHistory(sql.SqlTable): 'last_atime': atime }, replace=True) - def _parse_entry(self, line): - """Parse a history line like '12345 http://example.com title'.""" - if not line or line.startswith('#'): - return None - 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") - - # http://xn--pple-43d.com/ with - # https://bugreports.qt.io/browse/QTBUG-60364 - if url in ['http://.com/', 'https://.com/', - 'http://www..com/', 'https://www..com/']: - return None - - url = QUrl(url) - if not url.isValid(): - raise ValueError("Invalid URL: {}".format(url.errorString())) - - # https://github.com/qutebrowser/qutebrowser/issues/2646 - if url.scheme() == 'data': - return None - - # https://github.com/qutebrowser/qutebrowser/issues/670 - atime = atime.lstrip('\0') - - if '-' in atime: - atime, flags = atime.split('-') - else: - flags = '' - - if not set(flags).issubset('r'): - raise ValueError("Invalid flags {!r}".format(flags)) - - redirect = 'r' in flags - return (url, title, int(atime), redirect) - - def import_txt(self): - """Import a history text file into sqlite if it exists. - - In older versions of qutebrowser, history was stored in a text format. - This converts that file into the new sqlite format and moves it to a - backup location. - """ - path = os.path.join(standarddir.data(), 'history') - if not os.path.isfile(path): - return - - def action(): - """Actually run the import.""" - with debug.log_time(log.init, 'Import old history file to sqlite'): - try: - self._read(path) - except ValueError as ex: - message.error('Failed to import history: {}'.format(ex)) - else: - self._write_backup(path) - - # delay to give message time to appear before locking down for import - message.info('Converting {} to sqlite...'.format(path)) - QTimer.singleShot(100, action) - - def _read(self, path): - """Import a text file into the sql database.""" - with open(path, 'r', encoding='utf-8') as f: - data = {'url': [], 'title': [], 'atime': [], 'redirect': []} - completion_data = {'url': [], 'title': [], 'last_atime': []} - for (i, line) in enumerate(f): - try: - parsed = self._parse_entry(line.strip()) - if parsed is None: - continue - url, title, atime, redirect = parsed - data['url'].append(self._format_url(url)) - data['title'].append(title) - data['atime'].append(atime) - data['redirect'].append(redirect) - if not redirect: - completion_data['url'].append( - self._format_completion_url(url)) - completion_data['title'].append(title) - completion_data['last_atime'].append(atime) - except ValueError as ex: - raise ValueError('Failed to parse line #{} of {}: "{}"' - .format(i, path, ex)) - self.insert_batch(data) - self.completion.insert_batch(completion_data, replace=True) - - def _write_backup(self, path): - bak = path + '.bak' - message.info('History import complete. Appending {} to {}' - .format(path, bak)) - with open(path, 'r', encoding='utf-8') as infile: - with open(bak, 'a', encoding='utf-8') as outfile: - for line in infile: - outfile.write('\n' + line) - os.remove(path) - def _format_url(self, url): return url.toString(QUrl.RemovePassword | QUrl.FullyEncoded) diff --git a/tests/unit/browser/test_history.py b/tests/unit/browser/test_history.py index 0dc97f316..40dce3ec4 100644 --- a/tests/unit/browser/test_history.py +++ b/tests/unit/browser/test_history.py @@ -323,99 +323,6 @@ class TestInit: assert default_interface is None -class TestImport: - - def test_import_txt(self, hist, data_tmpdir, monkeypatch, stubs): - monkeypatch.setattr(history, 'QTimer', stubs.InstaTimer) - histfile = data_tmpdir / 'history' - # empty line is deliberate, to test skipping empty lines - histfile.write('''12345 http://example.com/ title - 12346 http://qutebrowser.org/ - 67890 http://example.com/path - - 68891-r http://example.com/path/other ''') - - hist.import_txt() - - assert list(hist) == [ - ('http://example.com/', 'title', 12345, False), - ('http://qutebrowser.org/', '', 12346, False), - ('http://example.com/path', '', 67890, False), - ('http://example.com/path/other', '', 68891, True) - ] - - assert not histfile.exists() - assert (data_tmpdir / 'history.bak').exists() - - def test_existing_backup(self, hist, data_tmpdir, monkeypatch, stubs): - monkeypatch.setattr(history, 'QTimer', stubs.InstaTimer) - histfile = data_tmpdir / 'history' - bakfile = data_tmpdir / 'history.bak' - histfile.write('12345 http://example.com/ title') - bakfile.write('12346 http://qutebrowser.org/') - - hist.import_txt() - - assert list(hist) == [('http://example.com/', 'title', 12345, False)] - - assert not histfile.exists() - assert bakfile.read().split('\n') == ['12346 http://qutebrowser.org/', - '12345 http://example.com/ title'] - - @pytest.mark.parametrize('line', [ - '', - '#12345 http://example.com/commented', - - # https://bugreports.qt.io/browse/QTBUG-60364 - '12345 http://.com/', - '12345 https://.com/', - '12345 http://www..com/', - '12345 https://www..com/', - - # issue #2646 - ('12345 data:text/html;' - 'charset=UTF-8,%3C%21DOCTYPE%20html%20PUBLIC%20%22-'), - ]) - def test_skip(self, hist, data_tmpdir, monkeypatch, stubs, line): - """import_txt should skip certain lines silently.""" - monkeypatch.setattr(history, 'QTimer', stubs.InstaTimer) - histfile = data_tmpdir / 'history' - histfile.write(line) - - hist.import_txt() - - assert not histfile.exists() - assert not len(hist) - - @pytest.mark.parametrize('line', [ - 'xyz http://example.com/bad-timestamp', - '12345', - 'http://example.com/no-timestamp', - '68891-r-r http://example.com/double-flag', - '68891-x http://example.com/bad-flag', - '68891 http://.com', - ]) - def test_invalid(self, hist, data_tmpdir, monkeypatch, stubs, caplog, - line): - """import_txt should fail on certain lines.""" - monkeypatch.setattr(history, 'QTimer', stubs.InstaTimer) - histfile = data_tmpdir / 'history' - histfile.write(line) - - with caplog.at_level(logging.ERROR): - hist.import_txt() - - assert any(rec.msg.startswith("Failed to import history:") - for rec in caplog.records) - - assert histfile.exists() - - def test_nonexistent(self, hist, data_tmpdir, monkeypatch, stubs): - """import_txt should do nothing if the history file doesn't exist.""" - monkeypatch.setattr(history, 'QTimer', stubs.InstaTimer) - hist.import_txt() - - class TestDump: def test_debug_dump_history(self, hist, tmpdir):