Remove support for importing pre-v1.0.0 history
This commit is contained in:
parent
ec774379bd
commit
d4f16f88b6
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user